aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2020-09-24 14:38:21 +0000
committerWerner Koch <[email protected]>2020-09-24 14:38:21 +0000
commitc2b14f5d6852fb9efaca8aeec7961e9d036203e8 (patch)
tree615bb6204f18f68b1c13fa58d6cac20acda9a834
parenttests: Integrate --use-keyboxd into the OpenPGP test suite. (diff)
downloadgnupg-c2b14f5d6852fb9efaca8aeec7961e9d036203e8.tar.gz
gnupg-c2b14f5d6852fb9efaca8aeec7961e9d036203e8.zip
keyboxd: New command TRANSACTION.
* kbx/backend-sqlite.c (be_sqlite_rollback): New. (be_sqlite_commit): New. (be_sqlite_search): Take care of global transactions. (be_sqlite_store): Ditto. (be_sqlite_delete): Ditto. * kbx/frontend.c (kbxd_rollback, kbxd_commit): New. * kbx/keyboxd.h (opt): Add vars for transactions. * kbx/kbxserver.c (struct server_local_s): Add fields next_session and client_pid. (session_list): New var. (cmd_transaction): New. (register_commands): Register command. (kbxd_start_command_handler): Store pids and track sessions. Do a final rollback. -- This command is currently an experiment to allow a client to run everything in one session. Signed-off-by: Werner Koch <[email protected]>
-rw-r--r--kbx/backend-sqlite.c91
-rw-r--r--kbx/backend.h2
-rw-r--r--kbx/frontend.c15
-rw-r--r--kbx/frontend.h2
-rw-r--r--kbx/kbxserver.c120
-rw-r--r--kbx/keyboxd.h10
6 files changed, 230 insertions, 10 deletions
diff --git a/kbx/backend-sqlite.c b/kbx/backend-sqlite.c
index 0b5155a8f..c31415694 100644
--- a/kbx/backend-sqlite.c
+++ b/kbx/backend-sqlite.c
@@ -681,6 +681,42 @@ be_sqlite_release_local (be_sqlite_local_t ctx)
}
+gpg_error_t
+be_sqlite_rollback (void)
+{
+ opt.in_transaction = 0;
+ if (!opt.active_transaction)
+ return 0; /* Nothing to do. */
+
+ if (!database_hd)
+ {
+ log_error ("Warning: No database handle for global rollback\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ opt.active_transaction = 0;
+ return run_sql_statement ("rollback");
+}
+
+
+gpg_error_t
+be_sqlite_commit (void)
+{
+ opt.in_transaction = 0;
+ if (!opt.active_transaction)
+ return 0; /* Nothing to do. */
+
+ if (!database_hd)
+ {
+ log_error ("Warning: No database handle for global commit\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ opt.active_transaction = 0;
+ return run_sql_statement ("commit");
+}
+
+
/* Run a select for the search given by (DESC,NDESC). The data is not
* returned but stored in the request item. */
static gpg_error_t
@@ -1000,6 +1036,16 @@ be_sqlite_search (ctrl_t ctrl,
goto leave;
}
+ /* Start a global transaction if needed. */
+ if (!opt.active_transaction && opt.in_transaction)
+ {
+ err = run_sql_statement ("begin transaction");
+ if (err)
+ goto leave;
+ opt.active_transaction = 1;
+ }
+
+
again:
if (!ctx->select_done)
{
@@ -1395,9 +1441,14 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
goto leave;
/* ctx = part->besqlite; */
- err = run_sql_statement ("begin transaction");
- if (err)
- goto leave;
+ if (!opt.active_transaction)
+ {
+ err = run_sql_statement ("begin transaction");
+ if (err)
+ goto leave;
+ if (opt.in_transaction)
+ opt.active_transaction = 1;
+ }
in_transaction = 1;
err = store_into_pubkey (mode, pktype, ubid, blob, bloblen);
@@ -1541,10 +1592,17 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
leave:
if (in_transaction && !err)
- err = run_sql_statement ("commit");
+ {
+ if (opt.active_transaction)
+ ; /* We are in a global transaction. */
+ else
+ err = run_sql_statement ("commit");
+ }
else if (in_transaction)
{
- if (run_sql_statement ("rollback"))
+ if (opt.active_transaction)
+ ; /* We are in a global transaction. */
+ else if (run_sql_statement ("rollback"))
log_error ("Warning: database rollback failed - should not happen!\n");
}
if (got_mutex)
@@ -1586,9 +1644,14 @@ be_sqlite_delete (ctrl_t ctrl, backend_handle_t backend_hd,
goto leave;
/* ctx = part->besqlite; */
- err = run_sql_statement ("begin transaction");
- if (err)
- goto leave;
+ if (!opt.active_transaction)
+ {
+ err = run_sql_statement ("begin transaction");
+ if (err)
+ goto leave;
+ if (opt.in_transaction)
+ opt.active_transaction = 1;
+ }
in_transaction = 1;
err = run_sql_statement_bind_ubid
@@ -1607,11 +1670,19 @@ be_sqlite_delete (ctrl_t ctrl, backend_handle_t backend_hd,
leave:
if (stmt)
sqlite3_finalize (stmt);
+
if (in_transaction && !err)
- err = run_sql_statement ("commit");
+ {
+ if (opt.active_transaction)
+ ; /* We are in a global transaction. */
+ else
+ err = run_sql_statement ("commit");
+ }
else if (in_transaction)
{
- if (run_sql_statement ("rollback"))
+ if (opt.active_transaction)
+ ; /* We are in a global transaction. */
+ else if (run_sql_statement ("rollback"))
log_error ("Warning: database rollback failed - should not happen!\n");
}
release_mutex ();
diff --git a/kbx/backend.h b/kbx/backend.h
index a241490a7..d6178cd01 100644
--- a/kbx/backend.h
+++ b/kbx/backend.h
@@ -171,6 +171,8 @@ void be_sqlite_release_resource (ctrl_t ctrl, backend_handle_t hd);
gpg_error_t be_sqlite_init_local (backend_handle_t backend_hd,
db_request_part_t part);
void be_sqlite_release_local (be_sqlite_local_t ctx);
+gpg_error_t be_sqlite_rollback (void);
+gpg_error_t be_sqlite_commit (void);
gpg_error_t be_sqlite_search (ctrl_t ctrl, backend_handle_t hd,
db_request_t request,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc);
diff --git a/kbx/frontend.c b/kbx/frontend.c
index 48b6fffa2..c80c9fa8a 100644
--- a/kbx/frontend.c
+++ b/kbx/frontend.c
@@ -172,6 +172,21 @@ kbxd_release_session_info (ctrl_t ctrl)
}
+
+gpg_error_t
+kbxd_rollback (void)
+{
+ return be_sqlite_rollback ();
+}
+
+
+gpg_error_t
+kbxd_commit (void)
+{
+ return be_sqlite_commit ();
+}
+
+
/* Search for the keys described by (DESC,NDESC) and return them to
* the caller. If RESET is set, the search state is first reset.
diff --git a/kbx/frontend.h b/kbx/frontend.h
index d38d442f0..50ba4a4e4 100644
--- a/kbx/frontend.h
+++ b/kbx/frontend.h
@@ -28,6 +28,8 @@ gpg_error_t kbxd_set_database (ctrl_t ctrl,
void kbxd_release_session_info (ctrl_t ctrl);
+gpg_error_t kbxd_rollback (void);
+gpg_error_t kbxd_commit (void);
gpg_error_t kbxd_search (ctrl_t ctrl,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc,
int reset);
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;
diff --git a/kbx/keyboxd.h b/kbx/keyboxd.h
index 22988bf1b..9cc2c23a6 100644
--- a/kbx/keyboxd.h
+++ b/kbx/keyboxd.h
@@ -45,6 +45,15 @@ struct
/* True if we are running detached from the tty. */
int running_detached;
+ /*
+ * Global state variables.
+ */
+
+ /* Whether a global transaction has been requested along with the
+ * caller's pid and whether a transaction is active. */
+ pid_t transaction_pid;
+ unsigned int in_transaction : 1;
+ unsigned int active_transaction : 1;
} opt;
@@ -118,6 +127,7 @@ struct server_control_s
unsigned int filter_x509 : 1;
/* Used by SEARCH and NEXT. */
unsigned int no_data_return : 1;
+
};