From: Andre Noll Date: Thu, 16 Nov 2017 19:57:34 +0000 (+0100) Subject: Merge branch 'refs/heads/t/ls-incomplete-duration' X-Git-Tag: v1.0.0~9 X-Git-Url: http://git.tuebingen.mpg.de/?p=dss.git;a=commitdiff_plain;h=07ab37b55e1bf4ad7ef65fd25db84fa2a5f50f56;hp=dfe40aec1c2e651fdb3b62b0bcae52a5dfb1aa8d Merge branch 'refs/heads/t/ls-incomplete-duration' A simple patch which improves the ls output. Cooking for 10 days. * refs/heads/t/ls-incomplete-duration: ls: Print current duration of incomplete snapshots. --- diff --git a/.gitignore b/.gitignore index 977f1dc..1bb2a25 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ Makefile.deps -*.[oa] -cmdline.[ch] +*.o +*.swp dss.lsg.* dss dss.1 dss.1.html +dss.png +index.html diff --git a/INSTALL b/INSTALL index 8c70c34..09d8505 100644 --- a/INSTALL +++ b/INSTALL @@ -24,7 +24,7 @@ to install in /usr/local, or to install in /somewhere/else. -Also make sure that [rsync](http://rsync.samba.org/) is installed on +Also make sure that [rsync](https://rsync.samba.org/) is installed on your system. Version 2.6.1 or newer is required. Examples: @@ -70,7 +70,7 @@ system boundaries and increases the number of snapshots. rsync-option "--exclude-from=/etc/dss.exclude" # don't cross filesystem boundaries rsync-option "--one-file-system" - # maintain 2^6 - 1 = 63 snaphots + # maintain 2^6 - 1 = 63 snapshots num-intervals "6" The /etc/dss.exclude file could look like this (see rsync(1) for diff --git a/Makefile b/Makefile index a8e045c..f5c34af 100644 --- a/Makefile +++ b/Makefile @@ -37,8 +37,8 @@ dss: $(dss_objects) %.o: %.c Makefile $(CC) -c $(DSS_CPPFLAGS) $(CPPFLAGS) $(DSS_CFLAGS) $(CFLAGS) $< -%.png: %.dia - dia -e $@ -t png $< +dss.png: mklogo + ./mklogo > $@ %.lsg.h: %.suite lopsubgen --gen-h=$@ < $< diff --git a/NEWS b/NEWS index 755cad2..f103fb4 100644 --- a/NEWS +++ b/NEWS @@ -1,26 +1,40 @@ ----------------------- -x.y.z (to be announced) +1.0.0 (to be announced) ----------------------- - - Improved error diagnostics for the kill subcommand. + - 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. help2man is + no longer required since lopsub has built-in roff support. + + - 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. - 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 - no longer required since lopsub has built-in roff support. + - The ls subcommand now shows the age of incomplete snapshots rather + than 0:00. - - "make install" will install the executable and the man page. + - New option --checksum to let rsync compute checksums occasionally. - In run mode, dss no longer exits successfully if another instance is already running. - - New option --checksum to let rsync compute checksums occasionally. + - "make install" will install the executable and the man page. + + - Improved error diagnostics for the kill subcommand. + + - For all subcommands other than "run", timestamps and function names + are omitted from the log output. - CFLAGS, CPPFLAGS and LDFLAGS can now be used to override the flags of the build system. + - The dss logo is now created with ImageMagick rather than dia. + ------------------ 0.1.7 (2017-04-17) ------------------ diff --git a/README b/README index c5abb29..79b8889 100644 --- a/README +++ b/README @@ -11,7 +11,7 @@ configured, and there is no database to maintain. dss is also user-friendly because users can browse the snapshot directories without admin intervention and see the contents of the file -system at the various times a snapshot was taken. Each snaphot looks +system at the various times a snapshot was taken. Each snapshot looks like a full backup, so users can easily restore accidentally removed files by using their favorite file browser to simply copy files from the snapshot directory back to the live system. diff --git a/df.c b/df.c index 003becc..82d14ba 100644 --- 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 122472e..24d77eb 100644 --- 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); @@ -1700,6 +1726,13 @@ static int com_ls(void) } 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; @@ -1741,7 +1774,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); } @@ -1749,18 +1782,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(); @@ -1772,24 +1804,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: @@ -1802,8 +1834,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); } diff --git a/dss.dia b/dss.dia deleted file mode 100644 index a02f700..0000000 --- a/dss.dia +++ /dev/null @@ -1,960 +0,0 @@ - - - - - - - - - - - - - #A4# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #DSS# - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dss.suite b/dss.suite index e69cc28..1e79870 100644 --- 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 3d4e136..e747bf3 100644 --- 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 140a505..f82643a 100644 --- 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; diff --git a/gcc-compat.h b/gcc-compat.h index a15b0ce..258a3cb 100644 --- a/gcc-compat.h +++ b/gcc-compat.h @@ -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 @@ -30,17 +18,10 @@ # 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/mklogo b/mklogo new file mode 100755 index 0000000..54c8c95 --- /dev/null +++ b/mklogo @@ -0,0 +1,100 @@ +#!/bin/bash + +# 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.h b/snap.h index 626ecad..489b67a 100644 --- 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 8ab5e5a..f954cef 100644 --- 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; }