Merge branch 'refs/heads/t/kill-w'
[dss.git] / dss.c
diff --git a/dss.c b/dss.c
index 22a350a478bb9d48c67dca9487e15145bb11c8fe..491b8aa791fdafc29589ec08eae878e9e67039be 100644 (file)
--- a/dss.c
+++ b/dss.c
@@ -63,6 +63,8 @@ static struct lls_parse_result *cmdline_lpr, *lpr;
 
 /** Parsed subcommand options. */
 static struct lls_parse_result *cmdline_sublpr, *sublpr;
+/* The executing subcommand (NULL at startup). */
+static const struct lls_command *subcmd;
 /** Wether daemon_init() was called. */
 static bool daemonized;
 /** Non-NULL if we log to a file. */
@@ -242,16 +244,19 @@ __printf_1_2 void dss_log(const char* fmt,...)
        if (loglevel < lpr_ll)
                return;
        outfd = logfile? logfile : stderr;
-       time(&t1);
-       tm = localtime(&t1);
-       strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
-       fprintf(outfd, "%s ", str);
-       if (lpr_ll <= INFO)
-               fprintf(outfd, "%i: ", loglevel);
+       if (subcmd == CMD_PTR(RUN)) {
+               time(&t1);
+               tm = localtime(&t1);
+               strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
+               fprintf(outfd, "%s ", str);
+               if (lpr_ll <= INFO)
+                       fprintf(outfd, "%i: ", loglevel);
+       }
+       if (subcmd == CMD_PTR(RUN))
 #ifdef DSS_NO_FUNC_NAMES
-       fprintf(outfd, "%s:%d: ", location_file, location_line);
+               fprintf(outfd, "%s:%d: ", location_file, location_line);
 #else
-       fprintf(outfd, "%s: ", location_func);
+               fprintf(outfd, "%s: ", location_func);
 #endif
        va_start(argp, fmt);
        vfprintf(outfd, fmt, argp);
@@ -282,11 +287,13 @@ static char *get_config_file_name(void)
        return config_file;
 }
 
-static int send_signal(int sig)
+static int send_signal(int sig, bool wait)
 {
        pid_t pid;
        char *config_file = get_config_file_name();
        int ret = get_dss_pid(config_file, &pid);
+       unsigned ms = 32;
+       struct timespec ts;
 
        free(config_file);
        if (ret < 0)
@@ -299,7 +306,23 @@ static int send_signal(int sig)
        ret = kill(pid, sig);
        if (ret < 0)
                return -ERRNO_TO_DSS_ERROR(errno);
-       return 1;
+       if (!wait)
+               return 1;
+       while (ms < 5000) {
+               ts.tv_sec = ms / 1000;
+               ts.tv_nsec = (ms % 1000) * 1000 * 1000;
+               ret = nanosleep(&ts, NULL);
+               if (ret < 0)
+                       return -ERRNO_TO_DSS_ERROR(errno);
+               ret = kill(pid, 0);
+               if (ret < 0) {
+                       if (errno != ESRCH)
+                               return -ERRNO_TO_DSS_ERROR(errno);
+                       return 1;
+               }
+               ms *= 2;
+       }
+       return -E_KILL_TIMEOUT;
 }
 
 struct signal_info {
@@ -358,6 +381,7 @@ static const struct signal_info signal_table[] = {
 
 static int com_kill(void)
 {
+       bool w_given = OPT_GIVEN(KILL, WAIT);
        const char *arg = OPT_STRING_VAL(KILL, SIGNAL);
        int ret, i;
 
@@ -368,17 +392,17 @@ static int com_kill(void)
                        return ret;
                if (val < 0 || val > SIGRTMAX)
                        return -ERRNO_TO_DSS_ERROR(EINVAL);
-               return send_signal(val);
+               return send_signal(val, w_given);
        }
        if (strncasecmp(arg, "sig", 3) == 0)
                arg += 3;
        if (strcasecmp(arg, "CLD") == 0)
-               return send_signal(SIGCHLD);
+               return send_signal(SIGCHLD, w_given);
        if (strcasecmp(arg, "IOT") == 0)
-               return send_signal(SIGABRT);
+               return send_signal(SIGABRT, w_given);
        for (i = 0; i < SIGNAL_TABLE_SIZE; i++)
                if (strcasecmp(arg, signal_table[i].name) == 0)
-                       return send_signal(signal_table[i].num);
+                       return send_signal(signal_table[i].num, w_given);
        DSS_ERROR_LOG(("invalid sigspec: %s\n", arg));
        return -ERRNO_TO_DSS_ERROR(EINVAL);
 }
@@ -1055,20 +1079,39 @@ static int handle_sigchld(void)
        return -E_BUG;
 }
 
