Implement signal handling.
authorAndre Noll <maan@systemlinux.org>
Sun, 1 Jun 2008 11:25:43 +0000 (13:25 +0200)
committerAndre Noll <maan@systemlinux.org>
Sun, 1 Jun 2008 11:25:43 +0000 (13:25 +0200)
Install a trivial signal handler that sets a global variable
and check the value of this variable from time to time.

This approach is good enough because we only want to cleanly close
the osl table in case we catch SIGINT or SIGTERM.

adu.c

diff --git a/adu.c b/adu.c
index 3a676c4..54af136 100644 (file)
--- a/adu.c
+++ b/adu.c
@@ -34,6 +34,9 @@ struct user_info {
 /** The decimal representation of an uint64_t never exceeds that size. */
 #define FORMATED_VALUE_SIZE 25
 
+#define FOR_EACH_USER(ui) for (ui = uid_hash_table; ui && ui < uid_hash_table \
+               + uid_hash_table_size; ui++)
+
 
 /**
  * Contains info for each user that owns at least one regular file.
@@ -119,7 +122,6 @@ out:
        return ret;
 }
 
-
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
 #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
 
@@ -377,6 +379,79 @@ static int create_tables(void)
        return 1;
 }
 
+static void close_dir_table(void)
+{
+       int ret;
+
+       if (!dir_table)
+               return;
+       ret = osl(osl_close_table(dir_table, OSL_MARK_CLEAN));
+       if (ret < 0)
+               ERROR_LOG("failed to close dir table: %s\n", adu_strerror(-ret));
+       free((char *)dir_table_desc.dir);
+       dir_table = NULL;
+}
+
+static void close_user_table(struct user_info *ui)
+{
+       int ret;
+
+       if (!ui || !ui_used(ui) || !ui_admissible(ui))
+               return;
+       ret = osl(osl_close_table(ui->table, OSL_MARK_CLEAN));
+       if (ret < 0)
+               ERROR_LOG("failed to close user table %u: %s\n",
+                       (unsigned) ui->uid, adu_strerror(-ret));
+       free((char *)ui->desc->name);
+       ui->desc->name = NULL;
+       free((char *)ui->desc->dir);
+       ui->desc->dir = NULL;
+       free(ui->desc);
+       ui->desc = NULL;
+       ui->table = NULL;
+       ui->flags = 0;
+}
+
+static void close_user_tables(void)
+{
+       struct user_info *ui;
+
+       FOR_EACH_USER(ui)
+               close_user_table(ui);
+}
+
+static void close_all_tables(void)
+{
+       close_dir_table();
+       close_user_tables();
+       free_hash_table();
+}
+
+static int signum;
+
+static void signal_handler(int s)
+{
+       signum = s;
+}
+
+static void check_signals(void)
+{
+       if (likely(!signum))
+               return;
+       EMERG_LOG("caught signal %d\n", signum);
+       close_all_tables();
+       exit(EXIT_FAILURE);
+}
+
+static int init_signals(void)
+{
+       if (signal(SIGINT, &signal_handler) == SIG_ERR)
+               return -E_SIGNAL_SIG_ERR;
+       if (signal(SIGTERM, &signal_handler) == SIG_ERR)
+               return -E_SIGNAL_SIG_ERR;
+       return 1;
+}
+
 /*
  * We use a hash table of size s=2^uid_hash_bits to map the uids into the
  * interval [0..s]. Hash collisions are treated by open addressing, i.e.
@@ -399,9 +474,6 @@ static uint32_t double_hash(uint32_t uid, uint32_t probe_num)
                % uid_hash_table_size;
 }
 
-#define FOR_EACH_USER(ui) for (ui = uid_hash_table; ui && ui < uid_hash_table \
-               + uid_hash_table_size; ui++)
-
 enum search_uid_flags {
        OPEN_USER_TABLE = 1,
        CREATE_USER_TABLE = 2,
@@ -512,6 +584,7 @@ int scan_dir(char *dirname, uint64_t *parent_dir_num)
        uint64_t dir_size = 0, dir_files = 0;
        uint64_t this_dir_num = ++num_dirs;
 
+       check_signals();
        DEBUG_LOG("----------------- %llu: %s\n", (long long unsigned)num_dirs, dirname);
        ret = para_opendir(dirname, &dir, &cwd_fd);
        if (ret < 0) {
@@ -712,6 +785,7 @@ static int global_stats_loop_function(struct osl_row *row, void *data)
        char *dirname, formated_value[FORMATED_VALUE_SIZE];
        int ret, summary = gsi->flags & GSF_COMPUTE_SUMMARY;
 
+       check_signals();
        if (!gsi->count && !summary) {
                ret = -E_LOOP_COMPLETE;
                goto err;
@@ -824,6 +898,7 @@ static int user_stats_loop_function(struct osl_row *row, void *data)
        int ret, summary = usi->flags & GSF_COMPUTE_SUMMARY;
        char formated_value[FORMATED_VALUE_SIZE];
 
+       check_signals();
        if (!usi->count && !summary) {
                ret = -E_LOOP_COMPLETE;
                goto err;
@@ -1035,55 +1110,6 @@ static int open_dir_table(void)
                dir_table_desc.dir = para_strdup(conf.database_dir_arg);
        return osl(osl_open_table(&dir_table_desc, &dir_table));
 }
-
-static void close_dir_table(void)
-{
-       int ret;
-
-       if (!dir_table)
-               return;
-       ret = osl(osl_close_table(dir_table, OSL_MARK_CLEAN));
-       if (ret < 0)
-               ERROR_LOG("failed to close dir table: %s\n", adu_strerror(-ret));
-       free((char *)dir_table_desc.dir);
-       dir_table = NULL;
-}
-
-static void close_user_table(struct user_info *ui)
-{
-       int ret;
-
-       if (!ui || !ui_used(ui) || !ui_admissible(ui))
-               return;
-       ret = osl(osl_close_table(ui->table, OSL_MARK_CLEAN));
-       if (ret < 0)
-               ERROR_LOG("failed to close user table %u: %s\n",
-                       (unsigned) ui->uid, adu_strerror(-ret));
-       free((char *)ui->desc->name);
-       ui->desc->name = NULL;
-       free((char *)ui->desc->dir);
-       ui->desc->dir = NULL;
-       free(ui->desc);
-       ui->desc = NULL;
-       ui->table = NULL;
-       ui->flags = 0;
-}
-
-static void close_user_tables(void)
-{
-       struct user_info *ui;
-
-       FOR_EACH_USER(ui)
-               close_user_table(ui);
-}
-
-static void close_all_tables(void)
-{
-       close_dir_table();
-       close_user_tables();
-       free_hash_table();
-}
-
 static int com_create()
 {
        uint64_t zero = 0ULL;
@@ -1091,9 +1117,11 @@ static int com_create()
 
        if (ret < 0)
                return ret;
+       check_signals();
        ret = open_dir_table();
        if (ret < 0)
                return ret;
+       check_signals();
        ret = scan_dir(conf.base_dir_arg, &zero);
        if (ret < 0)
                goto out;
@@ -1150,9 +1178,11 @@ static int com_select(void)
        ret = open_dir_table();
        if (ret < 0)
                return ret;
+       check_signals();
        ret = read_uid_file();
        if (ret < 0)
                return ret;
+       check_signals();
        ret = print_statistics();
        close_all_tables();
        return ret;
@@ -1202,6 +1232,9 @@ int main(int argc, char **argv)
        };
 
        cmdline_parser_ext(argc, argv, &conf, &params); /* aborts on errors */
+       ret = init_signals();
+       if (ret < 0)
+               goto out;
        ret = check_args();
        if (ret < 0)
                goto out;