19 #include "gcc-compat.h"
32 struct gengetopt_args_info conf
;
33 char *dss_error_txt
= NULL
;
37 /** Process id of current rsync process. */
38 static pid_t rsync_pid
;
39 /** Whether the rsync process is currently stopped */
40 static int rsync_stopped
;
41 /** Process id of current rm process. */
43 /** When the next snapshot is due. */
44 struct timeval next_snapshot_time
;
45 /* Creation time of the snapshot currently being created. */
46 int64_t current_snapshot_creation_time
;
52 /* a litte cpp magic helps to DRY */
58 #define COMMAND(x) int com_ ##x(void);
61 #define COMMAND(x) if (conf.x ##_given) return com_ ##x();
62 int call_command_handler(void)
65 DSS_EMERG_LOG("BUG: did not find command handler\n");
72 * complete, not being deleted: 1204565370-1204565371.Sun_Mar_02_2008_14_33-Sun_Mar_02_2008_14_43
73 * complete, being deleted: 1204565370-1204565371.being_deleted
74 * incomplete, not being deleted: 1204565370-incomplete
75 * incomplete, being deleted: 1204565370-incomplete.being_deleted
77 enum snapshot_status_flags
{
84 int64_t creation_time
;
85 int64_t completion_time
;
86 enum snapshot_status_flags flags
;
91 * An edge snapshot is either the oldest one or the newest one.
93 * We need to find either of them occasionally: The create code
94 * needs to know the newest snapshot because that is the one
95 * used as the link destination dir. The pruning code needs to
96 * find the oldest one in case disk space becomes low.
98 struct edge_snapshot_data
{
100 struct snapshot snap
;
103 __printf_2_3
void dss_log(int ll
, const char* fmt
,...)
111 if (ll
< conf
.loglevel_arg
)
113 outfd
= logfile
? logfile
: stderr
;
116 strftime(str
, sizeof(str
), "%b %d %H:%M:%S", tm
);
117 fprintf(outfd
, "%s ", str
);
118 if (conf
.loglevel_arg
<= INFO
)
119 fprintf(outfd
, "%i: ", ll
);
121 vfprintf(outfd
, fmt
, argp
);
126 * Print a message either to stdout or to the log file.
128 __printf_1_2
void dss_msg(const char* fmt
,...)
130 FILE *outfd
= conf
.daemon_given
? logfile
: stdout
;
133 vfprintf(outfd
, fmt
, argp
);
138 * Return the desired number of snapshots of an interval.
140 unsigned num_snapshots(int interval
)
144 assert(interval
>= 0);
146 if (interval
>= conf
.num_intervals_arg
)
148 n
= conf
.num_intervals_arg
- interval
- 1;
152 int is_snapshot(const char *dirname
, int64_t now
, struct snapshot
*s
)
155 char *dash
, *dot
, *tmp
;
159 dash
= strchr(dirname
, '-');
160 if (!dash
|| !dash
[1] || dash
== dirname
)
162 for (i
= 0; dirname
[i
] != '-'; i
++)
163 if (!isdigit(dirname
[i
]))
165 tmp
= dss_strdup(dirname
);
167 ret
= dss_atoi64(tmp
, &num
);
176 s
->creation_time
= num
;
177 //DSS_DEBUG_LOG("%s start time: %lli\n", dirname, (long long)s->creation_time);
178 s
->interval
= (long long) ((now
- s
->creation_time
)
179 / conf
.unit_interval_arg
/ 24 / 3600);
180 if (!strcmp(dash
+ 1, "incomplete")) {
181 s
->completion_time
= -1;
182 s
->flags
= 0; /* neither complete, nor being deleted */
185 if (!strcmp(dash
+ 1, "incomplete.being_deleted")) {
186 s
->completion_time
= -1;
187 s
->flags
= SS_BEING_DELETED
; /* mot cpmplete, being deleted */
191 dot
= strchr(tmp
, '.');
192 if (!dot
|| !dot
[1] || dot
== tmp
)
194 for (i
= 0; tmp
[i
] != '.'; i
++)
195 if (!isdigit(tmp
[i
]))
197 tmp
= dss_strdup(dash
+ 1);
199 ret
= dss_atoi64(tmp
, &num
);
207 s
->completion_time
= num
;
208 s
->flags
= SS_COMPLETE
;
209 if (!strcmp(dot
+ 1, "being_deleted"))
210 s
->flags
|= SS_BEING_DELETED
;
212 s
->name
= dss_strdup(dirname
);
216 int64_t get_current_time(void)
220 DSS_DEBUG_LOG("now: %lli\n", (long long) now
);
224 char *incomplete_name(int64_t start
)
226 return make_message("%lli-incomplete", (long long)start
);
229 char *being_deleted_name(struct snapshot
*s
)
231 if (s
->flags
& SS_COMPLETE
)
232 return make_message("%lli-%lli.being_deleted",
233 (long long)s
->creation_time
,
234 (long long)s
->completion_time
);
235 return make_message("%lli-incomplete.being_deleted",
236 (long long)s
->creation_time
);
239 int complete_name(int64_t start
, int64_t end
, char **result
)
241 struct tm start_tm
, end_tm
;
242 time_t *start_seconds
= (time_t *) (uint64_t *)&start
; /* STFU, gcc */
243 time_t *end_seconds
= (time_t *) (uint64_t *)&end
; /* STFU, gcc */
244 char start_str
[200], end_str
[200];
246 if (!localtime_r(start_seconds
, &start_tm
)) {
247 make_err_msg("%lli", (long long)start
);
250 if (!localtime_r(end_seconds
, &end_tm
)) {
251 make_err_msg("%lli", (long long)end
);
254 if (!strftime(start_str
, sizeof(start_str
), "%a_%b_%d_%Y_%H_%M_%S", &start_tm
)) {
255 make_err_msg("%lli", (long long)start
);
258 if (!strftime(end_str
, sizeof(end_str
), "%a_%b_%d_%Y_%H_%M_%S", &end_tm
)) {
259 make_err_msg("%lli", (long long)end
);
262 *result
= make_message("%lli-%lli.%s-%s", (long long) start
, (long long) end
,
267 struct snapshot_list
{
269 unsigned num_snapshots
;
271 struct snapshot
**snapshots
;
273 * Array of size num_intervals + 1
275 * It contains the number of snapshots in each interval. interval_count[num_intervals]
276 * is the number of snapshots which belong to any interval greater than num_intervals.
278 unsigned *interval_count
;
281 #define FOR_EACH_SNAPSHOT(s, i, sl) \
282 for ((i) = 0; (i) < (sl)->num_snapshots && ((s) = (sl)->snapshots[(i)]); (i)++)
286 #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
288 static int compare_snapshots(const void *a
, const void *b
)
290 struct snapshot
*s1
= *(struct snapshot
**)a
;
291 struct snapshot
*s2
= *(struct snapshot
**)b
;
292 return NUM_COMPARE(s2
->creation_time
, s1
->creation_time
);
295 /** Compute the minimum of \a a and \a b. */
296 #define DSS_MIN(a,b) ((a) < (b) ? (a) : (b))
298 int add_snapshot(const char *dirname
, void *private)
300 struct snapshot_list
*sl
= private;
302 int ret
= is_snapshot(dirname
, sl
->now
, &s
);
306 if (sl
->num_snapshots
>= sl
->array_size
) {
307 sl
->array_size
= 2 * sl
->array_size
+ 1;
308 sl
->snapshots
= dss_realloc(sl
->snapshots
,
309 sl
->array_size
* sizeof(struct snapshot
*));
311 sl
->snapshots
[sl
->num_snapshots
] = dss_malloc(sizeof(struct snapshot
));
312 *(sl
->snapshots
[sl
->num_snapshots
]) = s
;
313 sl
->interval_count
[DSS_MIN(s
.interval
, conf
.num_intervals_arg
)]++;
318 void get_snapshot_list(struct snapshot_list
*sl
)
320 sl
->now
= get_current_time();
321 sl
->num_snapshots
= 0;
323 sl
->snapshots
= NULL
;
324 sl
->interval_count
= dss_calloc((conf
.num_intervals_arg
+ 1) * sizeof(unsigned));
325 for_each_subdir(add_snapshot
, sl
);
326 qsort(sl
->snapshots
, sl
->num_snapshots
, sizeof(struct snapshot
*),
330 void stop_rsync_process(void)
332 if (!rsync_pid
|| rsync_stopped
)
334 kill(SIGSTOP
, rsync_pid
);
338 void restart_rsync_process(void)
340 if (!rsync_pid
|| !rsync_stopped
)
342 kill (SIGCONT
, rsync_pid
);
346 void free_snapshot_list(struct snapshot_list
*sl
)
351 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
355 free(sl
->interval_count
);
360 * Print a log message about the exit status of a child.
362 void log_termination_msg(pid_t pid
, int status
)
364 if (WIFEXITED(status
))
365 DSS_INFO_LOG("child %i exited. Exit status: %i\n", (int)pid
,
366 WEXITSTATUS(status
));
367 else if (WIFSIGNALED(status
))
368 DSS_NOTICE_LOG("child %i was killed by signal %i\n", (int)pid
,
371 DSS_WARNING_LOG("child %i terminated abormally\n", (int)pid
);
374 int wait_for_process(pid_t pid
, int *status
)
378 DSS_DEBUG_LOG("Waiting for process %d to terminate\n", (int)pid
);
386 if (ret
== SIGCHLD
) {
387 ret
= waitpid(pid
, status
, 0);
390 if (errno
!= EINTR
) /* error */
393 DSS_WARNING_LOG("sending SIGTERM to pid %d\n", (int)pid
);
397 ret
= -ERRNO_TO_DSS_ERROR(errno
);
398 make_err_msg("failed to wait for process %d", (int)pid
);
400 log_termination_msg(pid
, *status
);
404 int remove_snapshot(struct snapshot
*s
)
406 int fds
[3] = {0, 0, 0};
408 char *new_name
= being_deleted_name(s
);
409 int ret
= dss_rename(s
->name
, new_name
);
410 char *argv
[] = {"rm", "-rf", new_name
, NULL
};
414 DSS_NOTICE_LOG("removing %s (interval = %i)\n", s
->name
, s
->interval
);
415 stop_rsync_process();
416 ret
= dss_exec(&rm_pid
, argv
[0], argv
, fds
);
422 * return: 0: no redundant snapshots, 1: rm process started, negative: error
424 int remove_redundant_snapshot(struct snapshot_list
*sl
)
426 int ret
, i
, interval
;
428 unsigned missing
= 0;
430 DSS_INFO_LOG("looking for intervals containing too many snapshots\n");
431 for (interval
= conf
.num_intervals_arg
- 1; interval
>= 0; interval
--) {
432 unsigned keep
= num_snapshots(interval
);
433 unsigned num
= sl
->interval_count
[interval
];
434 struct snapshot
*victim
= NULL
, *prev
= NULL
;
435 int64_t score
= LONG_MAX
;
438 missing
+= keep
- num
;
439 DSS_DEBUG_LOG("interval %i: keep: %u, have: %u, missing: %u\n",
440 interval
, keep
, num
, missing
);
441 if (keep
+ missing
>= num
)
443 /* redundant snapshot in this interval, pick snapshot with lowest score */
444 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
447 //DSS_DEBUG_LOG("checking %s\n", s->name);
448 if (s
->interval
> interval
) {
452 if (s
->interval
< interval
)
460 /* check if s is a better victim */
461 this_score
= s
->creation_time
- prev
->creation_time
;
462 assert(this_score
>= 0);
463 //DSS_DEBUG_LOG("%s: score %lli\n", s->name, (long long)score);
464 if (this_score
< score
) {
471 if (conf
.dry_run_given
) {
472 dss_msg("%s would be removed (interval = %i)\n",
473 victim
->name
, victim
->interval
);
476 ret
= remove_snapshot(victim
);
477 return ret
< 0? ret
: 1;
482 int remove_outdated_snapshot(struct snapshot_list
*sl
)
487 DSS_INFO_LOG("looking for snapshots belonging to intervals greater than %d\n",
488 conf
.num_intervals_arg
);
489 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
490 if (s
->interval
<= conf
.num_intervals_arg
)
492 if (conf
.dry_run_given
) {
493 dss_msg("%s would be removed (interval = %i)\n",
494 s
->name
, s
->interval
);
497 ret
= remove_snapshot(s
);
505 int handle_rm_exit(int status
)
509 if (!WIFEXITED(status
)) {
510 make_err_msg("rm process %d died involuntary", (int)rm_pid
);
511 ret
= -E_INVOLUNTARY_EXIT
;
514 es
= WEXITSTATUS(status
);
516 make_err_msg("rm process %d returned %d", (int)rm_pid
, es
);
517 ret
= -E_BAD_EXIT_CODE
;
527 int wait_for_rm_process(void)
529 int status
, ret
= wait_for_process(rm_pid
, &status
);
533 return handle_rm_exit(status
);
536 void kill_process(pid_t pid
)
540 DSS_WARNING_LOG("sending SIGTERM to pid %d\n", (int)pid
);
546 DSS_INFO_LOG("FIXME: no sighup handling yet\n");
549 int rename_incomplete_snapshot(int64_t start
)
551 char *old_name
, *new_name
;
554 ret
= complete_name(start
, get_current_time(), &new_name
);
557 old_name
= incomplete_name(start
);
558 ret
= dss_rename(old_name
, new_name
);
560 DSS_NOTICE_LOG("%s -> %s\n", old_name
, new_name
);
567 int handle_rsync_exit(int status
)
571 if (!WIFEXITED(status
)) {
572 make_err_msg("rsync process %d died involuntary", (int)rsync_pid
);
573 ret
= -E_INVOLUNTARY_EXIT
;
576 es
= WEXITSTATUS(status
);
577 if (es
!= 0 && es
!= 23 && es
!= 24) {
578 make_err_msg("rsync process %d returned %d", (int)rsync_pid
, es
);
579 ret
= -E_BAD_EXIT_CODE
;
582 ret
= rename_incomplete_snapshot(current_snapshot_creation_time
);
585 current_snapshot_creation_time
= 0;
591 int get_newest_complete(const char *dirname
, void *private)
593 struct edge_snapshot_data
*esd
= private;
595 int ret
= is_snapshot(dirname
, esd
->now
, &s
);
599 if (s
.flags
!= SS_COMPLETE
) /* incomplete or being deleted */
601 if (s
.creation_time
< esd
->snap
.creation_time
)
603 free(esd
->snap
.name
);
608 __malloc
char *name_of_newest_complete_snapshot(void)
610 struct edge_snapshot_data esd
= {
611 .now
= get_current_time(),
612 .snap
= {.creation_time
= -1}
614 for_each_subdir(get_newest_complete
, &esd
);
615 return esd
.snap
.name
;
618 void create_rsync_argv(char ***argv
, int64_t *num
)
620 char *logname
, *newest
= name_of_newest_complete_snapshot();
623 *argv
= dss_malloc((15 + conf
.rsync_option_given
) * sizeof(char *));
624 (*argv
)[i
++] = dss_strdup("rsync");
625 (*argv
)[i
++] = dss_strdup("-aq");
626 (*argv
)[i
++] = dss_strdup("--delete");
627 for (j
= 0; j
< conf
.rsync_option_given
; j
++)
628 (*argv
)[i
++] = dss_strdup(conf
.rsync_option_arg
[j
]);
630 DSS_INFO_LOG("using %s as reference snapshot\n", newest
);
631 (*argv
)[i
++] = make_message("--link-dest=../%s", newest
);
634 DSS_INFO_LOG("no previous snapshot found\n");
635 if (conf
.exclude_patterns_given
) {
636 (*argv
)[i
++] = dss_strdup("--exclude-from");
637 (*argv
)[i
++] = dss_strdup(conf
.exclude_patterns_arg
);
640 logname
= dss_logname();
641 if (conf
.remote_user_given
&& !strcmp(conf
.remote_user_arg
, logname
))
642 (*argv
)[i
++] = dss_strdup(conf
.source_dir_arg
);
644 (*argv
)[i
++] = make_message("%s@%s:%s/", conf
.remote_user_given
?
645 conf
.remote_user_arg
: logname
,
646 conf
.remote_host_arg
, conf
.source_dir_arg
);
648 *num
= get_current_time();
649 (*argv
)[i
++] = incomplete_name(*num
);
651 for (j
= 0; j
< i
; j
++)
652 DSS_DEBUG_LOG("argv[%d] = %s\n", j
, (*argv
)[j
]);
655 void free_rsync_argv(char **argv
)
658 for (i
= 0; argv
[i
]; i
++)
663 int create_snapshot(char **argv
)
665 int fds
[3] = {0, 0, 0};
666 char *name
= incomplete_name(current_snapshot_creation_time
);
668 DSS_NOTICE_LOG("creating new snapshot %s\n", name
);
670 return dss_exec(&rsync_pid
, argv
[0], argv
, fds
);
673 void compute_next_snapshot_time(struct snapshot_list
*sl
)
675 struct timeval now
, unit_interval
= {.tv_sec
= 24 * 3600 * conf
.unit_interval_arg
},
678 unsigned wanted
= num_snapshots(0), num_complete_snapshots
= 0;
682 gettimeofday(&now
, NULL
);
683 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
684 if (!(s
->flags
& SS_COMPLETE
))
686 num_complete_snapshots
++;
687 x
+= s
->completion_time
- s
->creation_time
;
690 if (num_complete_snapshots
)
691 x
/= num_complete_snapshots
; /* avg time to create one snapshot */
692 x
*= wanted
; /* time to create all snapshots in interval 0 */
695 ret
= tv_diff(&unit_interval
, &tmp
, &diff
); /* time between creation */
697 next_snapshot_time
= now
;
700 tv_divide(wanted
, &diff
, &tmp
);
701 tv_add(&now
, &tmp
, &next_snapshot_time
);
704 void handle_signal(struct snapshot_list
*sl
)
706 int sig
, ret
= next_signal();
716 restart_rsync_process();
717 kill_process(rsync_pid
);
718 kill_process(rm_pid
);
724 ret
= reap_child(&pid
, &status
);
727 assert(pid
== rsync_pid
|| pid
== rm_pid
);
728 if (pid
== rsync_pid
)
729 ret
= handle_rsync_exit(status
);
731 ret
= handle_rm_exit(status
);
732 free_snapshot_list(sl
);
733 get_snapshot_list(sl
);
734 compute_next_snapshot_time(sl
);
738 log_err_msg(ERROR
, -ret
);
741 int get_oldest(const char *dirname
, void *private)
743 struct edge_snapshot_data
*esd
= private;
745 int ret
= is_snapshot(dirname
, esd
->now
, &s
);
749 if (s
.creation_time
> esd
->snap
.creation_time
)
751 free(esd
->snap
.name
);
756 int remove_oldest_snapshot()
759 struct edge_snapshot_data esd
= {
760 .now
= get_current_time(),
761 .snap
= {.creation_time
= LLONG_MAX
}
763 for_each_subdir(get_oldest
, &esd
);
764 if (!esd
.snap
.name
) /* no snapshot found */
766 DSS_INFO_LOG("oldest snapshot: %s\n", esd
.snap
.name
);
768 if (esd
.snap
.creation_time
== current_snapshot_creation_time
)
769 goto out
; /* do not remove the snapshot currently being created */
770 ret
= remove_snapshot(&esd
.snap
);
776 /* TODO: Also consider number of inodes. */
777 int disk_space_low(void)
779 struct disk_space ds
;
780 int ret
= get_disk_space(".", &ds
);
784 if (conf
.min_free_mb_arg
)
785 if (ds
.free_mb
< conf
.min_free_mb_arg
)
787 if (conf
.min_free_percent_arg
)
788 if (ds
.percent_free
< conf
.min_free_percent_arg
)
793 int try_to_free_disk_space(int low_disk_space
, struct snapshot_list
*sl
)
797 ret
= remove_outdated_snapshot(sl
);
798 if (ret
) /* error, or we are removing something */
800 /* no outdated snapshot */
801 ret
= remove_redundant_snapshot(sl
);
806 DSS_WARNING_LOG("disk space low and nothing obvious to remove\n");
807 ret
= remove_oldest_snapshot();
810 make_err_msg("uhuhu: not enough disk space for a single snapshot");
814 int select_loop(void)
817 struct timeval tv
= {.tv_sec
= 0, .tv_usec
= 0};
818 struct snapshot_list sl
= {.num_snapshots
= 0};
820 get_snapshot_list(&sl
);
821 compute_next_snapshot_time(&sl
);
823 struct timeval now
, *tvp
= &tv
;
829 FD_SET(signal_pipe
, &rfds
);
834 ret
= dss_select(signal_pipe
+ 1, &rfds
, NULL
, tvp
);
837 if (FD_ISSET(signal_pipe
, &rfds
))
841 ret
= disk_space_low();
844 low_disk_space
= ret
;
846 stop_rsync_process();
847 ret
= try_to_free_disk_space(low_disk_space
, &sl
);
853 restart_rsync_process();
856 /* neither rsync nor rm are running. Start rsync? */
857 gettimeofday(&now
, NULL
);
858 if (tv_diff(&next_snapshot_time
, &now
, &tv
) > 0)
860 create_rsync_argv(&rsync_argv
, ¤t_snapshot_creation_time
);
861 ret
= create_snapshot(rsync_argv
);
862 free_rsync_argv(rsync_argv
);
866 free_snapshot_list(&sl
);
874 if (conf
.dry_run_given
) {
875 make_err_msg("dry_run not supported by this command");
878 ret
= install_sighandler(SIGHUP
);
881 return select_loop();
884 void log_disk_space(struct disk_space
*ds
)
886 DSS_INFO_LOG("free: %uM/%uM (%u%%), %u%% inodes unused\n",
887 ds
->free_mb
, ds
->total_mb
, ds
->percent_free
,
888 ds
->percent_free_inodes
);
894 struct snapshot_list sl
;
895 struct disk_space ds
;
897 ret
= get_disk_space(".", &ds
);
902 get_snapshot_list(&sl
);
903 ret
= remove_outdated_snapshot(&sl
);
904 free_snapshot_list(&sl
);
909 ret
= wait_for_rm_process();
914 get_snapshot_list(&sl
);
915 ret
= remove_redundant_snapshot(&sl
);
916 free_snapshot_list(&sl
);
921 ret
= wait_for_rm_process();
935 create_rsync_argv(&rsync_argv
, ¤t_snapshot_creation_time
);
936 if (conf
.dry_run_given
) {
939 for (i
= 0; rsync_argv
[i
]; i
++) {
941 msg
= make_message("%s%s%s", tmp
? tmp
: "",
942 tmp
? " " : "", rsync_argv
[i
]);
945 dss_msg("%s\n", msg
);
949 ret
= create_snapshot(rsync_argv
);
952 ret
= wait_for_process(rsync_pid
, &status
);
955 ret
= handle_rsync_exit(status
);
957 free_rsync_argv(rsync_argv
);
964 struct snapshot_list sl
;
966 get_snapshot_list(&sl
);
967 FOR_EACH_SNAPSHOT(s
, i
, &sl
)
968 dss_msg("%u\t%s\n", s
->interval
, s
->name
);
969 free_snapshot_list(&sl
);
973 /* TODO: Unlink pid file */
974 __noreturn
void clean_exit(int status
)
981 int read_config_file(void)
987 if (conf
.config_file_given
)
988 config_file
= dss_strdup(conf
.config_file_arg
);
990 char *home
= get_homedir();
991 config_file
= make_message("%s/.dssrc", home
);
994 ret
= stat(config_file
, &statbuf
);
995 if (ret
&& conf
.config_file_given
) {
996 ret
= -ERRNO_TO_DSS_ERROR(errno
);
997 make_err_msg("failed to stat config file %s", config_file
);
1001 struct cmdline_parser_params params
= {
1004 .check_required
= 0,
1005 .check_ambiguity
= 0
1007 cmdline_parser_config_file(config_file
, &conf
, ¶ms
);
1009 if (!conf
.source_dir_given
|| !conf
.dest_dir_given
) {
1011 make_err_msg("you need to specify both source_dir and dest_dir");
1020 int check_config(void)
1022 if (conf
.unit_interval_arg
<= 0) {
1023 make_err_msg("bad unit interval: %i", conf
.unit_interval_arg
);
1024 return -E_INVALID_NUMBER
;
1026 DSS_DEBUG_LOG("unit interval: %i day(s)\n", conf
.unit_interval_arg
);
1027 if (conf
.num_intervals_arg
<= 0) {
1028 make_err_msg("bad number of intervals %i", conf
.num_intervals_arg
);
1029 return -E_INVALID_NUMBER
;
1031 DSS_DEBUG_LOG("number of intervals: %i\n", conf
.num_intervals_arg
);
1035 static void setup_signal_handling(void)
1039 DSS_INFO_LOG("setting up signal handlers\n");
1040 signal_pipe
= signal_init(); /* always successful */
1041 ret
= install_sighandler(SIGINT
);
1044 ret
= install_sighandler(SIGTERM
);
1047 ret
= install_sighandler(SIGCHLD
);
1052 DSS_EMERG_LOG("could not install signal handlers\n");
1057 int main(int argc
, char **argv
)
1061 cmdline_parser(argc
, argv
, &conf
); /* aborts on errors */
1062 ret
= read_config_file();
1065 ret
= check_config();
1068 if (conf
.logfile_given
) {
1069 logfile
= open_log(conf
.logfile_arg
);
1070 log_welcome(conf
.loglevel_arg
);
1072 ret
= dss_chdir(conf
.dest_dir_arg
);
1075 if (conf
.daemon_given
)
1077 setup_signal_handling();
1078 ret
= call_command_handler();
1081 log_err_msg(EMERG
, -ret
);
1082 clean_exit(ret
>= 0? EXIT_SUCCESS
: EXIT_FAILURE
);