+/* also checks if . is a mountpoint, if --mountpoint was given */
 static int change_to_dest_dir(void)
 {
        int ret;
        const char *dd = OPT_STRING_VAL(DSS, DEST_DIR);
+       struct stat dot, dotdot;
 
        DSS_INFO_LOG(("changing cwd to %s\n", dd));
-       if (chdir(dd) >= 0)
-               return 1;
-       ret = -ERRNO_TO_DSS_ERROR(errno);
-       DSS_ERROR_LOG(("could not change cwd to %s\n", dd));
-       return ret;
+       if (chdir(dd) < 0) {
+               ret = -ERRNO_TO_DSS_ERROR(errno);
+               DSS_ERROR_LOG(("could not change cwd to %s\n", dd));
+               return ret;
+       }
+       if (!OPT_GIVEN(DSS, MOUNTPOINT))
+               return 0;
+       if (stat(".", &dot) < 0) {
+               ret = -ERRNO_TO_DSS_ERROR(errno);
+               DSS_ERROR_LOG(("could not stat .\n"));
+               return ret;
+       }
+       if (stat("..", &dotdot) < 0) {
+               ret = -ERRNO_TO_DSS_ERROR(errno);
+               DSS_ERROR_LOG(("could not stat ..\n"));
+               return ret;
+       }
+       if (dot.st_dev == dotdot.st_dev && dot.st_ino != dotdot.st_ino) {
+               DSS_ERROR_LOG(("mountpoint check failed for %s\n", dd));
+               return -E_MOUNTPOINT;
+       }
+       return 1;
 }
 
-static int check_config(const struct lls_command *cmd)
+static int check_config(void)
 {
        int ret;
        uint32_t unit_interval = OPT_UINT32_VAL(DSS, UNIT_INTERVAL);
@@ -1084,13 +1127,13 @@ static int check_config(const struct lls_command *cmd)
                DSS_ERROR_LOG(("bad number of intervals: %i\n", num_intervals));
                return -E_INVALID_NUMBER;
        }
-       if (cmd == CMD_PTR(RUN) || cmd == CMD_PTR(CREATE))
+       if (subcmd == CMD_PTR(RUN) || subcmd == CMD_PTR(CREATE))
                if (!OPT_GIVEN(DSS, SOURCE_DIR)) {
                        DSS_ERROR_LOG(("--source-dir required\n"));
                        return -E_SYNTAX;
                }
