cli: add global option "--uuid"

The function notmuch_exit_if_unmatched_db_uuid is split from
notmuch_process_shared_options because it needs an open notmuch
database.

There are two exceptional cases in uuid handling.

1) notmuch config and notmuch setup don't currently open the database,
   so it doesn't make sense to check the UUID.

2) notmuch compact opens the database inside the library, so we either
   need to open the database just to check uuid, or change the API.
This commit is contained in:
David Bremner 2015-04-06 07:39:55 +09:00
parent 5a3b42fb8c
commit f76d8f82dd
17 changed files with 91 additions and 3 deletions

View file

@ -51,9 +51,16 @@ Supported global options for ``notmuch`` include
Specify the configuration file to use. This overrides any Specify the configuration file to use. This overrides any
configuration file specified by ${NOTMUCH\_CONFIG}. configuration file specified by ${NOTMUCH\_CONFIG}.
``--uuid=HEX``
Enforce that the database UUID (a unique identifier which
persists until e.g. the database is compacted)
is HEX; exit with an error if it is not. This is useful to
detect rollover in modification counts on messages. You can
find this UUID using e.g. ``notmuch count --lastmod``
All global options except ``--config`` can also be specified after the All global options except ``--config`` can also be specified after the
command. For example, ``notmuch subcommand --version`` is equivalent to command. For example, ``notmuch subcommand --uuid=HEX`` is
``notmuch --version subcommand``. equivalent to ``notmuch --uuid=HEX subcommand``.
COMMANDS COMMANDS
======== ========

View file

@ -466,7 +466,11 @@ notmuch_database_dump (notmuch_database_t *notmuch,
notmuch_bool_t gzip_output); notmuch_bool_t gzip_output);
#include "command-line-arguments.h" #include "command-line-arguments.h"
extern char *notmuch_requested_db_uuid;
extern const notmuch_opt_desc_t notmuch_shared_options []; extern const notmuch_opt_desc_t notmuch_shared_options [];
void notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch);
void notmuch_process_shared_options (const char* subcommand_name); void notmuch_process_shared_options (const char* subcommand_name);
int notmuch_minimal_options (const char* subcommand_name, int notmuch_minimal_options (const char* subcommand_name,
int argc, char **argv); int argc, char **argv);

View file

@ -46,6 +46,11 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[])
if (opt_index < 0) if (opt_index < 0)
return EXIT_FAILURE; return EXIT_FAILURE;
if (notmuch_requested_db_uuid) {
fprintf (stderr, "Error: --uuid not implemented for compact\n");
return EXIT_FAILURE;
}
notmuch_process_shared_options (argv[0]); notmuch_process_shared_options (argv[0]);
if (! quiet) if (! quiet)

View file

@ -878,6 +878,10 @@ notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
if (opt_index < 0) if (opt_index < 0)
return EXIT_FAILURE; return EXIT_FAILURE;
if (notmuch_requested_db_uuid)
fprintf (stderr, "Warning: ignoring --uuid=%s\n",
notmuch_requested_db_uuid);
/* skip at least subcommand argument */ /* skip at least subcommand argument */
argc-= opt_index; argc-= opt_index;
argv+= opt_index; argv+= opt_index;

View file

@ -189,6 +189,8 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch)) NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
return EXIT_FAILURE; return EXIT_FAILURE;
notmuch_exit_if_unmatched_db_uuid (notmuch);
query_str = query_string_from_args (config, argc-opt_index, argv+opt_index); query_str = query_string_from_args (config, argc-opt_index, argv+opt_index);
if (query_str == NULL) { if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n"); fprintf (stderr, "Out of memory.\n");

View file

@ -215,6 +215,8 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch)) NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
return EXIT_FAILURE; return EXIT_FAILURE;
notmuch_exit_if_unmatched_db_uuid (notmuch);
char *output_file_name = NULL; char *output_file_name = NULL;
int opt_index; int opt_index;

View file

@ -536,6 +536,8 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch)) NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
return EXIT_FAILURE; return EXIT_FAILURE;
notmuch_exit_if_unmatched_db_uuid (notmuch);
/* Write the message to the Maildir new directory. */ /* Write the message to the Maildir new directory. */
newpath = maildir_write_new (config, STDIN_FILENO, maildir); newpath = maildir_write_new (config, STDIN_FILENO, maildir);
if (! newpath) { if (! newpath) {

View file

@ -1009,10 +1009,11 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
fputs (status_string, stderr); fputs (status_string, stderr);
free (status_string); free (status_string);
} }
return EXIT_FAILURE; return EXIT_FAILURE;
} }
notmuch_exit_if_unmatched_db_uuid (notmuch);
if (notmuch_database_needs_upgrade (notmuch)) { if (notmuch_database_needs_upgrade (notmuch)) {
time_t now = time (NULL); time_t now = time (NULL);
struct tm *gm_time = gmtime (&now); struct tm *gm_time = gmtime (&now);

View file

@ -831,6 +831,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch)) NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
return EXIT_FAILURE; return EXIT_FAILURE;
notmuch_exit_if_unmatched_db_uuid (notmuch);
query = notmuch_query_create (notmuch, query_string); query = notmuch_query_create (notmuch, query_string);
if (query == NULL) { if (query == NULL) {
fprintf (stderr, "Out of memory\n"); fprintf (stderr, "Out of memory\n");

View file

@ -165,6 +165,8 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
} }
notmuch_process_shared_options (argv[0]); notmuch_process_shared_options (argv[0]);
notmuch_exit_if_unmatched_db_uuid (notmuch);
name_for_error = input_file_name ? input_file_name : "stdin"; name_for_error = input_file_name ? input_file_name : "stdin";
if (! accumulate) if (! accumulate)

