diff --git a/TODO b/TODO index 24d27ef6..2ba8d3c0 100644 --- a/TODO +++ b/TODO @@ -28,9 +28,9 @@ - [X] cleanup ascending/descending stuff - [X] testing -** release 0.7 [30%] +** release 0.7 [42%] - - [ ] signal handler for indexing + - [X] signal handler for indexing - [X] fix max 10000 matches limit - [X] embed database version in database - [X] MuMsgXapian => MuMsgIterXapian @@ -43,7 +43,7 @@ - [ ] test suite - [ ] moving msg field formatting to MuMsgField (?) - [ ] auto clean log file - - [ ] configure error for missing ->dt_dtype + - [X] configure error for missing ->dt_dtype ** release 0.8 [%] - [ ] bookmarks diff --git a/man/mu.1 b/man/mu.1 index 1a0ceec9..00a5cc7d 100644 --- a/man/mu.1 +++ b/man/mu.1 @@ -125,6 +125,12 @@ The optional phase two of the indexing-process is the removal of messages from the database for which there is no longer a corresponding file in the Maildir. If you do not want this, you can use \fB\-n\fR, \fB\-\-nocleanup\fR. +When \fBmu index\fR catches on of the signals \fBSIGINT\fR, \fBSIGHUP\fR or +\fBSIGTERM\fR (e.g,, when you press Ctrl-C during the indexing process), it +tries to shutdown gracefully; it tries to save and commit data, and close the +database etc. If it receives another signal (e.g,, when pressing Ctrl-C once +more), \fBmu index\fR will terminate immediately. + .SS Indexing options .TP diff --git a/src/mu-cmd.c b/src/mu-cmd.c index 1f1b1f5a..3f77bb08 100644 --- a/src/mu-cmd.c +++ b/src/mu-cmd.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "mu-msg-gmime.h" #include "mu-maildir.h" @@ -353,6 +355,38 @@ cmd_find (MuConfigOptions *opts) } +static gboolean MU_CAUGHT_SIGNAL; + +static void +sig_handler (int sig) +{ + g_debug ("caught signal %d", sig); + g_print ("\n"); + g_message ("Shutting down gracefully, " + "press again to kill immediately"); + + MU_CAUGHT_SIGNAL = TRUE; +} + +static void +install_sig_handler (void) +{ + struct sigaction action; + int i, sigs[] = { SIGINT, SIGHUP, SIGTERM }; + + MU_CAUGHT_SIGNAL = FALSE; + + action.sa_handler = sig_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_RESETHAND; + + for (i = 0; i != G_N_ELEMENTS(sigs); ++i) + if (sigaction (sigs[i], &action, NULL) != 0) + g_warning ("error: set sigaction for %d failed: %s", + sigs[i], strerror (errno));; +} + + static gboolean check_index_params (MuConfigOptions *opts) { @@ -361,7 +395,6 @@ check_index_params (MuConfigOptions *opts) return FALSE; } - if (!mu_util_check_dir (opts->maildir, TRUE, TRUE)) { g_message ("Please provide a valid Maildir"); return FALSE; @@ -369,7 +402,14 @@ check_index_params (MuConfigOptions *opts) return TRUE; } - + + +static MuResult +index_msg_silent_cb (MuIndexStats* stats, void *user_data) +{ + return MU_CAUGHT_SIGNAL ? MU_STOP: MU_OK; +} + static MuResult index_msg_cb (MuIndexStats* stats, void *user_data) @@ -391,7 +431,7 @@ index_msg_cb (MuIndexStats* stats, void *user_data) g_print ("%s", output); ++i; - return MU_OK; + return MU_CAUGHT_SIGNAL ? MU_STOP: MU_OK; } static gboolean @@ -403,6 +443,8 @@ cmd_cleanup (MuConfigOptions *opts) if (!check_index_params (opts)) return FALSE; + + install_sig_handler (); midx = mu_index_new (opts->xpath); if (!midx) { @@ -415,14 +457,17 @@ cmd_cleanup (MuConfigOptions *opts) mu_index_stats_clear (&stats); rv = mu_index_cleanup (midx, &stats, - opts->quiet ? NULL : index_msg_cb, + opts->quiet ? index_msg_silent_cb : index_msg_cb, NULL); mu_index_destroy (midx); if (!opts->quiet) g_print ("\n"); - - return rv == MU_OK ? TRUE : FALSE; + + if (rv == MU_OK || rv == MU_STOP) + return TRUE; + else + return FALSE; } @@ -461,7 +506,9 @@ cmd_index (MuConfigOptions *opts) if (!database_version_check_and_update(opts)) return FALSE; - + + install_sig_handler (); + mu_msg_gmime_init (); { MuIndex *midx; @@ -480,14 +527,16 @@ cmd_index (MuConfigOptions *opts) rv = mu_index_run (midx, opts->maildir, opts->reindex, &stats, - opts->quiet ? NULL : index_msg_cb, + opts->quiet ? + index_msg_silent_cb :index_msg_cb, NULL, NULL); - if (!opts->nocleanup) { + if (!opts->nocleanup && !MU_CAUGHT_SIGNAL) { stats._processed = 0; /* start over */ g_print ("\n"); g_message ("Cleaning up missing messages"); mu_index_cleanup (midx, &stats, - opts->quiet ? NULL : index_msg_cb, + opts->quiet ? + index_msg_silent_cb : index_msg_cb, NULL); } diff --git a/src/mu-index.c b/src/mu-index.c index 913bc53b..a7d7aabd 100644 --- a/src/mu-index.c +++ b/src/mu-index.c @@ -141,16 +141,16 @@ insert_or_update_maybe (const char* fullpath, time_t filestamp, static MuResult run_msg_callback_maybe (MuIndexCallbackData *data) { - if (data && data->_idx_msg_cb) { - - MuResult result; - - result = data->_idx_msg_cb (data->_stats, data->_user_data); - if (result != MU_OK && result != MU_STOP) - g_warning ("%s: callback said %d", __FUNCTION__, result); - } + MuResult result; - return MU_OK; + if (!data || !data->_idx_msg_cb) + return MU_OK; + + result = data->_idx_msg_cb (data->_stats, data->_user_data); + if (result != MU_OK && result != MU_STOP) + g_warning ("Error in callback"); + + return result; } @@ -237,6 +237,7 @@ mu_index_run (MuIndex *index, const char* path, MuResult rv; g_return_val_if_fail (index && index->_xapian, MU_ERROR); + g_return_val_if_fail (msg_cb, MU_ERROR); if (!check_path (path)) return MU_ERROR; @@ -303,7 +304,8 @@ mu_index_stats (MuIndex *index, const char* path, MuIndexCallbackData cb_data; g_return_val_if_fail (index, MU_ERROR); - + g_return_val_if_fail (cb_msg, MU_ERROR); + if (!check_path (path)) return MU_ERROR; @@ -369,7 +371,8 @@ mu_index_cleanup (MuIndex *index, MuIndexStats *stats, CleanupData cudata; g_return_val_if_fail (index, MU_ERROR); - + g_return_val_if_fail (cb, MU_ERROR); + cudata._xapian = index->_xapian; cudata._stats = stats; cudata._cb = cb; diff --git a/src/mu-index.h b/src/mu-index.h index 3992e510..992ca48e 100644 --- a/src/mu-index.h +++ b/src/mu-index.h @@ -95,7 +95,7 @@ typedef MuResult (*MuIndexDirCallback) (const char* path, gboolean enter, * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param cb_msg a callback function called for every msg indexed; - * @param cb_dir a callback function called for every dir entered/left; + * @param cb_dir a callback function called for every dir entered/left or NULL * @param user_data a user pointer that will be passed to the callback function * * @return MU_OK if the stats gathering was completed succesfully, @@ -118,7 +118,8 @@ MuResult mu_index_run (MuIndex *index, const char* path, gboolean force, * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function - * @param cb a callback function which will be called for every msg; + * @param msg_cb a callback function which will be called for every msg; + * @param dir_cb a callback function which will be called for every dir or NULL * @param user_data a user pointer that will be passed to the callback function * xb * @return MU_OK if the stats gathering was completed succesfully, diff --git a/src/mu-maildir.c b/src/mu-maildir.c index e1507f5c..a23fecf8 100644 --- a/src/mu-maildir.c +++ b/src/mu-maildir.c @@ -212,12 +212,13 @@ static MuResult process_dir (const char* path, MuMaildirWalkDirCallback dir_cb, void *data); static MuResult -process_file (const char* fullpath, MuMaildirWalkMsgCallback cb, void *data) +process_file (const char* fullpath, MuMaildirWalkMsgCallback msg_cb, + void *data) { MuResult result; struct stat statbuf; - if (!cb) + if (!msg_cb) return MU_OK; if (G_UNLIKELY(access(fullpath, R_OK) != 0)) { @@ -241,14 +242,13 @@ process_file (const char* fullpath, MuMaildirWalkMsgCallback cb, void *data) * use the ctime, so any status change will be visible (perms, * filename etc.) */ - result = (cb)(fullpath, statbuf.st_ctime, data); - if (G_LIKELY(result == MU_OK || result == MU_STOP)) - return result; - else { + result = (msg_cb)(fullpath, statbuf.st_ctime, data); + if (result == MU_STOP) + g_debug ("callback said 'MU_STOP' for %s", fullpath); + else if (result == MU_ERROR) g_warning ("%s: failed %d in callback (%s)", __FUNCTION__, result, fullpath); - return result; - } + return result; } @@ -481,9 +481,13 @@ process_dir (const char* path, MuMaildirWalkMsgCallback msg_cb, c = lst = g_list_sort (lst, (GCompareFunc)dirent_cmp); #endif /*HAVE_STRUCT_DIRENT_D_INO*/ - for (c = lst, result = MU_OK; c && result == MU_OK; c = c->next) + for (c = lst, result = MU_OK; c && result == MU_OK; c = c->next) { result = process_dir_entry (path, (struct dirent*)c->data, msg_cb, dir_cb, data); + /* hmmm, break on MU_ERROR as well? */ + if (result == MU_STOP) + break; + } g_list_foreach (lst, (GFunc)dirent_destroy, NULL); g_list_free (lst); @@ -517,8 +521,11 @@ mu_maildir_walk (const char *path, MuMaildirWalkMsgCallback cb_msg, /* skip the final slash from dirnames */ MuResult rv; char *mypath = g_strdup (path); + + /* strip the final / or \ */ if (mypath[strlen(mypath)-1] == G_DIR_SEPARATOR) mypath[strlen(mypath)-1] = '\0'; + rv = process_dir (mypath, cb_msg, cb_dir, data); g_free (mypath);