]> git.tuebingen.mpg.de Git - dss.git/commitdiff
Fix --config-file for relative paths. master
authorAndre Noll <maan@tuebingen.mpg.de>
Tue, 19 Sep 2023 14:31:49 +0000 (16:31 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 7 Nov 2023 14:11:55 +0000 (15:11 +0100)
The dss lock works by first turning the given config file path
argument into a canonical absolute path using dss_realpath(), then
hashing this absolute path to obtain a key ID for semget(2).

If the given path is relative, we have to compute the ID before
changing to the destination directory because dss_realpath() needs to
call stat(2) to detect symlinks, and this system call will fail if the
current working directory has changed. This is currently not the case
as we change to the destination directory early in check_config().

If dss_realpath() fails, we silently use the unmodified path argument
for hashing to deal with the case that the default config does not
exist. As a result, if relative paths are given, the key ID depends
on whether or not change_to_dest_dir() was called. This is the case
for the run subcommanmd, but not for the kill subcommand. Thus the
kill subcommand does not work as expected if a relative path is given.

Fix this by grabbing the lock before changing the working directory
in all cases.

14 files changed:
.gitignore
Makefile
NEWS
daemon.c
dss.c
dss.suite
dss.svg [new file with mode: 0644]
err.h
exec.c
index.html.in
mklogo [deleted file]
snap.c
str.c
str.h

index 1bb2a256e58d29073cf553ece92b355f5bf2ef7b..4aafb488c3fc2f1196e67b58c20b7998684a5126 100644 (file)
@@ -5,5 +5,4 @@ dss.lsg.*
 dss
 dss.1
 dss.1.html
-dss.png
 index.html
index 2d0a6bb64e6a4094081b6efc9779fcfc1156a153..a60a65b3e34e99f643ff4493cfff0c1b8d76a44d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ INSTALL_PROGRAM ?= $(INSTALL)
 INSTALL_DATA ?= $(INSTALL) -m 644
 MKDIR_P := mkdir -p
 
-VERSION_STRING = 1.0.0
+VERSION_STRING = 1.0.1
 
 dss_objects := dss.o str.o file.o exec.o sig.o daemon.o df.o tv.o snap.o ipc.o dss.lsg.o
 all: dss dss.1
@@ -38,9 +38,6 @@ dss: $(dss_objects)
 %.o: %.c Makefile
        $(CC) -c $(DSS_CPPFLAGS) $(CPPFLAGS) $(DSS_CFLAGS) $(CFLAGS) $<
 
-dss.png: mklogo
-       ./mklogo > $@
-
 %.lsg.h: %.suite
        lopsubgen --gen-h=$@ < $<
 %.lsg.c: %.suite
@@ -52,7 +49,7 @@ dss.png: mklogo
        groff -m man -Thtml -P -l -P -r $< | sed -e '1,/^<body>/d; /^<\/body>/,$$d' > $@
 
 clean:
-       rm -f *.o dss dss.1 dss.1.html Makefile.deps *.png *~ index.html dss.lsg.h dss.lsg.c
+       rm -f *.o dss dss.1 dss.1.html Makefile.deps *~ index.html dss.lsg.h dss.lsg.c
 
 ifneq ($(findstring strip, $(MAKECMDGOALS)),)
        strip_option := -s
diff --git a/NEWS b/NEWS
index 947ba587eee5524f15e7f04ac4c0d818e1199db1..a973da792387fac60cf26118281f66d2f94f83ee 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,19 @@
+------------------
+1.0.1 (2019-11-08)
+------------------
+Several fixes and improvements have accumulated over the years,
+so here is a new release. There are also some new features:
+
+- Support for multiple source directories.
+
+- The prune subcommand has been tweaked to use the same algorithm as
+  the run subcommand to decide which snapshot should be pruned. The
+  prune subcommand also gained the new --disk-space option.
+
+- When dss runs in daemon mode, it no longer sets the umask to zero.
+
+- The logo has been replaced by a slightly smaller one in svg format.
+
 ------------------
 1.0.0 (2017-11-19)
 ------------------
index 709785b437041654e3cacd2fd4f252aea8fa4173..ad73061c14ec5916284f2ab67438306d6484827d 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -60,7 +60,6 @@ int daemon_init(void)
        /* become session leader */
        if (setsid() < 0)
                goto err;
-       umask(0);
        null = open("/dev/null", O_RDWR);
        if (null < 0)
                goto err;
diff --git a/dss.c b/dss.c
index a219cfd37b4569fbc3aa67fb2d4443a950353197..0992ec6fd18651f85c2bab2c7d8d45603b3a786c 100644 (file)
--- a/dss.c
+++ b/dss.c
@@ -651,6 +651,7 @@ static struct snapshot *find_oldest_removable_snapshot(struct snapshot_list *sl)
        int i, num_complete;
        struct snapshot *s, *ref = NULL;
 
+       DSS_DEBUG_LOG(("picking snapshot with earliest creation time\n"));
        num_complete = num_complete_snapshots(sl);
        if (num_complete <= OPT_UINT32_VAL(DSS, MIN_COMPLETE))
                return NULL;
@@ -661,7 +662,6 @@ static struct snapshot *find_oldest_removable_snapshot(struct snapshot_list *sl)
                        ref = s;
                        continue;
                }
