]> git.tuebingen.mpg.de Git - dss.git/commitdiff
Merge branch 'refs/heads/t/exit-hook'
authorAndre Noll <maan@tuebingen.mpg.de>
Fri, 17 Nov 2017 15:41:27 +0000 (16:41 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Fri, 17 Nov 2017 15:44:19 +0000 (16:44 +0100)
A fix for a long standing issue with the exit hook.

Cooking for a week.

* refs/heads/t/exit-hook:
  Allow word-splitting for exit hook.

NEWS
df.c
dss.c
dss.suite
err.h
file.h
gcc-compat.h
snap.h
tv.c

diff --git a/NEWS b/NEWS
index de1dca67284ae98c713412eba429af24d45239e3..09e72093165b72ab94d0484211ad3292895316b5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,22 +1,37 @@
 -----------------------
-x.y.z (to be announced)
+1.0.0 (to be announced)
 -----------------------
 
- - Improved error diagnostics for the kill subcommand.
-
- - The --no-resume option has been removed.
-
  - The gengetopt option parser has been replaced by the
  [lopsub](http://people.tuebingen.mpg.de/maan/lopsub) library. Hence
- lopsub must be installed to compile this package. Also help2man is
+ lopsub must be installed to compile this package. help2man is
  no longer required since lopsub has built-in roff support.
 
- - "make install" will install the executable and the man page.
+ - New subcommand "configtest" to check the command line options and
+ the configuration file for syntactic correctness.
+
+ - New option: --mountpoint. If this option is given, dss aborts if
+ no file system is mounted on the destination directory.
+
+ - New option --checksum to let rsync compute checksums occasionally.
+
+ - The --no-resume option has been removed.
+
+ - The ls subcommand now shows the age of incomplete snapshots rather
+ than 0:00.
 
  - In run mode, dss no longer exits successfully if another instance
  is already running.
 
- - New option --checksum to let rsync compute checksums occasionally.
+ - The command specified as the argument to --exit-hook is now subject
+ to word splitting. Previously, the string was executed as-is.
+
+ - Improved error diagnostics for the kill subcommand.
+
+ - For all subcommands other than "run", timestamps and function names
+ are omitted from the log output.
+
+ - "make install" will install the executable and the man page.
 
  - CFLAGS, CPPFLAGS and LDFLAGS can now be used to override the flags
  of the build system.
diff --git a/df.c b/df.c
index 003becccddebe5231289afb63c5c527c550357b1..82d14ba9863a5975fd0b9ef46313ec5fd720becf 100644 (file)
--- a/df.c
+++ b/df.c
@@ -18,7 +18,7 @@
 
 int get_disk_space(const char *path, struct disk_space *result)
 {
-       /* using floats allows to not care about integer overflows */
+       /* With floats we don't need to care about integer overflows. */
        float total_blocks, available_blocks, blocksize;
        float total_inodes, available_inodes;
 
diff --git a/dss.c b/dss.c
index a65967c07e41785c165deffefc69bc02fe80bef3..d60f00b1cc08f44ac29ae383c71b376f9e9ef49d 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);
@@ -295,6 +300,7 @@ static int send_signal(int sig)
                dss_msg("%d\n", (int)pid);
                return 0;
        }
+       DSS_NOTICE_LOG(("sending signal %d to pid %d\n", sig, (int)pid));
        ret = kill(pid, sig);
        if (ret < 0)
                return -ERRNO_TO_DSS_ERROR(errno);
@@ -515,7 +521,7 @@ static struct snapshot *find_orphaned_snapshot(struct snapshot_list *sl)
        struct snapshot *s;
        int i;
 
-       DSS_DEBUG_LOG(("looking for orphaned snapshots\n"));
+       DSS_DEBUG_LOG(("looking for old incomplete snapshots\n"));
        FOR_EACH_SNAPSHOT(s, i, sl) {
                if (snapshot_is_being_created(s))
                        continue;
@@ -729,6 +735,7 @@ static int try_to_free_disk_space(void)
        if (!low_disk_space)
                goto out;
        DSS_WARNING_LOG(("disk space low and nothing obvious to remove\n"));
+       why = "oldest";
        victim = find_oldest_removable_snapshot(&sl);
        if (victim)
                goto remove;
@@ -1053,20 +1060,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);
@@ -1082,13 +1108,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;
@@ -1226,7 +1252,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);
@@ -1686,19 +1712,30 @@ 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;
 }
 EXPORT_CMD_HANDLER(ls);
 
