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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-09-24 16:38:21 +02:00
parent b19a60c6f7
commit c2b14f5d68
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
6 changed files with 230 additions and 10 deletions

View File

@ -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 ();

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -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. */
@ -568,6 +581,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"
@ -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;

View File

@ -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;
};