-               DSS_INFO_LOG(("oldest removable snapshot: %s\n", s->name));
                return s;
        }
        assert(ref);
@@ -669,6 +669,50 @@ static struct snapshot *find_oldest_removable_snapshot(struct snapshot_list *sl)
        return ref;
 }
 
+/* returns NULL <==> *reason is set to NULL */
+static struct snapshot *find_removable_snapshot(struct snapshot_list *sl,
+               bool try_hard, char **reason)
+{
+       struct snapshot *victim;
+
+       /*
+        * Don't remove anything if there is free space and we have fewer
+        * snapshots than configured, plus one. This way there is always one
+        * snapshot that can be recycled.
+        */
+       if (!try_hard && sl->num_snapshots <=
+                       1 << OPT_UINT32_VAL(DSS, NUM_INTERVALS))
+               goto nope;
+       victim = find_orphaned_snapshot(sl);
+       if (victim) {
+               *reason = make_message("orphaned");
+               return victim;
+       }
+       victim = find_outdated_snapshot(sl);
+       if (victim) {
+               *reason = make_message("outdated");
+               return victim;
+       }
+       if (!OPT_GIVEN(DSS, KEEP_REDUNDANT)) {
+               victim = find_redundant_snapshot(sl);
+               if (victim) {
+                       *reason = make_message("redundant");
+                       return victim;
+               }
+       }
+       if (!try_hard)
+               goto nope;
+       DSS_WARNING_LOG(("nothing obvious to remove\n"));
+       victim = find_oldest_removable_snapshot(sl);
+       if (victim) {
+               *reason = make_message("oldest");
+               return victim;
+       }
+nope:
+       *reason = NULL;
+       return NULL;
+}
+
 static int rename_incomplete_snapshot(int64_t start)
 {
        char *old_name;
@@ -701,7 +745,7 @@ static int try_to_free_disk_space(void)
        struct snapshot_list sl;
        struct snapshot *victim;
        struct timeval now;
-       const char *why;
+       char *why;
        int low_disk_space;
 
        ret = disk_space_low(NULL);
@@ -712,55 +756,25 @@ static int try_to_free_disk_space(void)
        if (tv_diff(&next_removal_check, &now, NULL) > 0)
                return 0;
        if (!low_disk_space) {
-               if (OPT_GIVEN(DSS, KEEP_REDUNDANT))
-                       return 0;
                if (snapshot_creation_status != HS_READY)
                        return 0;
                if (next_snapshot_is_due())
                        return 0;
        }
-       /*
-        * Idle and --keep_redundant not given, or low disk space. Look at
-        * existing snapshots.
-        */
+       /* Idle or low disk space, look at existing snapshots. */
        dss_get_snapshot_list(&sl);
-       ret = 0;
-       /*
-        * Don't remove anything if there is free space and we have fewer
-        * snapshots than configured, plus one. This way there is always one
-        * snapshot that can be recycled.
-        */
-       if (!low_disk_space && sl.num_snapshots <=
-                       1 << OPT_UINT32_VAL(DSS, NUM_INTERVALS))
-               goto out;
-       why = "outdated";
-       victim = find_outdated_snapshot(&sl);
-       if (victim)
-               goto remove;
-       why = "redundant";
-       victim = find_redundant_snapshot(&sl);
-       if (victim)
-               goto remove;
-       why = "orphaned";
-       victim = find_orphaned_snapshot(&sl);
+       victim = find_removable_snapshot(&sl, low_disk_space, &why);
+       if (victim) {
+               pre_remove_hook(victim, why);
+               free(why);
+       }
+       free_snapshot_list(&sl);
        if (victim)
-               goto remove;
-       /* try harder only if disk space is low */
+               return 1;
        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;
+               return 0;
        DSS_CRIT_LOG(("uhuhu: disk space low and nothing to remove\n"));
-       ret = -ERRNO_TO_DSS_ERROR(ENOSPC);
-       goto out;
-remove:
-       pre_remove_hook(victim, why);
-out:
-       free_snapshot_list(&sl);
-       return ret;
+       return -ERRNO_TO_DSS_ERROR(ENOSPC);
 }
 
 static void post_create_hook(void)
