19 #include "gcc-compat.h"
32 struct gengetopt_args_info conf
;
33 char *dss_error_txt
= NULL
;
35 static int signal_pipe
;
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
{
78 /** The rsync process terminated successfully. */
80 /** The rm process is running to remove this snapshot. */
86 int64_t creation_time
;
87 int64_t completion_time
;
88 enum snapshot_status_flags flags
;
93 * An edge snapshot is either the oldest one or the newest one.
95 * We need to find either of them occasionally: The create code
96 * needs to know the newest snapshot because that is the one
97 * used as the link destination dir. The pruning code needs to
98 * find the oldest one in case disk space becomes low.
100 struct edge_snapshot_data
{
102 struct snapshot snap
;
105 __printf_2_3
void dss_log(int ll
, const char* fmt
,...)
113 if (ll
< conf
.loglevel_arg
)
115 outfd
= logfile
? logfile
: stderr
;
118 strftime(str
, sizeof(str
), "%b %d %H:%M:%S", tm
);
119 fprintf(outfd
, "%s ", str
);
120 if (conf
.loglevel_arg
<= INFO
)
121 fprintf(outfd
, "%i: ", ll
);
123 vfprintf(outfd
, fmt
, argp
);
128 * Print a message either to stdout or to the log file.
130 __printf_1_2
void dss_msg(const char* fmt
,...)
132 FILE *outfd
= conf
.daemon_given
? logfile
: stdout
;
135 vfprintf(outfd
, fmt
, argp
);
140 * Return the desired number of snapshots of an interval.
142 unsigned num_snapshots(int interval
)
146 assert(interval
>= 0);
148 if (interval
>= conf
.num_intervals_arg
)
150 n
= conf
.num_intervals_arg
- interval
- 1;
154 /* return: Whether dirname is a snapshot directory (0: no, 1: yes) */
155 int is_snapshot(const char *dirname
, int64_t now
, struct snapshot
*s
)
158 char *dash
, *dot
, *tmp
;
162 dash
= strchr(dirname
, '-');
163 if (!dash
|| !dash
[1] || dash
== dirname
)
165 for (i
= 0; dirname
[i
] != '-'; i
++)
166 if (!isdigit(dirname
[i
]))
168 tmp
= dss_strdup(dirname
);
170 ret
= dss_atoi64(tmp
, &num
);
179 s
->creation_time
= num
;
180 //DSS_DEBUG_LOG("%s start time: %lli\n", dirname, (long long)s->creation_time);
181 s
->interval
= (long long) ((now
- s
->creation_time
)
182 / conf
.unit_interval_arg
/ 24 / 3600);
183 if (!strcmp(dash
+ 1, "incomplete")) {
184 s
->completion_time
= -1;
185 s
->flags
= 0; /* neither complete, nor being deleted */
188 if (!strcmp(dash
+ 1, "incomplete.being_deleted")) {
189 s
->completion_time
= -1;
190 s
->flags
= SS_BEING_DELETED
; /* mot cpmplete, being deleted */
194 dot
= strchr(tmp
, '.');
195 if (!dot
|| !dot
[1] || dot
== tmp
)
197 for (i
= 0; tmp
[i
] != '.'; i
++)
198 if (!isdigit(tmp
[i
]))
200 tmp
= dss_strdup(dash
+ 1);
202 ret
= dss_atoi64(tmp
, &num
);
210 s
->completion_time
= num
;
211 s
->flags
= SS_COMPLETE
;
212 if (!strcmp(dot
+ 1, "being_deleted"))
213 s
->flags
|= SS_BEING_DELETED
;
215 s
->name
= dss_strdup(dirname
);
219 int64_t get_current_time(void)
223 DSS_DEBUG_LOG("now: %lli\n", (long long) now
);
227 char *incomplete_name(int64_t start
)
229 return make_message("%lli-incomplete", (long long)start
);
232 char *being_deleted_name(struct snapshot
*s
)
234 if (s
->flags
& SS_COMPLETE
)
235 return make_message("%lli-%lli.being_deleted",
236 (long long)s
->creation_time
,
237 (long long)s
->completion_time
);
238 return make_message("%lli-incomplete.being_deleted",
239 (long long)s
->creation_time
);
242 int complete_name(int64_t start
, int64_t end
, char **result
)
244 struct tm start_tm
, end_tm
;
245 time_t *start_seconds
= (time_t *) (uint64_t *)&start
; /* STFU, gcc */
246 time_t *end_seconds
= (time_t *) (uint64_t *)&end
; /* STFU, gcc */
247 char start_str
[200], end_str
[200];
249 if (!localtime_r(start_seconds
, &start_tm
)) {
250 make_err_msg("%lli", (long long)start
);
253 if (!localtime_r(end_seconds
, &end_tm
)) {
254 make_err_msg("%lli", (long long)end
);
257 if (!strftime(start_str
, sizeof(start_str
), "%a_%b_%d_%Y_%H_%M_%S", &start_tm
)) {
258 make_err_msg("%lli", (long long)start
);
261 if (!strftime(end_str
, sizeof(end_str
), "%a_%b_%d_%Y_%H_%M_%S", &end_tm
)) {
262 make_err_msg("%lli", (long long)end
);
265 *result
= make_message("%lli-%lli.%s-%s", (long long) start
, (long long) end
,
270 struct snapshot_list
{
272 unsigned num_snapshots
;
274 struct snapshot
**snapshots
;
276 * Array of size num_intervals + 1
278 * It contains the number of snapshots in each interval. interval_count[num_intervals]
279 * is the number of snapshots which belong to any interval greater than num_intervals.
281 unsigned *interval_count
;
284 #define FOR_EACH_SNAPSHOT(s, i, sl) \
285 for ((i) = 0; (i) < (sl)->num_snapshots && ((s) = (sl)->snapshots[(i)]); (i)++)
289 #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
291 static int compare_snapshots(const void *a
, const void *b
)
293 struct snapshot
*s1
= *(struct snapshot
**)a
;
294 struct snapshot
*s2
= *(struct snapshot
**)b
;
295 return NUM_COMPARE(s2
->creation_time
, s1
->creation_time
);
298 /** Compute the minimum of \a a and \a b. */
299 #define DSS_MIN(a,b) ((a) < (b) ? (a) : (b))
301 int add_snapshot(const char *dirname
, void *private)
303 struct snapshot_list
*sl
= private;
305 int ret
= is_snapshot(dirname
, sl
->now
, &s
);
309 if (sl
->num_snapshots
>= sl
->array_size
) {
310 sl
->array_size
= 2 * sl
->array_size
+ 1;
311 sl
->snapshots
= dss_realloc(sl
->snapshots
,
312 sl
->array_size
* sizeof(struct snapshot
*));
314 sl
->snapshots
[sl
->num_snapshots
] = dss_malloc(sizeof(struct snapshot
));
315 *(sl
->snapshots
[sl
->num_snapshots
]) = s
;
316 sl
->interval_count
[DSS_MIN(s
.interval
, conf
.num_intervals_arg
)]++;
321 void get_snapshot_list(struct snapshot_list
*sl
)
323 sl
->now
= get_current_time();
324 sl
->num_snapshots
= 0;
326 sl
->snapshots
= NULL
;
327 sl
->interval_count
= dss_calloc((conf
.num_intervals_arg
+ 1) * sizeof(unsigned));
328 for_each_subdir(add_snapshot
, sl
);
329 qsort(sl
->snapshots
, sl
->num_snapshots
, sizeof(struct snapshot
*),
333 void free_snapshot_list(struct snapshot_list
*sl
)
338 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
342 free(sl
->interval_count
);
343 sl
->interval_count
= NULL
;
345 sl
->snapshots
= NULL
;
346 sl
->num_snapshots
= 0;
349 void stop_rsync_process(void)
351 if (!rsync_pid
|| rsync_stopped
)
353 kill(SIGSTOP
, rsync_pid
);
357 void restart_rsync_process(void)
359 if (!rsync_pid
|| !rsync_stopped
)
361 kill (SIGCONT
, rsync_pid
);
366 * Print a log message about the exit status of a child.
368 void log_termination_msg(pid_t pid
, int status
)
370 if (WIFEXITED(status
))
371 DSS_INFO_LOG("child %i exited. Exit status: %i\n", (int)pid
,
372 WEXITSTATUS(status
));
373 else if (WIFSIGNALED(status
))
374 DSS_NOTICE_LOG("child %i was killed by signal %i\n", (int)pid
,
377 DSS_WARNING_LOG("child %i terminated abormally\n", (int)pid
);
380 int wait_for_process(pid_t pid
, int *status
)
384 DSS_DEBUG_LOG("Waiting for process %d to terminate\n", (int)pid
);
389 FD_SET(signal_pipe
, &rfds
);
390 ret
= dss_select(signal_pipe
+ 1, &rfds
, NULL
, NULL
);
396 if (ret
== SIGCHLD
) {
397 ret
= waitpid(pid
, status
, 0);
400 if (errno
!= EINTR
) { /* error */
401 ret
= -ERRNO_TO_DSS_ERROR(errno
);
405 /* SIGINT or SIGTERM */
406 DSS_WARNING_LOG("sending SIGTERM to pid %d\n", (int)pid
);
410 make_err_msg("failed to wait for process %d", (int)pid
);
412 log_termination_msg(pid
, *status
);
416 int remove_snapshot(struct snapshot
*s
)
418 int fds
[3] = {0, 0, 0};
420 char *new_name
= being_deleted_name(s
);
421 int ret
= dss_rename(s
->name
, new_name
);
422 char *argv
[] = {"rm", "-rf", new_name
, NULL
};
426 DSS_NOTICE_LOG("removing %s (interval = %i)\n", s
->name
, s
->interval
);
427 stop_rsync_process();
428 ret
= dss_exec(&rm_pid
, argv
[0], argv
, fds
);
435 * return: 0: no redundant snapshots, 1: rm process started, negative: error
437 int remove_redundant_snapshot(struct snapshot_list
*sl
)
439 int ret
, i
, interval
;
441 unsigned missing
= 0;
443 DSS_INFO_LOG("looking for intervals containing too many snapshots\n");
444 for (interval
= conf
.num_intervals_arg
- 1; interval
>= 0; interval
--) {
445 unsigned keep
= num_snapshots(interval
);
446 unsigned num
= sl
->interval_count
[interval
];
447 struct snapshot
*victim
= NULL
, *prev
= NULL
;
448 int64_t score
= LONG_MAX
;
451 missing
+= keep
- num
;
452 DSS_DEBUG_LOG("interval %i: keep: %u, have: %u, missing: %u\n",
453 interval
, keep
, num
, missing
);
454 if (keep
+ missing
>= num
)
456 /* redundant snapshot in this interval, pick snapshot with lowest score */
457 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
460 //DSS_DEBUG_LOG("checking %s\n", s->name);
461 if (s
->interval
> interval
) {
465 if (s
->interval
< interval
)
473 /* check if s is a better victim */
474 this_score
= s
->creation_time
- prev
->creation_time
;
475 assert(this_score
>= 0);
476 //DSS_DEBUG_LOG("%s: score %lli\n", s->name, (long long)score);
477 if (this_score
< score
) {
484 if (conf
.dry_run_given
) {
485 dss_msg("%s would be removed (interval = %i)\n",
486 victim
->name
, victim
->interval
);
489 ret
= remove_snapshot(victim
);
490 return ret
< 0? ret
: 1;
495 int remove_outdated_snapshot(struct snapshot_list
*sl
)
500 DSS_INFO_LOG("looking for snapshots belonging to intervals greater than %d\n",
501 conf
.num_intervals_arg
);
502 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
503 if (s
->interval
<= conf
.num_intervals_arg
)
505 if (conf
.dry_run_given
) {
506 dss_msg("%s would be removed (interval = %i)\n",
507 s
->name
, s
->interval
);
510 ret
= remove_snapshot(s
);
518 int handle_rm_exit(int status
)
522 if (!WIFEXITED(status
)) {
523 make_err_msg("rm process %d died involuntary", (int)rm_pid
);
524 ret
= -E_INVOLUNTARY_EXIT
;
527 es
= WEXITSTATUS(status
);
529 make_err_msg("rm process %d returned %d", (int)rm_pid
, es
);
530 ret
= -E_BAD_EXIT_CODE
;
539 int wait_for_rm_process(void)
541 int status
, ret
= wait_for_process(rm_pid
, &status
);
545 return handle_rm_exit(status
);
548 void kill_process(pid_t pid
)
552 DSS_WARNING_LOG("sending SIGTERM to pid %d\n", (int)pid
);
556 int check_config(void)
558 if (conf
.unit_interval_arg
<= 0) {
559 make_err_msg("bad unit interval: %i", conf
.unit_interval_arg
);
560 return -E_INVALID_NUMBER
;
562 DSS_DEBUG_LOG("unit interval: %i day(s)\n", conf
.unit_interval_arg
);
563 if (conf
.num_intervals_arg
<= 0) {
564 make_err_msg("bad number of intervals %i", conf
.num_intervals_arg
);
565 return -E_INVALID_NUMBER
;
567 DSS_DEBUG_LOG("number of intervals: %i\n", conf
.num_intervals_arg
);
571 /* exits on errors */
572 void parse_config_file(int override
)
577 char *old_logfile_arg
= NULL
;
578 int old_daemon_given
= 0;
580 if (conf
.config_file_given
)
581 config_file
= dss_strdup(conf
.config_file_arg
);
583 char *home
= get_homedir();
584 config_file
= make_message("%s/.dssrc", home
);
587 if (override
) { /* SIGHUP */
588 if (conf
.logfile_given
)
589 old_logfile_arg
= dss_strdup(conf
.logfile_arg
);
590 old_daemon_given
= conf
.daemon_given
;
593 ret
= stat(config_file
, &statbuf
);
594 if (ret
&& conf
.config_file_given
) {
595 ret
= -ERRNO_TO_DSS_ERROR(errno
);
596 make_err_msg("failed to stat config file %s", config_file
);
600 struct cmdline_parser_params params
= {
601 .override
= override
,
606 cmdline_parser_config_file(config_file
, &conf
, ¶ms
);
608 ret
= check_config();
612 /* don't change daemon mode on SIGHUP */
613 conf
.daemon_given
= old_daemon_given
;
616 if (conf
.logfile_given
)
617 free(old_logfile_arg
);
618 else if (conf
.daemon_given
) { /* re-use old logfile */
619 conf
.logfile_arg
= old_logfile_arg
;
620 conf
.logfile_given
= 1;
623 if (conf
.logfile_given
) {
624 logfile
= open_log(conf
.logfile_arg
);
625 log_welcome(conf
.loglevel_arg
);
627 ret
= dss_chdir(conf
.dest_dir_arg
);
632 log_err_msg(EMERG
, -ret
);
636 void handle_sighup(void)
638 DSS_NOTICE_LOG("SIGHUP\n");
639 parse_config_file(1);
642 int rename_incomplete_snapshot(int64_t start
)
644 char *old_name
, *new_name
;
647 ret
= complete_name(start
, get_current_time(), &new_name
);
650 old_name
= incomplete_name(start
);
651 ret
= dss_rename(old_name
, new_name
);
653 DSS_NOTICE_LOG("%s -> %s\n", old_name
, new_name
);
659 int handle_rsync_exit(int status
)
663 if (!WIFEXITED(status
)) {
664 make_err_msg("rsync process %d died involuntary", (int)rsync_pid
);
665 ret
= -E_INVOLUNTARY_EXIT
;
668 es
= WEXITSTATUS(status
);
669 if (es
!= 0 && es
!= 23 && es
!= 24) {
670 make_err_msg("rsync process %d returned %d", (int)rsync_pid
, es
);
671 ret
= -E_BAD_EXIT_CODE
;
674 ret
= rename_incomplete_snapshot(current_snapshot_creation_time
);
677 current_snapshot_creation_time
= 0;
682 int get_newest_complete(const char *dirname
, void *private)
684 struct edge_snapshot_data
*esd
= private;
686 int ret
= is_snapshot(dirname
, esd
->now
, &s
);
690 if (s
.flags
!= SS_COMPLETE
) /* incomplete or being deleted */
692 if (s
.creation_time
< esd
->snap
.creation_time
)
694 free(esd
->snap
.name
);
699 __malloc
char *name_of_newest_complete_snapshot(void)
701 struct edge_snapshot_data esd
= {
702 .now
= get_current_time(),
703 .snap
= {.creation_time
= -1}
705 for_each_subdir(get_newest_complete
, &esd
);
706 return esd
.snap
.name
;
709 void create_rsync_argv(char ***argv
, int64_t *num
)
711 char *logname
, *newest
= name_of_newest_complete_snapshot();
714 *argv
= dss_malloc((15 + conf
.rsync_option_given
) * sizeof(char *));
715 (*argv
)[i
++] = dss_strdup("rsync");
716 (*argv
)[i
++] = dss_strdup("-aq");
717 (*argv
)[i
++] = dss_strdup("--delete");
718 for (j
= 0; j
< conf
.rsync_option_given
; j
++)
719 (*argv
)[i
++] = dss_strdup(conf
.rsync_option_arg
[j
]);
721 DSS_INFO_LOG("using %s as reference snapshot\n", newest
);
722 (*argv
)[i
++] = make_message("--link-dest=../%s", newest
);
725 DSS_INFO_LOG("no previous snapshot found\n");
726 if (conf
.exclude_patterns_given
) {
727 (*argv
)[i
++] = dss_strdup("--exclude-from");
728 (*argv
)[i
++] = dss_strdup(conf
.exclude_patterns_arg
);
731 logname
= dss_logname();
732 if (conf
.remote_user_given
&& !strcmp(conf
.remote_user_arg
, logname
))
733 (*argv
)[i
++] = dss_strdup(conf
.source_dir_arg
);
735 (*argv
)[i
++] = make_message("%s@%s:%s/", conf
.remote_user_given
?
736 conf
.remote_user_arg
: logname
,
737 conf
.remote_host_arg
, conf
.source_dir_arg
);
739 *num
= get_current_time();
740 (*argv
)[i
++] = incomplete_name(*num
);
742 for (j
= 0; j
< i
; j
++)
743 DSS_DEBUG_LOG("argv[%d] = %s\n", j
, (*argv
)[j
]);
746 void free_rsync_argv(char **argv
)
749 for (i
= 0; argv
[i
]; i
++)
754 int create_snapshot(char **argv
)
756 int fds
[3] = {0, 0, 0};
757 char *name
= incomplete_name(current_snapshot_creation_time
);
759 DSS_NOTICE_LOG("creating new snapshot %s\n", name
);
761 return dss_exec(&rsync_pid
, argv
[0], argv
, fds
);
764 void compute_next_snapshot_time(struct snapshot_list
*sl
)
766 struct timeval now
, unit_interval
= {.tv_sec
= 24 * 3600 * conf
.unit_interval_arg
},
769 unsigned wanted
= num_snapshots(0), num_complete_snapshots
= 0;
773 gettimeofday(&now
, NULL
);
774 FOR_EACH_SNAPSHOT(s
, i
, sl
) {
775 if (!(s
->flags
& SS_COMPLETE
))
777 num_complete_snapshots
++;
778 x
+= s
->completion_time
- s
->creation_time
;
781 if (num_complete_snapshots
)
782 x
/= num_complete_snapshots
; /* avg time to create one snapshot */
783 x
*= wanted
; /* time to create all snapshots in interval 0 */
786 ret
= tv_diff(&unit_interval
, &tmp
, &diff
); /* time between creation */
788 next_snapshot_time
= now
;
791 tv_divide(wanted
, &diff
, &tmp
);
792 tv_add(&now
, &tmp
, &next_snapshot_time
);
795 void handle_signal(void)
797 int sig
, ret
= next_signal();
807 restart_rsync_process();
808 kill_process(rsync_pid
);
809 kill_process(rm_pid
);
816 ret
= reap_child(&pid
, &status
);
819 assert(pid
== rsync_pid
|| pid
== rm_pid
);
820 if (pid
== rsync_pid
)
821 ret
= handle_rsync_exit(status
);
823 ret
= handle_rm_exit(status
);
827 log_err_msg(ERROR
, -ret
);
830 int get_oldest(const char *dirname
, void *private)
832 struct edge_snapshot_data
*esd
= private;
834 int ret
= is_snapshot(dirname
, esd
->now
, &s
);
838 if (s
.creation_time
> esd
->snap
.creation_time
)
840 free(esd
->snap
.name
);
845 int remove_oldest_snapshot()
848 struct edge_snapshot_data esd
= {
849 .now
= get_current_time(),
850 .snap
= {.creation_time
= LLONG_MAX
}
852 for_each_subdir(get_oldest
, &esd
);
853 if (!esd
.snap
.name
) /* no snapshot found */
855 DSS_INFO_LOG("oldest snapshot: %s\n", esd
.snap
.name
);
857 if (esd
.snap
.creation_time
== current_snapshot_creation_time
)
858 goto out
; /* do not remove the snapshot currently being created */
859 ret
= remove_snapshot(&esd
.snap
);
865 /* TODO: Also consider number of inodes. */
866 int disk_space_low(void)
868 struct disk_space ds
;
869 int ret
= get_disk_space(".", &ds
);
873 if (conf
.min_free_mb_arg
)
874 if (ds
.free_mb
< conf
.min_free_mb_arg
)
876 if (conf
.min_free_percent_arg
)
877 if (ds
.percent_free
< conf
.min_free_percent_arg
)
882 int try_to_free_disk_space(int low_disk_space
, struct snapshot_list
*sl
)
886 ret
= remove_outdated_snapshot(sl
);
887 if (ret
) /* error, or we are removing something */
889 /* no outdated snapshot */
890 ret
= remove_redundant_snapshot(sl
);
895 DSS_WARNING_LOG("disk space low and nothing obvious to remove\n");
896 ret
= remove_oldest_snapshot();
899 make_err_msg("uhuhu: not enough disk space for a single snapshot");
903 int select_loop(void)
906 struct timeval tv
= {.tv_sec
= 0, .tv_usec
= 0};
907 struct snapshot_list sl
= {.num_snapshots
= 0};
910 struct timeval now
, *tvp
= &tv
;
915 free_snapshot_list(&sl
);
916 get_snapshot_list(&sl
);
917 compute_next_snapshot_time(&sl
);
919 FD_SET(signal_pipe
, &rfds
);
924 ret
= dss_select(signal_pipe
+ 1, &rfds
, NULL
, tvp
);
927 if (FD_ISSET(signal_pipe
, &rfds
)) {
933 ret
= disk_space_low();
936 low_disk_space
= ret
;
938 stop_rsync_process();
939 ret
= try_to_free_disk_space(low_disk_space
, &sl
);
945 restart_rsync_process();
948 /* neither rsync nor rm are running. Start rsync? */
949 gettimeofday(&now
, NULL
);
950 if (tv_diff(&next_snapshot_time
, &now
, &tv
) > 0)
952 create_rsync_argv(&rsync_argv
, ¤t_snapshot_creation_time
);
953 ret
= create_snapshot(rsync_argv
);
954 free_rsync_argv(rsync_argv
);
958 free_snapshot_list(&sl
);
966 if (conf
.dry_run_given
) {
967 make_err_msg("dry_run not supported by this command");
970 ret
= install_sighandler(SIGHUP
);
973 return select_loop();
976 void log_disk_space(struct disk_space
*ds
)
978 DSS_INFO_LOG("free: %uM/%uM (%u%%), %u%% inodes unused\n",
979 ds
->free_mb
, ds
->total_mb
, ds
->percent_free
,
980 ds
->percent_free_inodes
);
986 struct snapshot_list sl
;
987 struct disk_space ds
;
989 ret
= get_disk_space(".", &ds
);
994 get_snapshot_list(&sl
);
995 ret
= remove_outdated_snapshot(&sl
);
996 free_snapshot_list(&sl
);
1001 ret
= wait_for_rm_process();
1006 get_snapshot_list(&sl
);
1007 ret
= remove_redundant_snapshot(&sl
);
1008 free_snapshot_list(&sl
);
1013 ret
= wait_for_rm_process();
1022 int com_create(void)
1027 create_rsync_argv(&rsync_argv
, ¤t_snapshot_creation_time
);
1028 if (conf
.dry_run_given
) {
1031 for (i
= 0; rsync_argv
[i
]; i
++) {
1033 msg
= make_message("%s%s%s", tmp
? tmp
: "",
1034 tmp
? " " : "", rsync_argv
[i
]);
1037 dss_msg("%s\n", msg
);
1041 ret
= create_snapshot(rsync_argv
);
1044 ret
= wait_for_process(rsync_pid
, &status
);
1047 ret
= handle_rsync_exit(status
);
1049 free_rsync_argv(rsync_argv
);
1056 struct snapshot_list sl
;
1058 get_snapshot_list(&sl
);
1059 FOR_EACH_SNAPSHOT(s
, i
, &sl
)
1060 dss_msg("%u\t%s\n", s
->interval
, s
->name
);
1061 free_snapshot_list(&sl
);
1065 __noreturn
void clean_exit(int status
)
1067 free(dss_error_txt
);
1070 static void setup_signal_handling(void)
1074 DSS_INFO_LOG("setting up signal handlers\n");
1075 signal_pipe
= signal_init(); /* always successful */
1076 ret
= install_sighandler(SIGINT
);
1079 ret
= install_sighandler(SIGTERM
);
1082 ret
= install_sighandler(SIGCHLD
);
1087 DSS_EMERG_LOG("could not install signal handlers\n");
1091 int main(int argc
, char **argv
)
1094 struct cmdline_parser_params params
= {
1097 .check_required
= 0,
1098 .check_ambiguity
= 0
1101 cmdline_parser_ext(argc
, argv
, &conf
, ¶ms
); /* aborts on errors */
1102 parse_config_file(0);
1104 if (conf
.daemon_given
)
1106 setup_signal_handling();
1107 ret
= call_command_handler();
1109 log_err_msg(EMERG
, -ret
);
1110 clean_exit(ret
>= 0? EXIT_SUCCESS
: EXIT_FAILURE
);