View file

@ -583,6 +583,8 @@ _notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int ar
return EXIT_FAILURE; return EXIT_FAILURE;
} }
notmuch_exit_if_unmatched_db_uuid (ctx->notmuch);
query_str = query_string_from_args (ctx->notmuch, argc, argv); query_str = query_string_from_args (ctx->notmuch, argc, argv);
if (query_str == NULL) { if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n"); fprintf (stderr, "Out of memory.\n");

View file

@ -148,6 +148,10 @@ notmuch_setup_command (notmuch_config_t *config,
if (notmuch_minimal_options ("setup", argc, argv) < 0) if (notmuch_minimal_options ("setup", argc, argv) < 0)
return EXIT_FAILURE; return EXIT_FAILURE;
if (notmuch_requested_db_uuid)
fprintf (stderr, "Warning: ignoring --uuid=%s\n",
notmuch_requested_db_uuid);
if (notmuch_config_is_new (config)) if (notmuch_config_is_new (config))
welcome_message_pre_setup (); welcome_message_pre_setup ();

View file

@ -1213,6 +1213,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch)) NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
return EXIT_FAILURE; return EXIT_FAILURE;
notmuch_exit_if_unmatched_db_uuid (notmuch);
query = notmuch_query_create (notmuch, query_string); query = notmuch_query_create (notmuch, query_string);
if (query == NULL) { if (query == NULL) {
fprintf (stderr, "Out of memory\n"); fprintf (stderr, "Out of memory\n");

View file

@ -261,6 +261,8 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch)) NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
return EXIT_FAILURE; return EXIT_FAILURE;
notmuch_exit_if_unmatched_db_uuid (notmuch);
if (notmuch_config_get_maildir_synchronize_flags (config)) if (notmuch_config_get_maildir_synchronize_flags (config))
tag_flags |= TAG_FLAG_MAILDIR_SYNC; tag_flags |= TAG_FLAG_MAILDIR_SYNC;

View file

@ -47,10 +47,12 @@ static int
_help_for (const char *topic); _help_for (const char *topic);
static notmuch_bool_t print_version = FALSE, print_help = FALSE; static notmuch_bool_t print_version = FALSE, print_help = FALSE;
char *notmuch_requested_db_uuid = NULL;
const notmuch_opt_desc_t notmuch_shared_options [] = { const notmuch_opt_desc_t notmuch_shared_options [] = {
{ NOTMUCH_OPT_BOOLEAN, &print_version, "version", 'v', 0 }, { NOTMUCH_OPT_BOOLEAN, &print_version, "version", 'v', 0 },
{ NOTMUCH_OPT_BOOLEAN, &print_help, "help", 'h', 0 }, { NOTMUCH_OPT_BOOLEAN, &print_help, "help", 'h', 0 },
{ NOTMUCH_OPT_STRING, &notmuch_requested_db_uuid, "uuid", 'u', 0 },
{0, 0, 0, 0, 0} {0, 0, 0, 0, 0}
}; };
@ -218,6 +220,22 @@ be supported in the future.\n", notmuch_format_version);
} }
} }
void
notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch)
{
const char *uuid = NULL;
if (!notmuch_requested_db_uuid)
return;
IGNORE_RESULT (notmuch_database_get_revision (notmuch, &uuid));
if (strcmp (notmuch_requested_db_uuid, uuid) != 0){
fprintf (stderr, "Error: requested database revision %s does not match %s\n",
notmuch_requested_db_uuid, uuid);
exit (1);
}
}
static void static void
exec_man (const char *page) exec_man (const char *page)
{ {

View file

@ -46,4 +46,31 @@ notmuch tag +a-random-tag-8743632 '*'
after=$(notmuch count --lastmod '*' | cut -f3) after=$(notmuch count --lastmod '*' | cut -f3)
result=$(($before < $after)) result=$(($before < $after))
test_expect_equal 1 ${result} test_expect_equal 1 ${result}
notmuch count --lastmod '*' | cut -f2 > UUID
test_expect_success 'search succeeds with correct uuid' \
"notmuch search --uuid=$(cat UUID) '*'"
test_expect_success 'uuid works as global option ' \
"notmuch --uuid=$(cat UUID) search '*'"
test_expect_code 1 'uuid works as global option II' \
"notmuch --uuid=this-is-no-uuid search '*'"
test_expect_code 1 'search fails with incorrect uuid' \
"notmuch search --uuid=this-is-no-uuid '*'"
test_expect_success 'show succeeds with correct uuid' \
"notmuch show --uuid=$(cat UUID) '*'"
test_expect_code 1 'show fails with incorrect uuid' \
"notmuch show --uuid=this-is-no-uuid '*'"
test_expect_success 'tag succeeds with correct uuid' \
"notmuch tag --uuid=$(cat UUID) +test '*'"
test_expect_code 1 'tag fails with incorrect uuid' \
"notmuch tag --uuid=this-is-no-uuid '*' +test2"
test_done test_done

View file

@ -119,6 +119,8 @@ const notmuch_opt_desc_t notmuch_shared_options[] = {
{ 0, 0, 0, 0, 0 } { 0, 0, 0, 0, 0 }
}; };
char *notmuch_requested_db_uuid = NULL;
void void
notmuch_process_shared_options (unused (const char *dummy)) notmuch_process_shared_options (unused (const char *dummy))
{ {