@@ -1108,7 +1122,6 @@ static int change_to_dest_dir(void)
 
 static int check_config(void)
 {
-       int ret;
        uint32_t unit_interval = OPT_UINT32_VAL(DSS, UNIT_INTERVAL);
        uint32_t num_intervals = OPT_UINT32_VAL(DSS, NUM_INTERVALS);
 
@@ -1133,9 +1146,6 @@ static int check_config(void)
                        DSS_ERROR_LOG(("--dest-dir required\n"));
                        return -E_SYNTAX;
                }
-               ret = change_to_dest_dir();
-               if (ret < 0)
-                       return ret;
        }
        DSS_DEBUG_LOG(("number of intervals: %i\n", num_intervals));
        return 1;
@@ -1249,8 +1259,6 @@ close_fd:
                close(fd);
 out:
        free(config_file);
-       if (ret < 0)
-               DSS_EMERG_LOG(("%s\n", dss_strerror(-ret)));
        return ret;
 }
 
@@ -1444,7 +1452,7 @@ static void create_rsync_argv(char ***argv, int64_t *num)
                                        OPT_STRING_VAL(DSS, REMOTE_USER) : logname,
                                OPT_STRING_VAL(DSS, REMOTE_HOST),
                                lls_string_val(j, OPT_RESULT(DSS, SOURCE_DIR)),
-                               N == 1? "/" : ""
+                               OPT_GIVEN(DSS, SOURCE_DIR) == 1? "/" : ""
                        );
                }
        }
@@ -1567,7 +1575,7 @@ static void exit_hook(int exit_code)
 {
        pid_t pid;
        char **argv, *tmp = dss_strdup(OPT_STRING_VAL(DSS, EXIT_HOOK));
-       unsigned n = split_args(tmp, &argv, " \t");
+       unsigned n = split_args(tmp, &argv);
 
        n++;
        argv = dss_realloc(argv, (n + 1) * sizeof(char *));
@@ -1608,12 +1616,23 @@ static int com_run(void)
                DSS_ERROR_LOG(("pid %d\n", (int)pid));
                return -E_ALREADY_RUNNING;
        }
+       /*
+        * Order is important here: Since daemon_init() forks, it would drop
+        * the lock if it had been acquired already. Changing the cwd before
+        * grabbing the lock causes stat(2) to fail in case a relative config
+        * file path was given, which results in a different key ID for
+        * locking. Therefore we must first daemonize, then lock, then change
+        * the cwd.
+        */
        if (OPT_GIVEN(RUN, DAEMON)) {
                fd = daemon_init();
                daemonized = true;
                logfile = open_log(OPT_STRING_VAL(RUN, LOGFILE));
        }
        lock_dss_or_die();
