aboutsummaryrefslogtreecommitdiffstats
path: root/kbx/kbxserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'kbx/kbxserver.c')
-rw-r--r--kbx/kbxserver.c120
1 files changed, 120 insertions, 0 deletions
diff --git a/kbx/kbxserver.c b/kbx/kbxserver.c
index 264c3be4e..8a75035a5 100644
--- a/kbx/kbxserver.c
+++ b/kbx/kbxserver.c
@@ -50,6 +50,13 @@
/* Control structure per connection. */
struct server_local_s
{
+ /* We keep a list of all active sessions with the anchor at
+ * SESSION_LIST (see below). This field is used for linking. */
+ struct server_local_s *next_session;
+
+ /* The pid of the client. */
+ pid_t client_pid;
+
/* Data used to associate an Assuan context with local server data */
assuan_context_t assuan_ctx;
@@ -89,6 +96,12 @@ struct server_local_s
};
+/* To keep track of all running sessions, we link all active server
+ * contexts and anchor them at this variable. */
+static struct server_local_s *session_list;
+
+
+
/* Return the assuan contxt from the local server info in CTRL. */
@@ -569,6 +582,75 @@ cmd_delete (assuan_context_t ctx, char *line)
+static const char hlp_transaction[] =
+ "TRANSACTION [begin|commit|rollback]\n"
+ "\n"
+ "For bulk import of data it is often useful to run everything\n"
+ "in one transaction. This can be achieved with this command.\n"
+ "If the last connection of client is closed before a commit\n"
+ "or rollback an implicit rollback is done. With no argument\n"
+ "the status of the current transaction is returned.";
+static gpg_error_t
+cmd_transaction (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err = 0;
+
+ line = skip_options (line);
+
+ if (!strcmp (line, "begin"))
+ {
+ /* Note that we delay the actual transaction until we have to
+ * use SQL. */
+ if (opt.in_transaction)
+ err = set_error (GPG_ERR_CONFLICT, "already in a transaction");
+ else
+ {
+ opt.in_transaction = 1;
+ opt.transaction_pid = assuan_get_pid (ctx);
+ }
+ }
+ else if (!strcmp (line, "commit"))
+ {
+ if (!opt.in_transaction)
+ err = set_error (GPG_ERR_CONFLICT, "not in a transaction");
+ else if (opt.transaction_pid != assuan_get_pid (ctx))
+ err = set_error (GPG_ERR_CONFLICT, "other client is in a transaction");
+ else
+ err = kbxd_commit ();
+ }
+ else if (!strcmp (line, "rollback"))
+ {
+ if (!opt.in_transaction)
+ err = set_error (GPG_ERR_CONFLICT, "not in a transaction");
+ else if (opt.transaction_pid != assuan_get_pid (ctx))
+ err = set_error (GPG_ERR_CONFLICT, "other client is in a transaction");
+ else
+ err = kbxd_rollback ();
+ }
+ else if (!*line)
+ {
+ if (opt.in_transaction && opt.transaction_pid == assuan_get_pid (ctx))
+ err = assuan_set_okay_line (ctx, opt.active_transaction?
+ "active transaction" :
+ "pending transaction");
+ else if (opt.in_transaction)
+ err = assuan_set_okay_line (ctx, opt.active_transaction?
+ "active transaction on other client" :
+ "pending transaction on other client");
+ else
+ err = set_error (GPG_ERR_FALSE, "no transaction");
+ }
+ else
+ {
+ err = set_error (GPG_ERR_ASS_PARAMETER, "unknown transaction command");
+ }
+
+
+ return leave_cmd (ctx, err);
+}
+
+
+
static const char hlp_getinfo[] =
"GETINFO <what>\n"
"\n"
@@ -689,6 +771,7 @@ register_commands (assuan_context_t ctx)
{ "NEXT", cmd_next, hlp_next },
{ "STORE", cmd_store, hlp_store },
{ "DELETE", cmd_delete, hlp_delete },
+ { "TRANSACTION",cmd_transaction,hlp_transaction },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
{ "OUTPUT", NULL, hlp_output },
{ "KILLKEYBOXD",cmd_killkeyboxd,hlp_killkeyboxd },
@@ -764,6 +847,7 @@ kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
xfree (ctrl);
return;
}
+ ctrl->server_local->client_pid = ASSUAN_INVALID_PID;
rc = assuan_new (&ctx);
if (rc)
@@ -825,6 +909,11 @@ kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
ctrl->server_local->session_id = session_id;
+ /* Put the session int a list. */
+ ctrl->server_local->next_session = session_list;
+ session_list = ctrl->server_local;
+
+
/* The next call enable the use of status_printf. */
set_assuan_context_func (get_assuan_ctx_from_ctrl);
@@ -850,6 +939,7 @@ kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
(long)peercred->gid);
}
#endif
+ ctrl->server_local->client_pid = assuan_get_pid (ctx);
rc = assuan_process (ctx);
if (rc)
@@ -859,6 +949,22 @@ kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
}
}
+ if (opt.in_transaction
+ && opt.transaction_pid == ctrl->server_local->client_pid)
+ {
+ struct server_local_s *sl;
+ pid_t thispid = ctrl->server_local->client_pid;
+ int npids = 0;
+
+ /* Only if this is the last connection rollback the transaction. */
+ for (sl = session_list; sl; sl = sl->next_session)
+ if (sl->client_pid == thispid)
+ npids++;
+
+ if (npids == 1)
+ kbxd_rollback ();
+ }
+
assuan_close_output_fd (ctx);
set_assuan_context_func (NULL);
@@ -873,6 +979,20 @@ kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
ctrl->refcount);
else
{
+ if (session_list == ctrl->server_local)
+ session_list = ctrl->server_local->next_session;
+ else
+ {
+ struct server_local_s *sl;
+
+ for (sl=session_list; sl->next_session; sl = sl->next_session)
+ if (sl->next_session == ctrl->server_local)
+ break;
+ if (!sl->next_session)
+ BUG ();
+ sl->next_session = ctrl->server_local->next_session;
+ }
+
xfree (ctrl->server_local->multi_search_desc);
xfree (ctrl->server_local);
ctrl->server_local = NULL;