+static int com_configtest(void)
+{
+       printf("Syntax Ok\n");
+       return 0;
+}
+EXPORT_CMD_HANDLER(configtest);
+
 static int setup_signal_handling(void)
 {
        int ret;
@@ -1740,7 +1777,7 @@ static void show_subcommand_summary(void)
        for (i = 1; (cmd = lls_cmd(i, dss_suite)); i++) {
                const char *name = lls_command_name(cmd);
                const char *purpose = lls_purpose(cmd);
-               printf("%-10s%s\n", name, purpose);
+               printf("%-11s%s\n", name, purpose);
        }
        exit(EXIT_SUCCESS);
 }
@@ -1748,18 +1785,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();
@@ -1771,24 +1807,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:
@@ -1801,8 +1837,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);
 }
index e69cc28630c8a39359cba3aead33ab3b847011a8..1e798702676106238c180879889957d7913c2056 100644 (file)
--- a/dss.suite
+++ b/dss.suite
@@ -34,7 +34,7 @@ caption = Subcommands
                        However, there is one exception to this rule: The run subcommand
                        re-reads the configuration file when it receives the HUP signal. In
                        this case the options in the config file override any options that
-                       were previously given at the command line. This allows to change the
+                       were previously given at the command line. This allows changing the
                        configuration of a running dss process by sending SIGHUP.
                [/help]
        [option loglevel]
@@ -79,6 +79,18 @@ caption = Subcommands
 
                        This option is mandatory for all subcommands except kill.
                [/help]
+       [option mountpoint]
+               summary = abort if destination directory is not a mountpoint
+               [help]
+                       This option checks whether a file system is mounted on the directory
+                       specified as the argument to --dest-dir. Operation proceeds only
+                       if this is the case. Otherwise dss exits unsuccessfully without
+                       performing any action. Use this option to prevent snapshot creation
+                       if the snapshot file system is not mounted.
+
+                       This option is silently ignored for subcommands which do not depend
+                       on the destination directory.
+               [/help]
        [option Rsync-options]
                summary = Controlling how rsync is run
                flag ignored
@@ -312,8 +324,8 @@ caption = Subcommands
                typestr = percent
                default_val = 2
                [help]
-                       This is like --min-free-mb but allows to specify the amount of
-                       free disk space as a percentage. It is not recommended to set both
+                       This is like --min-free-mb but the amount of free disk space
+                       is specified as a percentage. It is not recommended to set both
                        --min-free-mb and --min-free-percent to zero as this will cause your
                        file system to fill up quickly.
                [/help]
@@ -475,6 +487,14 @@ caption = Subcommands
 
                        Sending SIGHUP causes the running dss process to reload its config file.
                [/help]
+[subcommand configtest]
+       purpose = run a configuration file syntax test
+       [description]
+               This command checks the command line options and the configuration
+               file for syntactic correctness. It either reports "Syntax Ok" and
+               exits successfully or prints information about the first syntax error
+               detected and terminates with exit code 1.
+       [/description]
 
 [section copyright]
        Written by Andre Noll
diff --git a/err.h b/err.h
index 3d4e136f208473fb2de31cb645a868f33995bbaa..e747bf3c1e1c079afe0188da066d9c4ac572d222 100644 (file)
--- a/err.h
+++ b/err.h
@@ -48,6 +48,7 @@ static inline char *dss_strerror(int num)
        DSS_ERROR(INVALID_NUMBER, "invalid number"), \
        DSS_ERROR(STRFTIME, "strftime() failed"), \
        DSS_ERROR(LOCALTIME, "localtime() failed"), \