+       ret = change_to_dest_dir();
+       if (ret < 0)
+               return ret;
        dump_dss_config("startup");
        ret = install_sighandler(SIGHUP);
        if (ret < 0)
@@ -1645,55 +1664,58 @@ static int com_prune(void)
        struct snapshot_list sl;
        struct snapshot *victim;
        struct disk_space ds;
-       const char *why;
+       char *why;
+       bool try_hard;
 
        lock_dss_or_die();
-       ret = get_disk_space(".", &ds);
+       ret = change_to_dest_dir();
        if (ret < 0)
                return ret;
-       log_disk_space(&ds);
+       switch (OPT_UINT32_VAL(PRUNE, DISK_SPACE)) {
+       case FDS_LOW: try_hard = true; break;
+       case FDS_HIGH: try_hard = false; break;
+       default:
+               ret = get_disk_space(".", &ds);
+               if (ret < 0)
+                       return ret;
+               log_disk_space(&ds);
+               try_hard = disk_space_low(&ds);
+       }
        dss_get_snapshot_list(&sl);
-       why = "outdated";
-       victim = find_outdated_snapshot(&sl);
-       if (victim)
-               goto rm;
-       why = "redundant";
-       victim = find_redundant_snapshot(&sl);
-       if (victim)
-               goto rm;
-       ret = 0;
-       goto out;
-rm:
+       victim = find_removable_snapshot(&sl, try_hard, &why);
+       if (!victim) {
+               dss_msg("nothing to prune\n");
+               ret = 0;
+               goto free_sl;
+       }
        if (OPT_GIVEN(DSS, DRY_RUN)) {
-               dss_msg("%s snapshot %s (interval = %i)\n",
+               dss_msg("picking %s snapshot %s (interval = %i)\n",
                        why, victim->name, victim->interval);
                ret = 0;
-               goto out;
+               goto free_why;
        }
        pre_remove_hook(victim, why);
        if (snapshot_removal_status == HS_PRE_RUNNING) {
                ret = wait_for_remove_process();
                if (ret < 0)
-                       goto out;
+                       goto free_why;
+               ret = -E_HOOK_FAILED;
                if (snapshot_removal_status != HS_PRE_SUCCESS)
-                       goto out;
+                       goto free_why;
        }
        ret = exec_rm();
        if (ret < 0)
-               goto out;
+               goto free_why;
        ret = wait_for_remove_process();
        if (ret < 0)
-               goto out;
-       if (snapshot_removal_status != HS_SUCCESS)
-               goto out;
+               goto free_why;
+       assert(snapshot_removal_status == HS_SUCCESS);
        post_remove_hook();
-       if (snapshot_removal_status != HS_POST_RUNNING)
-               goto out;
+       assert(snapshot_removal_status == HS_POST_RUNNING);
        ret = wait_for_remove_process();
-       if (ret < 0)
-               goto out;
-       ret = 1;
-out:
+free_why:
+       free(why);
+free_sl:
        free_snapshot_list(&sl);
        return ret;
 }
@@ -1705,6 +1727,9 @@ static int com_create(void)
        char **rsync_argv;
 
        lock_dss_or_die();
