]> git.tuebingen.mpg.de Git - dss.git/blob - dss.c
5f7cfe04599ef8c60b7a68c94ff26cd44117e28d
[dss.git] / dss.c
1 /*
2  * Copyright (C) 2008-2011 Andre Noll <maan@tuebingen.mpg.de>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdarg.h>
9 #include <assert.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <signal.h>
13 #include <ctype.h>
14 #include <stdbool.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <inttypes.h>
18 #include <sys/time.h>
19 #include <time.h>
20 #include <sys/wait.h>
21 #include <fnmatch.h>
22 #include <limits.h>
23
24
25 #include "gcc-compat.h"
26 #include "cmdline.h"
27 #include "log.h"
28 #include "str.h"
29 #include "err.h"
30 #include "file.h"
31 #include "exec.h"
32 #include "daemon.h"
33 #include "sig.h"
34 #include "df.h"
35 #include "tv.h"
36 #include "snap.h"
37 #include "ipc.h"
38
39 /** Command line and config file options. */
40 static struct gengetopt_args_info conf;
41 /** Non-NULL if we log to a file. */
42 static FILE *logfile;
43 /** The read end of the signal pipe */
44 static int signal_pipe;
45 /** Process id of current pre-create-hook/rsync/post-create-hook process. */
46 static pid_t create_pid;
47 /** Whether the pre-create-hook/rsync/post-create-hook is currently stopped. */
48 static int create_process_stopped;
49 /** How many times in a row the rsync command failed. */
50 static int num_consecutive_rsync_errors;
51 /** Process id of current pre-remove/rm/post-remove process. */
52 static pid_t remove_pid;
53 /** When the next snapshot is due. */
54 static int64_t next_snapshot_time;
55 /** When to try to remove something. */
56 static struct timeval next_removal_check;
57 /** Creation time of the snapshot currently being created. */
58 static int64_t current_snapshot_creation_time;
59 /** The snapshot currently being removed. */
60 struct snapshot *snapshot_currently_being_removed;
61 /** Needed by the post-create hook. */
62 static char *path_to_last_complete_snapshot;
63 static char *name_of_reference_snapshot;
64 /** \sa \ref snap.h for details. */
65 enum hook_status snapshot_creation_status;
66 /** \sa \ref snap.h for details. */
67 enum hook_status snapshot_removal_status;
68
69
70 DEFINE_DSS_ERRLIST;
71 static const char *hook_status_description[] = {HOOK_STATUS_ARRAY};
72
73 /* may be called with ds == NULL. */
74 static int disk_space_low(struct disk_space *ds)
75 {
76         struct disk_space ds_struct;
77
78         if (!ds) {
79                 int ret = get_disk_space(".", &ds_struct);
80                 if (ret < 0)
81                         return ret;
82                 ds = &ds_struct;
83         }
84         if (conf.min_free_mb_arg)
85                 if (ds->free_mb < conf.min_free_mb_arg)
86                         return 1;
87         if (conf.min_free_percent_arg)
88                 if (ds->percent_free < conf.min_free_percent_arg)
89                         return 1;
90         if (conf.min_free_percent_inodes_arg)
91                 if (ds->percent_free_inodes < conf.min_free_percent_inodes_arg)
92                         return 1;
93         return 0;
94 }
95
96 static void dump_dss_config(const char *msg)
97 {
98         const char dash[] = "-----------------------------";
99         int ret;
100         FILE *log = logfile? logfile : stderr;
101         struct disk_space ds;
102         int64_t now = get_current_time();
103
104         if (conf.loglevel_arg > INFO)
105                 return;
106
107         fprintf(log, "%s <%s config> %s\n", dash, msg, dash);
108         fprintf(log, "\n*** disk space ***\n\n");
109         ret = get_disk_space(".", &ds);
110         if (ret >= 0) {
111                 DSS_INFO_LOG(("disk space low: %s\n", disk_space_low(&ds)?
112                         "yes" : "no"));
113                 log_disk_space(&ds);
114         } else
115                 DSS_ERROR_LOG(("can not get free disk space: %s\n",
116                         dss_strerror(-ret)));
117
118         /* we continue on errors from get_disk_space */
119
120         fprintf(log, "\n*** command line and config file options ***\n\n");
121         cmdline_parser_dump(log, &conf);
122         fprintf(log, "\n*** internal state ***\n\n");
123         fprintf(log,
124                 "pid: %d\n"
125                 "logile: %s\n"
126                 "snapshot_currently_being_removed: %s\n"
127                 "path_to_last_complete_snapshot: %s\n"
128                 "reference_snapshot: %s\n"
129                 "snapshot_creation_status: %s\n"
130                 "snapshot_removal_status: %s\n"
131                 "num_consecutive_rsync_errors: %d\n"
132                 ,
133                 (int) getpid(),
134                 logfile? conf.logfile_arg : "stderr",
135                 snapshot_currently_being_removed?
136                         snapshot_currently_being_removed->name : "(none)",
137                 path_to_last_complete_snapshot?
138                         path_to_last_complete_snapshot : "(none)",
139                 name_of_reference_snapshot?
140                         name_of_reference_snapshot : "(none)",
141                 hook_status_description[snapshot_creation_status],
142                 hook_status_description[snapshot_removal_status],
143                 num_consecutive_rsync_errors
144         );
145         if (create_pid != 0)
146                 fprintf(log,
147                         "create_pid: %" PRId32 "\n"
148                         "create process is %sstopped\n"
149                         ,
150                         create_pid,
151                         create_process_stopped? "" : "not "
152                 );
153         if (remove_pid != 0)
154                 fprintf(log, "remove_pid: %" PRId32 "\n", remove_pid);
155         if (next_snapshot_time != 0)
156                 fprintf(log, "next snapshot due in %" PRId64 " seconds\n",
157                         next_snapshot_time - now);
158         if (current_snapshot_creation_time != 0)
159                 fprintf(log, "current_snapshot_creation_time: %"
160                         PRId64 " (%" PRId64 " seconds ago)\n",
161                         current_snapshot_creation_time,
162                         now - current_snapshot_creation_time
163                 );
164         if (next_removal_check.tv_sec != 0) {
165                 fprintf(log, "next removal check: %llu (%llu seconds ago)\n",
166                         (long long unsigned)next_removal_check.tv_sec,
167                         now - (long long unsigned)next_removal_check.tv_sec
168                 );
169
170         }
171         fprintf(log, "%s </%s config> %s\n", dash, msg, dash);
172 }
173
174 /* a litte cpp magic helps to DRY */
175 #define COMMANDS \
176         COMMAND(ls) \
177         COMMAND(create) \
178         COMMAND(prune) \
179         COMMAND(run) \
180         COMMAND(kill) \
181         COMMAND(reload) \
182
183 #define COMMAND(x) static int com_ ##x(void);
184 COMMANDS
185 #undef COMMAND
186 #define COMMAND(x) if (conf.x ##_given) return com_ ##x();
187 static int call_command_handler(void)
188 {
189         COMMANDS
190         DSS_EMERG_LOG(("BUG: did not find command handler\n"));
191         return -E_BUG;
192 }
193 #undef COMMAND
194 #undef COMMANDS
195
196 static int loglevel = -1;
197 static const char *location_file = NULL;
198 static int         location_line = -1;
199 static const char *location_func = NULL;
200
201 void dss_log_set_params(int ll, const char *file, int line, const char *func)
202 {
203         loglevel = ll;
204         location_file = file;
205         location_line = line;
206         location_func = func;
207 }
208
209 /**
210  * The log function of dss.
211  *
212  * \param ll Loglevel.
213  * \param fml Usual format string.
214  *
215  * All DSS_XXX_LOG() macros use this function.
216  */
217 __printf_1_2 void dss_log(const char* fmt,...)
218 {
219         va_list argp;
220         FILE *outfd;
221         struct tm *tm;
222         time_t t1;
223         char str[255] = "";
224
225         if (loglevel < conf.loglevel_arg)
226                 return;
227         outfd = logfile? logfile : stderr;
228         time(&t1);
229         tm = localtime(&t1);
230         strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
231         fprintf(outfd, "%s ", str);
232         if (conf.loglevel_arg <= INFO)
233                 fprintf(outfd, "%i: ", loglevel);
234 #ifdef DSS_NO_FUNC_NAMES
235         fprintf(outfd, "%s:%d: ", location_file, location_line);
236 #else
237         fprintf(outfd, "%s: ", location_func);
238 #endif
239         va_start(argp, fmt);
240         vfprintf(outfd, fmt, argp);
241         va_end(argp);
242 }
243
244 /**
245  * Print a message either to stdout or to the log file.
246  */
247 static __printf_1_2 void dss_msg(const char* fmt,...)
248 {
249         FILE *outfd = conf.daemon_given? logfile : stdout;
250         va_list argp;
251         va_start(argp, fmt);
252         vfprintf(outfd, fmt, argp);
253         va_end(argp);
254 }
255
256 static char *get_config_file_name(void)
257 {
258         char *home, *config_file;
259
260         if (conf.config_file_given)
261                 return dss_strdup(conf.config_file_arg);
262         home = get_homedir();
263         config_file = make_message("%s/.dssrc", home);
264         free(home);
265         return config_file;
266 }
267
268 static int send_signal(int sig)
269 {
270         pid_t pid;
271         char *config_file = get_config_file_name();
272         int ret = get_dss_pid(config_file, &pid);
273
274         free(config_file);
275         if (ret < 0)
276                 return ret;
277         if (conf.dry_run_given) {
278                 dss_msg("%d\n", (int)pid);
279                 return 0;
280         }
281         ret = kill(pid, sig);
282         if (ret < 0)
283                 return -ERRNO_TO_DSS_ERROR(errno);
284         return 1;
285 }
286
287 static int com_kill(void)
288 {
289         return send_signal(SIGTERM);
290 }
291
292 static int com_reload(void)
293 {
294         return send_signal(SIGHUP);
295 }
296
297 static void dss_get_snapshot_list(struct snapshot_list *sl)
298 {
299         get_snapshot_list(sl, conf.unit_interval_arg, conf.num_intervals_arg);
300 }
301
302 static int64_t compute_next_snapshot_time(void)
303 {
304         int64_t x = 0, now = get_current_time(), unit_interval
305                 = 24 * 3600 * conf.unit_interval_arg, ret;
306         unsigned wanted = desired_number_of_snapshots(0, conf.num_intervals_arg),
307                 num_complete = 0;
308         int i;
309         struct snapshot *s = NULL;
310         struct snapshot_list sl;
311
312         dss_get_snapshot_list(&sl);
313         FOR_EACH_SNAPSHOT(s, i, &sl) {
314                 if (!(s->flags & SS_COMPLETE))
315                         continue;
316                 num_complete++;
317                 x += s->completion_time - s->creation_time;
318         }
319         assert(x >= 0);
320
321         ret = now;
322         if (num_complete == 0)
323                 goto out;
324         x /= num_complete; /* avg time to create one snapshot */
325         if (unit_interval < x * wanted) /* oops, no sleep at all */
326                 goto out;
327         ret = s->completion_time + unit_interval / wanted - x;
328 out:
329         free_snapshot_list(&sl);
330         return ret;
331 }
332
333 static inline void invalidate_next_snapshot_time(void)
334 {
335         next_snapshot_time = 0;
336 }
337
338 static inline int next_snapshot_time_is_valid(void)
339 {
340         return next_snapshot_time != 0;
341 }
342
343 static int next_snapshot_is_due(void)
344 {
345         int64_t now = get_current_time();
346
347         if (!next_snapshot_time_is_valid())
348                 next_snapshot_time = compute_next_snapshot_time();
349         if (next_snapshot_time <= now) {
350                 DSS_DEBUG_LOG(("next snapshot: now\n"));
351                 return 1;
352         }
353         DSS_DEBUG_LOG(("next snapshot due in %" PRId64 " seconds\n",
354                 next_snapshot_time - now));
355         return 0;
356 }
357
358 static void pre_create_hook(void)
359 {
360         assert(snapshot_creation_status == HS_READY);
361         /* make sure that the next snapshot time will be recomputed */
362         invalidate_next_snapshot_time();
363         DSS_DEBUG_LOG(("executing %s\n", conf.pre_create_hook_arg));
364         dss_exec_cmdline_pid(&create_pid, conf.pre_create_hook_arg);
365         snapshot_creation_status = HS_PRE_RUNNING;
366 }
367
368 static void pre_remove_hook(struct snapshot *s, const char *why)
369 {
370         char *cmd;
371
372         if (!s)
373                 return;
374         DSS_DEBUG_LOG(("%s snapshot %s\n", why, s->name));
375         assert(snapshot_removal_status == HS_READY);
376         assert(remove_pid == 0);
377         assert(!snapshot_currently_being_removed);
378
379         snapshot_currently_being_removed = dss_malloc(sizeof(struct snapshot));
380         *snapshot_currently_being_removed = *s;
381         snapshot_currently_being_removed->name = dss_strdup(s->name);
382
383         cmd = make_message("%s %s/%s", conf.pre_remove_hook_arg,
384                 conf.dest_dir_arg, s->name);
385         DSS_DEBUG_LOG(("executing %s\n", cmd));
386         dss_exec_cmdline_pid(&remove_pid, cmd);
387         free(cmd);
388         snapshot_removal_status = HS_PRE_RUNNING;
389 }
390
391 static int exec_rm(void)
392 {
393         struct snapshot *s = snapshot_currently_being_removed;
394         char *new_name = being_deleted_name(s);
395         char *argv[4];
396         int ret;
397
398         argv[0] = "rm";
399         argv[1] = "-rf";
400         argv[2] = new_name;
401         argv[3] = NULL;
402
403         assert(snapshot_removal_status == HS_PRE_SUCCESS);
404         assert(remove_pid == 0);
405
406         DSS_NOTICE_LOG(("removing %s (interval = %i)\n", s->name, s->interval));
407         ret = dss_rename(s->name, new_name);
408         if (ret < 0)
409                 goto out;
410         dss_exec(&remove_pid, argv[0], argv);
411         snapshot_removal_status = HS_RUNNING;
412 out:
413         free(new_name);
414         return ret;
415 }
416
417 static int snapshot_is_being_created(struct snapshot *s)
418 {
419         return s->creation_time == current_snapshot_creation_time;
420 }
421
422 static struct snapshot *find_orphaned_snapshot(struct snapshot_list *sl)
423 {
424         struct snapshot *s;
425         int i;
426
427         DSS_DEBUG_LOG(("looking for orphaned snapshots\n"));
428         FOR_EACH_SNAPSHOT(s, i, sl) {
429                 if (snapshot_is_being_created(s))
430                         continue;
431                 /*
432                  * We know that no rm is currently running, so if s is marked
433                  * as being deleted, a previously started rm must have failed.
434                  */
435                 if (s->flags & SS_BEING_DELETED)
436                         return s;
437
438                 if (s->flags & SS_COMPLETE) /* good snapshot */
439                         continue;
440                 /*
441                  * This snapshot is incomplete and it is not the snapshot
442                  * currently being created. However, we must not remove it if
443                  * rsync is about to be restarted. As only the newest snapshot
444                  * can be restarted, this snapshot is orphaned if it is not the
445                  * newest snapshot or if we are not about to restart rsync.
446                  */
447                 if (get_newest_snapshot(sl) != s)
448                         return s;
449                 if (snapshot_creation_status != HS_NEEDS_RESTART)
450                         return s;
451         }
452         /* no orphaned snapshots */
453         return NULL;
454 }
455
456 static int is_reference_snapshot(struct snapshot *s)
457 {
458         if (!name_of_reference_snapshot)
459                 return 0;
460         return strcmp(s->name, name_of_reference_snapshot)? 0 : 1;
461 }
462
463 /*
464  * return: 0: no redundant snapshots, 1: rm process started, negative: error
465  */
466 static struct snapshot *find_redundant_snapshot(struct snapshot_list *sl)
467 {
468         int i, interval;
469         struct snapshot *s;
470         unsigned missing = 0;
471
472         DSS_DEBUG_LOG(("looking for intervals containing too many snapshots\n"));
473         for (interval = conf.num_intervals_arg - 1; interval >= 0; interval--) {
474                 unsigned keep = desired_number_of_snapshots(interval, conf.num_intervals_arg);
475                 unsigned num = sl->interval_count[interval];
476                 struct snapshot *victim = NULL, *prev = NULL;
477                 int64_t score = LONG_MAX;
478
479                 if (keep >= num)
480                         missing += keep - num;
481                 if (keep + missing >= num)
482                         continue;
483                 /* redundant snapshot in this interval, pick snapshot with lowest score */
484                 FOR_EACH_SNAPSHOT(s, i, sl) {
485                         int64_t this_score;
486
487                         if (snapshot_is_being_created(s))
488                                 continue;
489                         if (is_reference_snapshot(s))
490                                 continue;
491                         if (s->interval > interval) {
492                                 prev = s;
493                                 continue;
494                         }
495                         if (s->interval < interval)
496                                 break;
497                         if (!victim) {
498                                 victim = s;
499                                 prev = s;
500                                 continue;
501                         }
502                         assert(prev);
503                         /* check if s is a better victim */
504                         this_score = s->creation_time - prev->creation_time;
505                         assert(this_score >= 0);
506                         if (this_score < score) {
507                                 score = this_score;
508                                 victim = s;
509                         }
510                         prev = s;
511                 }
512                 assert(victim);
513                 return victim;
514         }
515         return NULL;
516 }
517
518 static struct snapshot *find_outdated_snapshot(struct snapshot_list *sl)
519 {
520         int i;
521         struct snapshot *s;
522
523         DSS_DEBUG_LOG(("looking for snapshots belonging to intervals >= %d\n",
524                 conf.num_intervals_arg));
525         FOR_EACH_SNAPSHOT(s, i, sl) {
526                 if (snapshot_is_being_created(s))
527                         continue;
528                 if (is_reference_snapshot(s))
529                         continue;
530                 if (s->interval < conf.num_intervals_arg)
531                         continue;
532                 return s;
533         }
534         return NULL;
535 }
536
537 static struct snapshot *find_oldest_removable_snapshot(struct snapshot_list *sl)
538 {
539         int i, num_complete;
540         struct snapshot *s, *ref = NULL;
541
542         num_complete = num_complete_snapshots(sl);
543         if (num_complete <= conf.min_complete_arg)
544                 return NULL;
545         FOR_EACH_SNAPSHOT(s, i, sl) {
546                 if (snapshot_is_being_created(s))
547                         continue;
548                 if (is_reference_snapshot(s)) { /* avoid this one */
549                         ref = s;
550                         continue;
551                 }
552                 DSS_INFO_LOG(("oldest removable snapshot: %s\n", s->name));
553                 return s;
554         }
555         assert(ref);
556         DSS_WARNING_LOG(("removing reference snapshot %s\n", ref->name));
557         return ref;
558 }
559
560 static int rename_incomplete_snapshot(int64_t start)
561 {
562         char *old_name;
563         int ret;
564         int64_t now;
565
566         /*
567          * We don't want the dss_rename() below to fail with EEXIST because the
568          * last complete snapshot was created (and completed) in the same
569          * second as this one.
570          */
571         while ((now = get_current_time()) == start)
572                 sleep(1);
573         free(path_to_last_complete_snapshot);
574         ret = complete_name(start, now, &path_to_last_complete_snapshot);
575         if (ret < 0)
576                 return ret;
577         old_name = incomplete_name(start);
578         ret = dss_rename(old_name, path_to_last_complete_snapshot);
579         if (ret >= 0)
580                 DSS_NOTICE_LOG(("%s -> %s\n", old_name,
581                         path_to_last_complete_snapshot));
582         free(old_name);
583         return ret;
584 }
585
586 static int try_to_free_disk_space(void)
587 {
588         int ret;
589         struct snapshot_list sl;
590         struct snapshot *victim;
591         struct timeval now;
592         const char *why;
593         int low_disk_space;
594
595         ret = disk_space_low(NULL);
596         if (ret < 0)
597                 return ret;
598         low_disk_space = ret;
599         gettimeofday(&now, NULL);
600         if (tv_diff(&next_removal_check, &now, NULL) > 0)
601                 return 0;
602         if (!low_disk_space) {
603                 if (conf.keep_redundant_given)
604                         return 0;
605                 if (snapshot_creation_status != HS_READY)
606                         return 0;
607                 if (next_snapshot_is_due())
608                         return 0;
609         }
610         /*
611          * Idle and --keep_redundant not given, or low disk space. Look at
612          * existing snapshots.
613          */
614         dss_get_snapshot_list(&sl);
615         ret = 0;
616         /*
617          * Don't remove anything if there is free space and we have fewer
618          * snapshots than configured, plus one. This way there is always one
619          * snapshot that can be recycled.
620          */
621         if (!low_disk_space && sl.num_snapshots <= 1 << conf.num_intervals_arg)
622                 goto out;
623         why = "outdated";
624         victim = find_outdated_snapshot(&sl);
625         if (victim)
626                 goto remove;
627         why = "redundant";
628         victim = find_redundant_snapshot(&sl);
629         if (victim)
630                 goto remove;
631         why = "orphaned";
632         victim = find_orphaned_snapshot(&sl);
633         if (victim)
634                 goto remove;
635         /* try harder only if disk space is low */
636         if (!low_disk_space)
637                 goto out;
638         DSS_WARNING_LOG(("disk space low and nothing obvious to remove\n"));
639         victim = find_oldest_removable_snapshot(&sl);
640         if (victim)
641                 goto remove;
642         DSS_CRIT_LOG(("uhuhu: disk space low and nothing to remove\n"));
643         ret = -ERRNO_TO_DSS_ERROR(ENOSPC);
644         goto out;
645 remove:
646         pre_remove_hook(victim, why);
647 out:
648         free_snapshot_list(&sl);
649         return ret;
650 }
651
652 static void post_create_hook(void)
653 {
654         char *cmd = make_message("%s %s/%s", conf.post_create_hook_arg,
655                 conf.dest_dir_arg, path_to_last_complete_snapshot);
656         DSS_NOTICE_LOG(("executing %s\n", cmd));
657         dss_exec_cmdline_pid(&create_pid, cmd);
658         free(cmd);
659         snapshot_creation_status = HS_POST_RUNNING;
660 }
661
662 static void post_remove_hook(void)
663 {
664         char *cmd;
665         struct snapshot *s = snapshot_currently_being_removed;
666
667         assert(s);
668
669         cmd = make_message("%s %s/%s", conf.post_remove_hook_arg,
670                 conf.dest_dir_arg, s->name);
671         DSS_NOTICE_LOG(("executing %s\n", cmd));
672         dss_exec_cmdline_pid(&remove_pid, cmd);
673         free(cmd);
674         snapshot_removal_status = HS_POST_RUNNING;
675 }
676
677 static void dss_kill(pid_t pid, int sig, const char *msg)
678 {
679         const char *signame, *process_name;
680
681         if (pid == 0)
682                 return;
683         switch (sig) {
684         case SIGTERM: signame = "TERM"; break;
685         case SIGSTOP: signame = "STOP"; break;
686         case SIGCONT: signame = "CONT"; break;
687         default: signame = "????";
688         }
689
690         if (pid == create_pid)
691                 process_name = "create";
692         else if (pid == remove_pid)
693                 process_name = "remove";
694         else process_name = "??????";
695
696         if (msg)
697                 DSS_INFO_LOG(("%s\n", msg));
698         DSS_DEBUG_LOG(("sending signal %d (%s) to pid %d (%s process)\n",
699                 sig, signame, (int)pid, process_name));
700         if (kill(pid, sig) >= 0)
701                 return;
702         DSS_INFO_LOG(("failed to send signal %d (%s) to pid %d (%s process)\n",
703                 sig, signame, (int)pid, process_name));
704 }
705
706 static void stop_create_process(void)
707 {
708         if (create_process_stopped)
709                 return;
710         dss_kill(create_pid, SIGSTOP, "suspending create process");
711         create_process_stopped = 1;
712 }
713
714 static void restart_create_process(void)
715 {
716         if (!create_process_stopped)
717                 return;
718         dss_kill(create_pid, SIGCONT, "resuming create process");
719         create_process_stopped = 0;
720 }
721
722 /**
723  * Print a log message about the exit status of a child.
724  */
725 static void log_termination_msg(pid_t pid, int status)
726 {
727         if (WIFEXITED(status))
728                 DSS_INFO_LOG(("child %i exited. Exit status: %i\n", (int)pid,
729                         WEXITSTATUS(status)));
730         else if (WIFSIGNALED(status))
731                 DSS_NOTICE_LOG(("child %i was killed by signal %i\n", (int)pid,
732                         WTERMSIG(status)));
733         else
734                 DSS_WARNING_LOG(("child %i terminated abormally\n", (int)pid));
735 }
736
737 static int wait_for_process(pid_t pid, int *status)
738 {
739         int ret;
740
741         DSS_DEBUG_LOG(("Waiting for process %d to terminate\n", (int)pid));
742         for (;;) {
743                 fd_set rfds;
744
745                 FD_ZERO(&rfds);
746                 FD_SET(signal_pipe, &rfds);
747                 ret = dss_select(signal_pipe + 1, &rfds, NULL, NULL);
748                 if (ret < 0)
749                         break;
750                 ret = next_signal();
751                 if (!ret)
752                         continue;
753                 if (ret == SIGCHLD) {
754                         ret = waitpid(pid, status, 0);
755                         if (ret >= 0)
756                                 break;
757                         if (errno != EINTR) { /* error */
758                                 ret = -ERRNO_TO_DSS_ERROR(errno);
759                                 break;
760                         }
761                 }
762                 /* SIGINT or SIGTERM */
763                 dss_kill(pid, SIGTERM, "killing child process");
764         }
765         if (ret < 0)
766                 DSS_ERROR_LOG(("failed to wait for process %d\n", (int)pid));
767         else
768                 log_termination_msg(pid, *status);
769         return ret;
770 }
771
772 static void handle_pre_remove_exit(int status)
773 {
774         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
775                 snapshot_removal_status = HS_READY;
776                 gettimeofday(&next_removal_check, NULL);
777                 next_removal_check.tv_sec += 60;
778                 return;
779         }
780         snapshot_removal_status = HS_PRE_SUCCESS;
781 }
782
783 static int handle_rm_exit(int status)
784 {
785         if (!WIFEXITED(status)) {
786                 snapshot_removal_status = HS_READY;
787                 return -E_INVOLUNTARY_EXIT;
788         }
789         if (WEXITSTATUS(status)) {
790                 snapshot_removal_status = HS_READY;
791                 return -E_BAD_EXIT_CODE;
792         }
793         snapshot_removal_status = HS_SUCCESS;
794         return 1;
795 }
796
797 static void handle_post_remove_exit(void)
798 {
799         snapshot_removal_status = HS_READY;
800 }
801
802 static int handle_remove_exit(int status)
803 {
804         int ret;
805         struct snapshot *s = snapshot_currently_being_removed;
806
807         assert(s);
808         switch (snapshot_removal_status) {
809         case HS_PRE_RUNNING:
810                 handle_pre_remove_exit(status);
811                 ret = 1;
812                 break;
813         case HS_RUNNING:
814                 ret = handle_rm_exit(status);
815                 break;
816         case HS_POST_RUNNING:
817                 handle_post_remove_exit();
818                 ret = 1;
819                 break;
820         default:
821                 ret = -E_BUG;
822         }
823         if (snapshot_removal_status == HS_READY) {
824                 free(s->name);
825                 free(s);
826                 snapshot_currently_being_removed = NULL;
827         }
828         remove_pid = 0;
829         return ret;
830 }
831
832 static int wait_for_remove_process(void)
833 {
834         int status, ret;
835
836         assert(remove_pid);
837         assert(
838                 snapshot_removal_status == HS_PRE_RUNNING ||
839                 snapshot_removal_status == HS_RUNNING ||
840                 snapshot_removal_status == HS_POST_RUNNING
841         );
842         ret = wait_for_process(remove_pid, &status);
843         if (ret < 0)
844                 return ret;
845         return handle_remove_exit(status);
846 }
847
848 static int handle_rsync_exit(int status)
849 {
850         int es, ret;
851
852         if (!WIFEXITED(status)) {
853                 DSS_ERROR_LOG(("rsync process %d died involuntary\n", (int)create_pid));
854                 ret = -E_INVOLUNTARY_EXIT;
855                 snapshot_creation_status = HS_READY;
856                 goto out;
857         }
858         es = WEXITSTATUS(status);
859         /*
860          * Restart rsync on non-fatal errors:
861          * 24: Partial transfer due to vanished source files
862          */
863         if (es != 0 && es != 24) {
864                 DSS_WARNING_LOG(("rsync exit code %d, error count %d\n",
865                         es, ++num_consecutive_rsync_errors));
866                 if (conf.create_given) {
867                         ret = -E_BAD_EXIT_CODE;
868                         goto out;
869                 }
870                 if (num_consecutive_rsync_errors > conf.max_rsync_errors_arg) {
871                         ret = -E_TOO_MANY_RSYNC_ERRORS;
872                         snapshot_creation_status = HS_READY;
873                         goto out;
874                 }
875                 DSS_WARNING_LOG(("restarting rsync process\n"));
876                 snapshot_creation_status = HS_NEEDS_RESTART;
877                 next_snapshot_time = get_current_time() + 60;
878                 ret = 1;
879                 goto out;
880         }
881         num_consecutive_rsync_errors = 0;
882         ret = rename_incomplete_snapshot(current_snapshot_creation_time);
883         if (ret < 0)
884                 goto out;
885         snapshot_creation_status = HS_SUCCESS;
886         free(name_of_reference_snapshot);
887         name_of_reference_snapshot = NULL;
888 out:
889         create_process_stopped = 0;
890         return ret;
891 }
892
893 static int handle_pre_create_hook_exit(int status)
894 {
895         int es, ret;
896         static int warn_count;
897
898         if (!WIFEXITED(status)) {
899                 snapshot_creation_status = HS_READY;
900                 ret = -E_INVOLUNTARY_EXIT;
901                 goto out;
902         }
903         es = WEXITSTATUS(status);
904         if (es) {
905                 if (!warn_count--) {
906                         DSS_NOTICE_LOG(("pre_create_hook %s returned %d\n",
907                                 conf.pre_create_hook_arg, es));
908                         DSS_NOTICE_LOG(("deferring snapshot creation...\n"));
909                         warn_count = 60; /* warn only once per hour */
910                 }
911                 next_snapshot_time = get_current_time() + 60;
912                 snapshot_creation_status = HS_READY;
913                 ret = 0;
914                 goto out;
915         }
916         warn_count = 0;
917         snapshot_creation_status = HS_PRE_SUCCESS;
918         ret = 1;
919 out:
920         return ret;
921 }
922
923 static int handle_sigchld(void)
924 {
925         pid_t pid;
926         int status, ret = reap_child(&pid, &status);
927
928         if (ret <= 0)
929                 return ret;
930
931         if (pid == create_pid) {
932                 switch (snapshot_creation_status) {
933                 case HS_PRE_RUNNING:
934                         ret = handle_pre_create_hook_exit(status);
935                         break;
936                 case HS_RUNNING:
937                         ret = handle_rsync_exit(status);
938                         break;
939                 case HS_POST_RUNNING:
940                         snapshot_creation_status = HS_READY;
941                         ret = 1;
942                         break;
943                 default:
944                         DSS_EMERG_LOG(("BUG: create can't die in status %d\n",
945                                 snapshot_creation_status));
946                         return -E_BUG;
947                 }
948                 create_pid = 0;
949                 return ret;
950         }
951         if (pid == remove_pid) {
952                 ret = handle_remove_exit(status);
953                 if (ret < 0)
954                         return ret;
955                 return ret;
956         }
957         DSS_EMERG_LOG(("BUG: unknown process %d died\n", (int)pid));
958         return -E_BUG;
959 }
960
961 static int check_config(void)
962 {
963         if (conf.unit_interval_arg <= 0) {
964                 DSS_ERROR_LOG(("bad unit interval: %i\n", conf.unit_interval_arg));
965                 return -E_INVALID_NUMBER;
966         }
967         DSS_DEBUG_LOG(("unit interval: %i day(s)\n", conf.unit_interval_arg));
968         if (conf.num_intervals_arg <= 0 || conf.num_intervals_arg > 30) {
969                 DSS_ERROR_LOG(("bad number of intervals: %i\n",
970                         conf.num_intervals_arg));
971                 return -E_INVALID_NUMBER;
972         }
973         DSS_DEBUG_LOG(("number of intervals: %i\n", conf.num_intervals_arg));
974         return 1;
975 }
976
977 /*
978  * Returns < 0 on errors, 0 if no config file is given and > 0 if the config
979  * file was read successfully.
980  */
981 static int parse_config_file(bool sighup)
982 {
983         int ret, config_file_exists;
984         char *config_file = get_config_file_name();
985         struct stat statbuf;
986         char *old_logfile_arg = NULL;
987         int old_daemon_given = 0;
988
989         if (sighup) {
990                 if (conf.logfile_given)
991                         old_logfile_arg = dss_strdup(conf.logfile_arg);
992                 old_daemon_given = conf.daemon_given;
993         }
994
995         config_file_exists = !stat(config_file, &statbuf);
996         if (!config_file_exists && conf.config_file_given) {
997                 ret = -ERRNO_TO_DSS_ERROR(errno);
998                 DSS_ERROR_LOG(("failed to stat config file %s\n", config_file));
999                 goto out;
1000         }
1001         if (config_file_exists) {
1002                 struct cmdline_parser_params params;
1003                 params.override = sighup;
1004                 params.initialize = 0;
1005                 params.check_required = 1;
1006                 params.check_ambiguity = 0;
1007                 params.print_errors = 1;
1008                 if (sighup) { /* invalidate all rsync options */
1009                         int i;
1010
1011                         for (i = 0; i < conf.rsync_option_given; i++) {
1012                                 free(conf.rsync_option_arg[i]);
1013                                 conf.rsync_option_arg[i] = NULL;
1014                         }
1015                         conf.rsync_option_given = 0;
1016                 }
1017                 cmdline_parser_config_file(config_file, &conf, &params);
1018         }
1019         ret = check_config();
1020         if (ret < 0)
1021                 goto out;
1022         if (sighup) {
1023                 /* don't change daemon mode on SIGHUP */
1024                 conf.daemon_given = old_daemon_given;
1025                 close_log(logfile);
1026                 logfile = NULL;
1027                 if (conf.logfile_given)
1028                         free(old_logfile_arg);
1029                 else if (conf.daemon_given) { /* re-use old logfile */
1030                         conf.logfile_arg = old_logfile_arg;
1031                         conf.logfile_given = 1;
1032                 }
1033         }
1034         if (conf.logfile_given && conf.run_given && conf.daemon_given) {
1035                 logfile = open_log(conf.logfile_arg);
1036                 log_welcome(conf.loglevel_arg);
1037         }
1038         DSS_DEBUG_LOG(("loglevel: %d\n", conf.loglevel_arg));
1039         ret = config_file_exists;
1040 out:
1041         free(config_file);
1042         if (ret < 0)
1043                 DSS_EMERG_LOG(("%s\n", dss_strerror(-ret)));
1044         return ret;
1045 }
1046
1047 static int change_to_dest_dir(void)
1048 {
1049         DSS_INFO_LOG(("changing cwd to %s\n", conf.dest_dir_arg));
1050         return dss_chdir(conf.dest_dir_arg);
1051 }
1052
1053 static int handle_sighup(void)
1054 {
1055         int ret;
1056
1057         DSS_NOTICE_LOG(("SIGHUP, re-reading config\n"));
1058         dump_dss_config("old");
1059         ret = parse_config_file(true /* SIGHUP */);
1060         if (ret < 0)
1061                 return ret;
1062         dump_dss_config("reloaded");
1063         invalidate_next_snapshot_time();
1064         return change_to_dest_dir();
1065 }
1066
1067 static void kill_children(void)
1068 {
1069         restart_create_process();
1070         dss_kill(create_pid, SIGTERM, NULL);
1071         dss_kill(remove_pid, SIGTERM, NULL);
1072 }
1073
1074 static int handle_signal(void)
1075 {
1076         int sig, ret = next_signal();
1077
1078         if (ret <= 0)
1079                 goto out;
1080         sig = ret;
1081         switch (sig) {
1082         case SIGINT:
1083         case SIGTERM:
1084                 kill_children();
1085                 ret = -E_SIGNAL;
1086                 break;
1087         case SIGHUP:
1088                 ret = handle_sighup();
1089                 break;
1090         case SIGCHLD:
1091                 ret = handle_sigchld();
1092                 break;
1093         }
1094 out:
1095         if (ret < 0)
1096                 DSS_ERROR_LOG(("%s\n", dss_strerror(-ret)));
1097         return ret;
1098 }
1099
1100 /*
1101  * We can not use rsync locally if the local user is different from the remote
1102  * user or if the src dir is not on the local host (or both).
1103  */
1104 static int use_rsync_locally(char *logname)
1105 {
1106         char *h = conf.remote_host_arg;
1107
1108         if (strcmp(h, "localhost") && strcmp(h, "127.0.0.1"))
1109                 return 0;
1110         if (conf.remote_user_given && strcmp(conf.remote_user_arg, logname))
1111                 return 0;
1112         return 1;
1113 }
1114
1115 static int rename_resume_snap(int64_t creation_time)
1116 {
1117         struct snapshot_list sl;
1118         struct snapshot *s = NULL;
1119         char *new_name = incomplete_name(creation_time);
1120         int ret;
1121         const char *why;
1122
1123         sl.num_snapshots = 0;
1124
1125         ret = 0;
1126         if (conf.no_resume_given)
1127                 goto out;
1128         dss_get_snapshot_list(&sl);
1129         /*
1130          * Snapshot recycling: We first look at the newest snapshot. If this
1131          * snapshot happens to be incomplete, the last rsync process was
1132          * aborted and we reuse this one. Otherwise we look at snapshots which
1133          * could be removed (outdated and redundant snapshots) as candidates
1134          * for recycling. If no outdated/redundant snapshot exists, we check if
1135          * there is an orphaned snapshot, which likely is useless anyway.
1136          *
1137          * Only if no existing snapshot is suitable for recycling, we bite the
1138          * bullet and create a new one.
1139          */
1140         s = get_newest_snapshot(&sl);
1141         if (!s) /* no snapshots at all */
1142                 goto out;
1143         /* re-use last snapshot if it is incomplete */
1144         why = "aborted";
1145         if ((s->flags & SS_COMPLETE) == 0)
1146                 goto out;
1147         why = "outdated";
1148         s = find_outdated_snapshot(&sl);
1149         if (s)
1150                 goto out;
1151         why = "redundant";
1152         s = find_redundant_snapshot(&sl);
1153         if (s)
1154                 goto out;
1155         why = "orphaned";
1156         s = find_orphaned_snapshot(&sl);
1157 out:
1158         if (s) {
1159                 DSS_INFO_LOG(("reusing %s snapshot %s\n", why, s->name));
1160                 ret = dss_rename(s->name, new_name);
1161         }
1162         if (ret >= 0)
1163                 DSS_NOTICE_LOG(("creating new snapshot %s\n", new_name));
1164         free(new_name);
1165         free_snapshot_list(&sl);
1166         return ret;
1167 }
1168
1169 static void create_rsync_argv(char ***argv, int64_t *num)
1170 {
1171         char *logname;
1172         int i = 0, j;
1173         struct snapshot_list sl;
1174
1175         dss_get_snapshot_list(&sl);
1176         assert(!name_of_reference_snapshot);
1177         name_of_reference_snapshot = name_of_newest_complete_snapshot(&sl);
1178         free_snapshot_list(&sl);
1179
1180         *argv = dss_malloc((15 + conf.rsync_option_given) * sizeof(char *));
1181         (*argv)[i++] = dss_strdup("rsync");
1182         (*argv)[i++] = dss_strdup("-a");
1183         (*argv)[i++] = dss_strdup("--delete");
1184         for (j = 0; j < conf.rsync_option_given; j++)
1185                 (*argv)[i++] = dss_strdup(conf.rsync_option_arg[j]);
1186         if (name_of_reference_snapshot) {
1187                 DSS_INFO_LOG(("using %s as reference\n", name_of_reference_snapshot));
1188                 (*argv)[i++] = make_message("--link-dest=../%s",
1189                         name_of_reference_snapshot);
1190         } else
1191                 DSS_INFO_LOG(("no suitable reference snapshot found\n"));
1192         logname = dss_logname();
1193         if (use_rsync_locally(logname))
1194                 (*argv)[i++] = dss_strdup(conf.source_dir_arg);
1195         else
1196                 (*argv)[i++] = make_message("%s@%s:%s/", conf.remote_user_given?
1197                         conf.remote_user_arg : logname,
1198                         conf.remote_host_arg, conf.source_dir_arg);
1199         free(logname);
1200         *num = get_current_time();
1201         (*argv)[i++] = incomplete_name(*num);
1202         (*argv)[i++] = NULL;
1203         for (j = 0; j < i; j++)
1204                 DSS_DEBUG_LOG(("argv[%d] = %s\n", j, (*argv)[j]));
1205 }
1206
1207 static void free_rsync_argv(char **argv)
1208 {
1209         int i;
1210
1211         if (!argv)
1212                 return;
1213         for (i = 0; argv[i]; i++)
1214                 free(argv[i]);
1215         free(argv);
1216 }
1217
1218 static int create_snapshot(char **argv)
1219 {
1220         int ret;
1221
1222         ret = rename_resume_snap(current_snapshot_creation_time);
1223         if (ret < 0)
1224                 return ret;
1225         dss_exec(&create_pid, argv[0], argv);
1226         snapshot_creation_status = HS_RUNNING;
1227         return ret;
1228 }
1229
1230 static int select_loop(void)
1231 {
1232         int ret;
1233         /* check every 60 seconds for free disk space */
1234         struct timeval tv;
1235         char **rsync_argv = NULL;
1236
1237         for (;;) {
1238                 fd_set rfds;
1239                 struct timeval *tvp;
1240
1241                 if (remove_pid)
1242                         tvp = NULL; /* sleep until rm hook/process dies */
1243                 else { /* sleep one minute */
1244                         tv.tv_sec = 60;
1245                         tv.tv_usec = 0;
1246                         tvp = &tv;
1247                 }
1248                 FD_ZERO(&rfds);
1249                 FD_SET(signal_pipe, &rfds);
1250                 ret = dss_select(signal_pipe + 1, &rfds, NULL, tvp);
1251                 if (ret < 0)
1252                         goto out;
1253                 if (FD_ISSET(signal_pipe, &rfds)) {
1254                         ret = handle_signal();
1255                         if (ret < 0)
1256                                 goto out;
1257                 }
1258                 if (remove_pid)
1259                         continue;
1260                 if (snapshot_removal_status == HS_PRE_SUCCESS) {
1261                         ret = exec_rm();
1262                         if (ret < 0)
1263                                 goto out;
1264                         continue;
1265                 }
1266                 if (snapshot_removal_status == HS_SUCCESS) {
1267                         post_remove_hook();
1268                         continue;
1269                 }
1270                 ret = try_to_free_disk_space();
1271                 if (ret < 0)
1272                         goto out;
1273                 if (snapshot_removal_status != HS_READY) {
1274                         stop_create_process();
1275                         continue;
1276                 }
1277                 restart_create_process();
1278                 switch (snapshot_creation_status) {
1279                 case HS_READY:
1280                         if (!next_snapshot_is_due())
1281                                 continue;
1282                         pre_create_hook();
1283                         continue;
1284                 case HS_PRE_RUNNING:
1285                 case HS_RUNNING:
1286                 case HS_POST_RUNNING:
1287                         continue;
1288                 case HS_PRE_SUCCESS:
1289                         if (!name_of_reference_snapshot) {
1290                                 free_rsync_argv(rsync_argv);
1291                                 create_rsync_argv(&rsync_argv, &current_snapshot_creation_time);
1292                         }
1293                         ret = create_snapshot(rsync_argv);
1294                         if (ret < 0)
1295                                 goto out;
1296                         continue;
1297                 case HS_NEEDS_RESTART:
1298                         if (!next_snapshot_is_due())
1299                                 continue;
1300                         ret = create_snapshot(rsync_argv);
1301                         if (ret < 0)
1302                                 goto out;
1303                         continue;
1304                 case HS_SUCCESS:
1305                         post_create_hook();
1306                         continue;
1307                 }
1308         }
1309 out:
1310         return ret;
1311 }
1312
1313 static void exit_hook(int exit_code)
1314 {
1315         char *argv[3];
1316         pid_t pid;
1317
1318         argv[0] = conf.exit_hook_arg;
1319         argv[1] = dss_strerror(-exit_code);
1320         argv[2] = NULL;
1321
1322         DSS_NOTICE_LOG(("executing %s %s\n", argv[0], argv[1]));
1323         dss_exec(&pid, conf.exit_hook_arg, argv);
1324 }
1325
1326 static void lock_dss_or_die(void)
1327 {
1328         char *config_file = get_config_file_name();
1329         int ret = lock_dss(config_file);
1330
1331         free(config_file);
1332         if (ret < 0) {
1333                 DSS_EMERG_LOG(("failed to lock: %s\n", dss_strerror(-ret)));
1334                 exit(EXIT_FAILURE);
1335         }
1336 }
1337
1338 static int com_run(void)
1339 {
1340         int ret;
1341
1342         lock_dss_or_die();
1343         if (conf.dry_run_given) {
1344                 DSS_ERROR_LOG(("dry_run not supported by this command\n"));
1345                 return -E_SYNTAX;
1346         }
1347         ret = install_sighandler(SIGHUP);
1348         if (ret < 0)
1349                 return ret;
1350         ret = select_loop();
1351         if (ret >= 0) /* impossible */
1352                 ret = -E_BUG;
1353         kill_children();
1354         exit_hook(ret);
1355         return ret;
1356 }
1357
1358 static int com_prune(void)
1359 {
1360         int ret;
1361         struct snapshot_list sl;
1362         struct snapshot *victim;
1363         struct disk_space ds;
1364         const char *why;
1365
1366         lock_dss_or_die();
1367         ret = get_disk_space(".", &ds);
1368         if (ret < 0)
1369                 return ret;
1370         log_disk_space(&ds);
1371         dss_get_snapshot_list(&sl);
1372         why = "outdated";
1373         victim = find_outdated_snapshot(&sl);
1374         if (victim)
1375                 goto rm;
1376         why = "redundant";
1377         victim = find_redundant_snapshot(&sl);
1378         if (victim)
1379                 goto rm;
1380         ret = 0;
1381         goto out;
1382 rm:
1383         if (conf.dry_run_given) {
1384                 dss_msg("%s snapshot %s (interval = %i)\n",
1385                         why, victim->name, victim->interval);
1386                 ret = 0;
1387                 goto out;
1388         }
1389         pre_remove_hook(victim, why);
1390         if (snapshot_removal_status == HS_PRE_RUNNING) {
1391                 ret = wait_for_remove_process();
1392                 if (ret < 0)
1393                         goto out;
1394                 if (snapshot_removal_status != HS_PRE_SUCCESS)
1395                         goto out;
1396         }
1397         ret = exec_rm();
1398         if (ret < 0)
1399                 goto out;
1400         ret = wait_for_remove_process();
1401         if (ret < 0)
1402                 goto out;
1403         if (snapshot_removal_status != HS_SUCCESS)
1404                 goto out;
1405         post_remove_hook();
1406         if (snapshot_removal_status != HS_POST_RUNNING)
1407                 goto out;
1408         ret = wait_for_remove_process();
1409         if (ret < 0)
1410                 goto out;
1411         ret = 1;
1412 out:
1413         free_snapshot_list(&sl);
1414         return ret;
1415 }
1416
1417 static int com_create(void)
1418 {
1419         int ret, status;
1420         char **rsync_argv;
1421
1422         lock_dss_or_die();
1423         if (conf.dry_run_given) {
1424                 int i;
1425                 char *msg = NULL;
1426                 create_rsync_argv(&rsync_argv, &current_snapshot_creation_time);
1427                 for (i = 0; rsync_argv[i]; i++) {
1428                         char *tmp = msg;
1429                         msg = make_message("%s%s%s", tmp? tmp : "",
1430                                 tmp? " " : "", rsync_argv[i]);
1431                         free(tmp);
1432                 }
1433                 free_rsync_argv(rsync_argv);
1434                 dss_msg("%s\n", msg);
1435                 free(msg);
1436                 return 1;
1437         }
1438         pre_create_hook();
1439         if (create_pid) {
1440                 ret = wait_for_process(create_pid, &status);
1441                 if (ret < 0)
1442                         return ret;
1443                 ret = handle_pre_create_hook_exit(status);
1444                 if (ret <= 0) /* error, or pre-create failed */
1445                         return ret;
1446         }
1447         create_rsync_argv(&rsync_argv, &current_snapshot_creation_time);
1448         ret = create_snapshot(rsync_argv);
1449         if (ret < 0)
1450                 goto out;
1451         ret = wait_for_process(create_pid, &status);
1452         if (ret < 0)
1453                 goto out;
1454         ret = handle_rsync_exit(status);
1455         if (ret < 0)
1456                 goto out;
1457         post_create_hook();
1458         if (create_pid)
1459                 ret = wait_for_process(create_pid, &status);
1460 out:
1461         free_rsync_argv(rsync_argv);
1462         return ret;
1463 }
1464
1465 static int com_ls(void)
1466 {
1467         int i;
1468         struct snapshot_list sl;
1469         struct snapshot *s;
1470
1471         dss_get_snapshot_list(&sl);
1472         FOR_EACH_SNAPSHOT(s, i, &sl) {
1473                 int64_t d = 0;
1474                 if (s->flags & SS_COMPLETE)
1475                         d = (s->completion_time - s->creation_time) / 60;
1476                 dss_msg("%u\t%s\t%3" PRId64 ":%02" PRId64 "\n", s->interval, s->name, d/60, d%60);
1477         }
1478         free_snapshot_list(&sl);
1479         return 1;
1480 }
1481
1482 static int setup_signal_handling(void)
1483 {
1484         int ret;
1485
1486         DSS_INFO_LOG(("setting up signal handlers\n"));
1487         signal_pipe = signal_init(); /* always successful */
1488         ret = install_sighandler(SIGINT);
1489         if (ret < 0)
1490                 return ret;
1491         ret = install_sighandler(SIGTERM);
1492         if (ret < 0)
1493                 return ret;
1494         return install_sighandler(SIGCHLD);
1495 }
1496
1497 int main(int argc, char **argv)
1498 {
1499         int ret;
1500         struct cmdline_parser_params params;
1501
1502         params.override = 0;
1503         params.initialize = 1;
1504         params.check_required = 0;
1505         params.check_ambiguity = 0;
1506         params.print_errors = 1;
1507
1508         cmdline_parser_ext(argc, argv, &conf, &params); /* aborts on errors */
1509         ret = parse_config_file(0);
1510         ret = parse_config_file(false /* no SIGHUP */);
1511         if (ret < 0)
1512                 goto out;
1513         if (ret == 0) { /* no config file given */
1514                 /*
1515                  * Parse the command line options again, but this time check
1516                  * that all required options are given.
1517                  */
1518                 struct cmdline_parser_params params;
1519                 params.override = 1;
1520                 params.initialize = 1;
1521                 params.check_required = 1;
1522                 params.check_ambiguity = 1;
1523                 params.print_errors = 1;
1524                 cmdline_parser_ext(argc, argv, &conf, &params); /* aborts on errors */
1525         }
1526         if (conf.daemon_given)
1527                 daemon_init();
1528         ret = change_to_dest_dir();
1529         if (ret < 0)
1530                 goto out;
1531         dump_dss_config("startup");
1532         ret = setup_signal_handling();
1533         if (ret < 0)
1534                 goto out;
1535         ret = call_command_handler();
1536         signal_shutdown();
1537 out:
1538         if (ret < 0)
1539                 DSS_EMERG_LOG(("%s\n", dss_strerror(-ret)));
1540         exit(ret >= 0? EXIT_SUCCESS : EXIT_FAILURE);
1541 }