+       DSS_ERROR(MOUNTPOINT, "destination directory is no mountpoint"), \
        DSS_ERROR(NULL_OPEN, "can not open /dev/null"), \
        DSS_ERROR(DUP_PIPE, "exec error: can not create pipe"), \
        DSS_ERROR(INVOLUNTARY_EXIT, "unexpected termination cause"), \
diff --git a/file.h b/file.h
index 140a505ba01b1fe3adb3614e118103517a722384..f82643a57fe961d3d1f67c837b69f794670188d0 100644 (file)
--- a/file.h
+++ b/file.h
@@ -5,17 +5,7 @@
  */
 int for_each_subdir(int (*func)(const char *, void *), void *private_data);
 __must_check int mark_fd_nonblocking(int fd);
-/**
- * A wrapper for rename(2).
- *
- * \param old_path The source path.
- * \param new_path The destination path.
- *
- * \return Standard.
- *
- * \sa rename(2).
- */
-_static_inline_ int dss_rename(const char *old_path, const char *new_path)
+static inline int dss_rename(const char *old_path, const char *new_path)
 {
        if (rename(old_path, new_path) >= 0)
                return 1;
index a15b0cef5c0f4b10672ab8a620429438baa1e1c2..258a3cb279b7e9045da566a17f9f7d8f52f6b40d 100644 (file)
@@ -2,24 +2,12 @@
        defined(__GNUC__) && \
        (__GNUC__ > maj || (__GNUC__ == maj && __GNUC_MINOR__ >= min))
 
-#if HAVE_GNUC(2,5)
-# define __noreturn    __attribute__ ((noreturn))
-#else
-# define __noreturn
-#endif
-
 #if HAVE_GNUC(3,0)
 # define __malloc      __attribute__ ((malloc))
 #else
 # define __malloc
 #endif
 
-#if HAVE_GNUC(2,7)
-# define __a_unused    __attribute__ ((unused))
-#else
-# define __a_unused
-#endif
-
 /* 
  * p is the number of the "format string" parameter, and q is 
  * the number of the first variadic parameter 
 # define __printf(p,q)
 #endif
 
-/*
- * as direct use of __printf(p,q) confuses doxygen, here are two extra macros
- * for those values p,q that are actually used by paraslash.
- */
 #define  __printf_1_2 __printf(1,2)
-#define  __printf_2_3 __printf(2,3)
 
 #if HAVE_GNUC(3,3)
 # define __must_check  __attribute__ ((warn_unused_result))
 #else
 # define __must_check  /* no warn_unused_result */
 #endif
-
-#define _static_inline_ static inline
diff --git a/snap.h b/snap.h
index 626ecad003a52a1a465ea6f7f2e11f960a97df3c..489b67a2fbbb39f69533e50bfc996b1c7db20525 100644 (file)
--- a/snap.h
+++ b/snap.h
@@ -93,7 +93,7 @@ int num_complete_snapshots(struct snapshot_list *sl);
 /**
  * Get the newest snapshot in a snapshot list.
  */
-_static_inline_ struct snapshot *get_newest_snapshot(struct snapshot_list *sl)
+static inline struct snapshot *get_newest_snapshot(struct snapshot_list *sl)
 {
        if (!sl->num_snapshots)
                return NULL;
diff --git a/tv.c b/tv.c
index 8ab5e5a90a6bd151c87750adb761c135b7ec774e..f954cef0c9a1342fedc4d2a49851ad56ca11aa9e 100644 (file)
--- a/tv.c
+++ b/tv.c
@@ -54,6 +54,5 @@ int64_t get_current_time(void)
 {
        time_t now;
        time(&now);
-       DSS_DEBUG_LOG(("now: %jd\n", (intmax_t)now));
        return (int64_t)now;
 }