+       ret = change_to_dest_dir();
+       if (ret < 0)
+               return ret;
        if (OPT_GIVEN(DSS, DRY_RUN)) {
                int i;
                char *msg = NULL;
@@ -1750,11 +1775,14 @@ EXPORT_CMD_HANDLER(create);
 
 static int com_ls(void)
 {
-       int i;
+       int i, ret;
        struct snapshot_list sl;
        struct snapshot *s;
        int64_t now = get_current_time();
 
+       ret = change_to_dest_dir();
+       if (ret < 0)
+               return ret;
        dss_get_snapshot_list(&sl);
        FOR_EACH_SNAPSHOT(s, i, &sl) {
                int64_t d;
index 16c7e58542eff8750c8437dcea137f347a435950..f9cf8af5993345452fddc7b3d9790d0d7e6d7a13 100644 (file)
--- a/dss.suite
+++ b/dss.suite
@@ -450,19 +450,38 @@ caption = Subcommands
                snapshots.
        [/description]
 [subcommand prune]
-       purpose = remove redundant and outdated snapshots
+       purpose = remove snapshots
        [description]
-               A snapshot is considered outdated if its interval number is greater or
-               equal than the specified number of unit intervals. See --unit-interval
-               and --num-intervals above.
-
-               A snapshot is said to be redundant if the interval it belongs to
-               contains more than the configured number of snapshots.
-
-               The prune command gets rid of both outdated and redundant snapshots. At
-               most one snapshot is removed per invocation. If --dry-run is given, the
-               subcommand only prints the snapshot that would be removed.
+               A snapshot is said to be (a) outdated if its interval number is greater
+               or equal than the specified number of unit intervals, (b) redundant if
+               the interval it belongs to contains more than the configured number of
+               snapshots, and (c) orphaned if it is incomplete and not being created
+               or deleted. All other snapshots are called regular.
+
+               Unless --dry-run is given, which just prints the snapshot that would be
+               removed, this subcommand gets rid of non-regular snapshots.  At most
+               one snapshot is removed per invocation. If no such snapshot exists
+               and disk space is low, the subcommand also removes regular snapshots,
+               always picking the oldest one.
+
+               The subcommand fails if there is another dss "run" process.
        [/description]
+       [option disk-space]
+               summary = act as if free disk space was high/low
+               arg_info = required_arg
+               arg_type = string
+               typestr = mode
+               values = {
+                       FDS_CHECK = "check",
+                       FDS_HIGH = "high",
+                       FDS_LOW = "low"
+               }
+               default_val = check
+               [help]
+                       By default, free disk space is checked and even regular snapshots
+                       become candidates for removal if disk space is low. This option
+                       overrides the result of the check.
+               [/help]
 [subcommand ls]
        purpose = print the list of all snapshots
        [description]
diff --git a/dss.svg b/dss.svg
new file mode 100644 (file)
index 0000000..fb69bd7
--- /dev/null
+++ b/dss.svg
@@ -0,0 +1,65 @@
+<svg
+       xmlns="http://www.w3.org/2000/svg"
+       xmlns:xlink="http://www.w3.org/1999/xlink"
+       width="154"
+       height="100"
+>
+       <defs>
+               <rect id="rect"
+                       stroke-width="0"
+                       x="0"
+                       y="0"
+                       width="37"
+                       height="94"
+               />
+       </defs>
+       <defs>
+               <circle id="circle"
+                       stroke-width="1"
+                       stroke="black"
+                       fill="#a22"
+                       cx="0"
+                       cy="0"
+                       r="4"
+               />
+       </defs>
+       <marker
+               id="arrow"
+               stroke="white"
+               fill="white"
+               viewBox="0 0 10 10" refX="5" refY="5"
+               markerWidth="4" markerHeight="4"
+               orient="auto-start-reverse">
+               <path d="M 0 0 L 10 5 L 0 10 z" />
+       </marker>
+       <use fill="#bbf" xlink:href="#rect" transform="translate(0, 0)"/>
+       <use fill="#99f" xlink:href="#rect" transform="translate(36, 0)"/>
+       <use fill="#44f" xlink:href="#rect" transform="translate(72, 0)"/>
+       <use fill="#11f" xlink:href="#rect" transform="translate(108, 0)"/>
+
+       <text x="47" y="40" stroke="white" fill="white" font-size="25">DSS</text>
+
+       <line
+               x1="7" y1="50" x2="127" y2="50" stroke-width="3"
+               stroke="white"
+               marker-end="url(#arrow)"
+       />
+       <use xlink:href="#circle" x="18" y="77"/>
+
+       <use xlink:href="#circle" x="48" y="77"/>
+       <use xlink:href="#circle" x="60" y="77"/>
+
+       <use xlink:href="#circle" x="77" y="77"/>
+       <use xlink:href="#circle" x="86" y="77"/>
+       <use xlink:href="#circle" x="95" y="77"/>
+       <use xlink:href="#circle" x="104" y="77"/>
+
+       <use xlink:href="#circle" x="113" y="70"/>
+       <use xlink:href="#circle" x="122" y="70"/>
+       <use xlink:href="#circle" x="131" y="70"/>
+       <use xlink:href="#circle" x="140" y="70"/>
+       <use xlink:href="#circle" x="114" y="85"/>
+       <use xlink:href="#circle" x="122" y="85"/>
+       <use xlink:href="#circle" x="130" y="85"/>
+       <use xlink:href="#circle" x="138" y="85"/>
+</svg>
diff --git a/err.h b/err.h
index 9505dfcc99f718ce1f8fe540401446e1dff82e8e..bd22554dd90d8ee2e282ff3c8c24b2fd56b7d5cd 100644 (file)
--- a/err.h
+++ b/err.h
@@ -44,6 +44,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(HOOK_FAILED, "hook failure"), \
        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"), \
diff --git a/exec.c b/exec.c
index 9e0565dc5e08cd0905fd70de01ca0c9b8a100a0e..ece7ec29aa233051cf10bd5c4f3b1b6adfd14cfe 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -56,7 +56,7 @@ void dss_exec_cmdline_pid(pid_t *pid, const char *cmdline)
 {
        char **argv, *tmp = dss_strdup(cmdline);
 
-       split_args(tmp, &argv, " \t");
+       split_args(tmp, &argv);
        dss_exec(pid, argv[0], argv);
        free(argv);
        free(tmp);
index 42606cabd91b2e80a41a3c959ac4b683a53f5753..b505761997987f74dfe6a9d2fe73046e21a0e868 100644 (file)
@@ -12,7 +12,7 @@
        <table>
                <tr>
                        <td>
-                               <IMG SRC="dss.png" alt="dss">
+                               <img src="dss.svg" alt="dss">
                        </td>
                        <td>
                                <h1>The dyadic snapshot scheduler</h1>
diff --git a/mklogo b/mklogo
deleted file mode 100755 (executable)
index d4705ba..0000000
--- a/mklogo
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-
-# Script for Image Magick that writes the dss logo in png format
-# to stdout.
-
-set -u
-
-width=320
-height=110
-border=5
-radius1=4
-radius2=5
-circle_y=90
-arrow_y=70
-text_x=128
-text_y=55
-pointsize=36
-
-declare -a circle arrow rectangle text
-
-make_circles()
-{
-       local inner='stroke black fill red circle'
-       local outer='stroke black fill black circle'
-       local num=1
-       local idx=0
-       local y=$circle_y
-       local step x0 i j idx num
-
-       for ((i = 0; i < 4; i++)); do
-               step=$(((width / 4 + num) / (num + 1)))
-               x0=$((border + width / 4 * i))
-               for ((j = 1; j <= $num; j++)); do
-                       x=$((x0 + j * $step))
-                       circle[$idx]='-draw'; let idx++
-                       circle[$idx]="$outer $x,$y,$((x + radius2)),$y"; let idx++
-                       circle[$idx]='-draw'; let idx++
-                       circle[$idx]="$inner $x,$y,$((x + radius1)),$y"; let idx++
-               done
-               num=$((num * 2))
-       done
-       #echo "${circle[@]}"; exit 0
-}
-
-make_arrow()
-{
-       local arrow_head='l -15,-5  +5,+5  -5,+5  +15,-5 z'
-       local idx=0
-       local x0 x1 y
-
-       arrow[$idx]='-draw'; let idx++
-       x0=$((3 * border)); x1=$((width - 2 * border))
-       y=$arrow_y
-       arrow[$idx]="stroke white line $x0,$y $x1,$y"; let idx++
-       arrow[$idx]='-draw'; let idx++
-       x0=$((width - 2 * border))
-       arrow[$idx]="stroke white fill white path 'M $x0,$y $arrow_head'"
-       #echo "${arrow[@]}"; exit 0
-}
-
-make_rectangles()
-{
-       local idx=0
-       local x x0 x1 y y0 y1 i red_green color
-
-       rectangle[$idx]='-draw'; let idx++
-       x=$((width + 2 * border))
-       y=$((height + 2 * border))
-       rectangle[$idx]="stroke yellow fill yellow rectangle 0,0 $x,$y"; let idx++
-       for ((i = 0; i < 4; i++)); do
-               rectangle[$idx]='-draw'; let idx++
-               red_green="$(printf '%02x' $(((3 - i) * 60)))"
-               color="#${red_green}${red_green}ff"
-               x0=$((border + i * width / 4)); x1=$((x0 + width / 4 - 1))
-               y0=$border; y1=$((y0 + height))
-               rectangle[$idx]="stroke $color fill $color rectangle $x0,$y0 $x1,$y1"
-               let idx++
-       done
-       #echo "${rectangle[@]}"; exit 0
-}
-
-make_text()
-{
-       text=(-pointsize $pointsize -draw \
-               "fill white text $text_x,$text_y DSS")
-       #echo "${text[@]}"; exit 0
-}
-
-make_rectangles
-make_arrow
-make_circles
-make_text
-
-convert -size $((width + 2 * border))x$((height + 2 * border)) \
-       -background none xc: \
-       "${rectangle[@]}" \
-       "${arrow[@]}" \
-       "${circle[@]}" \
-       "${text[@]}" \
-       png:-
diff --git a/snap.c b/snap.c
index 8de76d459da1a8844a452fa576d706014b229be9..da95c1f342dc09bcc01d1eef58a075dd721472ef 100644 (file)
--- a/snap.c
+++ b/snap.c
@@ -146,7 +146,7 @@ static int compare_snapshots(const void *a, const void *b)
        return NUM_COMPARE(s2->creation_time, s1->creation_time);
 }
 
-
+/* The returned snapshot list is sorted by creation time. */
 void get_snapshot_list(struct snapshot_list *sl, int unit_interval,
                int num_intervals)
 {
diff --git a/str.c b/str.c
index 623def1fd9b7aa7ee72f221aa6759fcf74b70948..13776300d6ca1aa62479df6be4428a5ba8d836a0 100644 (file)
--- a/str.c
+++ b/str.c
@@ -212,20 +212,21 @@ __must_check __malloc char *dss_logname(void)
  *
  * \param args The string to be split.
  * \param argv_ptr Pointer to the list of substrings.
- * \param delim Delimiter.
  *
- * This function modifies \a args by replacing each occurrence of \a delim by
- * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically
- * and these pointers are initialized to point to the broken-up substrings
- * within \a args. A pointer to this array is returned via \a argv_ptr.
+ * This function modifies the string given by the first argument by replacing
+ * all occurrences of space and '\t' characters by '\0'. A NULL-terminated
+ * array of pointers to char * is allocated dynamically, and these pointers are
+ * initialized to point to the broken-up substrings.  A pointer to this array
+ * is returned via the last argument.
  *
- * \return The number of substrings found in \a args.
+ * \return The number of substrings found.
  */
-unsigned split_args(char *args, char *** const argv_ptr, const char *delim)
+unsigned split_args(char *args, char *** const argv_ptr)
 {
        char *p;
        char **argv;
        size_t n = 0, i, j;
+       const char delim[] = " \t";
 
        p = args + strspn(args, delim);
        for (;;) {
diff --git a/str.h b/str.h
index b7f26350ccb5941ced169d6b893ff18b38fcaee1..7b121efb00d1e19c5b92e1711b25c23cd29a8092 100644 (file)
--- a/str.h
+++ b/str.h
@@ -8,7 +8,7 @@ __must_check __malloc char *dss_strdup(const char *s);
 __must_check __malloc char *get_homedir(void);
 int dss_atoi64(const char *str, int64_t *value);
 __must_check __malloc char *dss_logname(void);
-unsigned split_args(char *args, char *** const argv_ptr, const char *delim);
+unsigned split_args(char *args, char *** const argv_ptr);
 
 
 /** \cond LLONG_MAX and LLONG_LIN might not be defined. */