-       if (cmd == CMD_PTR(RUN) || cmd == CMD_PTR(CREATE)
-                       || cmd == CMD_PTR(LS) || cmd == CMD_PTR(PRUNE)) {
+       if (subcmd == CMD_PTR(RUN) || subcmd == CMD_PTR(CREATE)
+                       || subcmd == CMD_PTR(LS) || subcmd == CMD_PTR(PRUNE)) {
                if (!OPT_GIVEN(DSS, DEST_DIR)) {
                        DSS_ERROR_LOG(("--dest-dir required\n"));
                        return -E_SYNTAX;
@@ -1228,7 +1271,7 @@ static int handle_sighup(void)
        ret = parse_config_file(true /* SIGHUP */, CMD_PTR(RUN));
        if (ret < 0)
                return ret;
-       ret = check_config(CMD_PTR(RUN));
+       ret = check_config();
        if (ret < 0)
                return ret;
        close_log(logfile);
@@ -1502,15 +1545,18 @@ out:
 
 static void exit_hook(int exit_code)
 {
-       const char *argv[3];
        pid_t pid;
-
-       argv[0] = OPT_STRING_VAL(DSS, EXIT_HOOK);
-       argv[1] = dss_strerror(-exit_code);
-       argv[2] = NULL;
-
-       DSS_NOTICE_LOG(("executing %s %s\n", argv[0], argv[1]));
-       dss_exec(&pid, argv[0], (char **)argv);
+       char **argv, *tmp = dss_strdup(OPT_STRING_VAL(DSS, EXIT_HOOK));
+       unsigned n = split_args(tmp, &argv, " \t");
+
+       n++;
+       argv = dss_realloc(argv, (n + 1) * sizeof(char *));
+       argv[n - 1] = dss_strdup(dss_strerror(-exit_code));
+       argv[n] = NULL;
+       dss_exec(&pid, argv[0], argv);
+       free(argv[n - 1]);
+       free(argv);
+       free(tmp);
 }
 
 static void lock_dss_or_die(void)
@@ -1567,6 +1613,8 @@ static int com_run(void)
                ret = -E_BUG;
        kill_children();
        exit_hook(ret);
+       while (wait(NULL) >= 0 || errno != ECHILD)
+               ; /* still have children to wait for */
        return ret;
 }
 EXPORT_CMD_HANDLER(run);
@@ -1685,13 +1733,17 @@ static int com_ls(void)
        int i;
        struct snapshot_list sl;
        struct snapshot *s;
+       int64_t now = get_current_time();
 
        dss_get_snapshot_list(&sl);
        FOR_EACH_SNAPSHOT(s, i, &sl) {
-               int64_t d = 0;
+               int64_t d;
                if (s->flags & SS_COMPLETE)
                        d = (s->completion_time - s->creation_time) / 60;
-               dss_msg("%u\t%s\t%3" PRId64 ":%02" PRId64 "\n", s->interval, s->name, d/60, d%60);
+               else
+                       d = (now - s->creation_time) / 60;
+               dss_msg("%u\t%s\t%3" PRId64 ":%02" PRId64 "\n", s->interval,
+                       s->name, d / 60, d % 60);
        }
        free_snapshot_list(&sl);
        return 1;
@@ -1754,18 +1806,17 @@ static void show_subcommand_summary(void)
 int main(int argc, char **argv)
 {
        int ret;
-       const struct lls_command *cmd = CMD_PTR(DSS);
        char *errctx = NULL;
        unsigned num_inputs;
-       const struct dss_user_data *ud = NULL;
+       const struct dss_user_data *ud;
 
-       ret = lls_parse(argc, argv, cmd, &cmdline_lpr, &errctx);
+       ret = lls_parse(argc, argv, CMD_PTR(DSS), &cmdline_lpr, &errctx);
        if (ret < 0) {
                ret = lopsub_error(ret, &errctx);
                goto out;
        }
        lpr = cmdline_lpr;
-       ret = parse_config_file(false /* no SIGHUP */, cmd);
+       ret = parse_config_file(false /* no SIGHUP */, CMD_PTR(DSS));
        if (ret < 0)
                goto out;
        handle_version_and_help();
@@ -1777,24 +1828,24 @@ int main(int argc, char **argv)
                ret = lopsub_error(ret, &errctx);
                goto out;
        }
-       cmd = lls_cmd(ret, dss_suite);
-       ret = lls_parse(num_inputs, argv + argc - num_inputs, cmd,
+       subcmd = lls_cmd(ret, dss_suite);
+       ret = lls_parse(num_inputs, argv + argc - num_inputs, subcmd,
                &cmdline_sublpr, &errctx);
        if (ret < 0) {
                ret = lopsub_error(ret, &errctx);
                goto out;
        }
        sublpr = cmdline_sublpr;
-       ret = parse_config_file(false /* no SIGHUP */, cmd);
+       ret = parse_config_file(false /* no SIGHUP */, subcmd);
        if (ret < 0)
                goto out;
-       ret = check_config(cmd);
+       ret = check_config();
        if (ret < 0)
                goto out;
        ret = setup_signal_handling();
        if (ret < 0)
                goto out;
-       ud = lls_user_data(cmd);
+       ud = lls_user_data(subcmd);
        ret = ud->handler();
        signal_shutdown();
 out:
@@ -1807,8 +1858,8 @@ out:
        lls_free_parse_result(lpr, CMD_PTR(DSS));
        if (lpr != cmdline_lpr)
                lls_free_parse_result(cmdline_lpr, CMD_PTR(DSS));
-       lls_free_parse_result(sublpr, cmd);
+       lls_free_parse_result(sublpr, subcmd);
        if (sublpr != cmdline_sublpr)
-               lls_free_parse_result(cmdline_sublpr, cmd);
+               lls_free_parse_result(cmdline_sublpr, subcmd);
        exit(ret >= 0? EXIT_SUCCESS : EXIT_FAILURE);
 }