X-Git-Url: http://git.tuebingen.mpg.de/?p=dss.git;a=blobdiff_plain;f=dss.c;h=7c8b54fc6a0502d0a2ab983b5bc8ff3d33e0bda2;hp=68efc2b07cc457c4944013b6e3147fdc3f2adce1;hb=9528cf1dad242f2f41757fb1c7cb93acdba2370d;hpb=d28be18d969b6e72021e7229be49c02d3b549794 diff --git a/dss.c b/dss.c index 68efc2b..7c8b54f 100644 --- a/dss.c +++ b/dss.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Andre Noll + * Copyright (C) 2008-2011 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -64,7 +64,106 @@ enum hook_status snapshot_removal_status; DEFINE_DSS_ERRLIST; +static const char *hook_status_description[] = {HOOK_STATUS_ARRAY}; +/* may be called with ds == NULL. */ +static int disk_space_low(struct disk_space *ds) +{ + struct disk_space ds_struct; + + if (!ds) { + int ret = get_disk_space(".", &ds_struct); + if (ret < 0) + return ret; + ds = &ds_struct; + } + if (conf.min_free_mb_arg) + if (ds->free_mb < conf.min_free_mb_arg) + return 1; + if (conf.min_free_percent_arg) + if (ds->percent_free < conf.min_free_percent_arg) + return 1; + if (conf.min_free_percent_inodes_arg) + if (ds->percent_free_inodes < conf.min_free_percent_inodes_arg) + return 1; + return 0; +} + +static void dump_dss_config(const char *msg) +{ + const char dash[] = "-----------------------------"; + int ret; + FILE *log = logfile? logfile : stderr; + struct disk_space ds; + int64_t now = get_current_time(); + + if (conf.loglevel_arg > INFO) + return; + + fprintf(log, "%s <%s config> %s\n", dash, msg, dash); + fprintf(log, "\n*** disk space ***\n\n"); + ret = get_disk_space(".", &ds); + if (ret >= 0) { + DSS_INFO_LOG("disk space low: %s\n", disk_space_low(&ds)? + "yes" : "no"); + log_disk_space(&ds); + } else + DSS_ERROR_LOG("can not get free disk space: %s\n", + dss_strerror(-ret)); + + /* we continue on errors from get_disk_space */ + + fprintf(log, "\n*** command line and config file options ***\n\n"); + cmdline_parser_dump(log, &conf); + fprintf(log, "\n*** internal state ***\n\n"); + fprintf(log, + "pid: %d\n" + "logile: %s\n" + "snapshot_currently_being_removed: %s\n" + "path_to_last_complete_snapshot: %s\n" + "reference_snapshot: %s\n" + "snapshot_creation_status: %s\n" + "snapshot_removal_status: %s\n" + , + (int) getpid(), + logfile? conf.logfile_arg : "stderr", + snapshot_currently_being_removed? + snapshot_currently_being_removed->name : "(none)", + path_to_last_complete_snapshot? + path_to_last_complete_snapshot : "(none)", + name_of_reference_snapshot? + name_of_reference_snapshot : "(none)", + hook_status_description[snapshot_creation_status], + hook_status_description[snapshot_removal_status] + ); + if (create_pid != 0) + fprintf(log, + "create_pid: %" PRId32 "\n" + "create process is %sstopped\n" + , + create_pid, + create_process_stopped? "" : "not " + ); + if (remove_pid != 0) + fprintf(log, "remove_pid: %" PRId32 "\n", remove_pid); + if (next_snapshot_time != 0) + fprintf(log, "next snapshot due in %" PRId64 " seconds\n", + next_snapshot_time - now); + if (current_snapshot_creation_time != 0) + fprintf(log, "current_snapshot_creation_time: %" + PRId64 " (%" PRId64 " seconds ago)\n", + current_snapshot_creation_time, + now - current_snapshot_creation_time + ); + if (next_removal_check.tv_sec != 0) { + fprintf(log, "next removal check: %llu (%llu seconds ago)\n", + (long long unsigned)next_removal_check.tv_sec, + now - (long long unsigned)next_removal_check.tv_sec + ); + + } + fprintf(log, "%s %s\n", dash, msg, dash); +} /* a litte cpp magic helps to DRY */ #define COMMANDS \ @@ -127,25 +226,6 @@ static __printf_1_2 void dss_msg(const char* fmt,...) va_end(argp); } -static int disk_space_low(void) -{ - struct disk_space ds; - int ret = get_disk_space(".", &ds); - - if (ret < 0) - return ret; - if (conf.min_free_mb_arg) - if (ds.free_mb < conf.min_free_mb_arg) - return 1; - if (conf.min_free_percent_arg) - if (ds.percent_free < conf.min_free_percent_arg) - return 1; - if (conf.min_free_percent_inodes_arg) - if (ds.percent_free_inodes < conf.min_free_percent_inodes_arg) - return 1; - return 0; -} - static void dss_get_snapshot_list(struct snapshot_list *sl) { get_snapshot_list(sl, conf.unit_interval_arg, conf.num_intervals_arg); @@ -207,33 +287,22 @@ static int next_snapshot_is_due(void) return 0; } -static int pre_create_hook(void) +static void pre_create_hook(void) { - int ret, fds[3] = {0, 0, 0}; - assert(snapshot_creation_status == HS_READY); /* make sure that the next snapshot time will be recomputed */ invalidate_next_snapshot_time(); - if (!conf.pre_create_hook_given) { - snapshot_creation_status = HS_PRE_SUCCESS; - return 0; - } DSS_DEBUG_LOG("executing %s\n", conf.pre_create_hook_arg); - ret = dss_exec_cmdline_pid(&create_pid, - conf.pre_create_hook_arg, fds); - if (ret < 0) - return ret; + dss_exec_cmdline_pid(&create_pid, conf.pre_create_hook_arg); snapshot_creation_status = HS_PRE_RUNNING; - return ret; } -static int pre_remove_hook(struct snapshot *s, const char *why) +static void pre_remove_hook(struct snapshot *s, const char *why) { - int ret, fds[3] = {0, 0, 0}; char *cmd; if (!s) - return 0; + return; DSS_DEBUG_LOG("%s snapshot %s\n", why, s->name); assert(snapshot_removal_status == HS_READY); assert(remove_pid == 0); @@ -243,30 +312,26 @@ static int pre_remove_hook(struct snapshot *s, const char *why) *snapshot_currently_being_removed = *s; snapshot_currently_being_removed->name = dss_strdup(s->name); - if (!conf.pre_remove_hook_given) { - snapshot_removal_status = HS_PRE_SUCCESS; - return 0; - } cmd = make_message("%s %s/%s", conf.pre_remove_hook_arg, conf.dest_dir_arg, s->name); DSS_DEBUG_LOG("executing %s\n", cmd); - ret = dss_exec_cmdline_pid(&remove_pid, - conf.pre_remove_hook_arg, fds); + dss_exec_cmdline_pid(&remove_pid, cmd); free(cmd); - if (ret < 0) - return ret; snapshot_removal_status = HS_PRE_RUNNING; - return ret; } static int exec_rm(void) { struct snapshot *s = snapshot_currently_being_removed; - int fds[3] = {0, 0, 0}; char *new_name = being_deleted_name(s); - char *argv[] = {"rm", "-rf", new_name, NULL}; + char *argv[4]; int ret; + argv[0] = "rm"; + argv[1] = "-rf"; + argv[2] = new_name; + argv[3] = NULL; + assert(snapshot_removal_status == HS_PRE_SUCCESS); assert(remove_pid == 0); @@ -274,9 +339,7 @@ static int exec_rm(void) ret = dss_rename(s->name, new_name); if (ret < 0) goto out; - ret = dss_exec(&remove_pid, argv[0], argv, fds); - if (ret < 0) - goto out; + dss_exec(&remove_pid, argv[0], argv); snapshot_removal_status = HS_RUNNING; out: free(new_name); @@ -450,7 +513,7 @@ static int try_to_free_disk_space(void) const char *why; int low_disk_space; - ret = disk_space_low(); + ret = disk_space_low(NULL); if (ret < 0) return ret; low_disk_space = ret; @@ -492,77 +555,79 @@ static int try_to_free_disk_space(void) ret = -ERRNO_TO_DSS_ERROR(ENOSPC); goto out; remove: - ret = pre_remove_hook(victim, why); + pre_remove_hook(victim, why); out: free_snapshot_list(&sl); return ret; } -static int post_create_hook(void) +static void post_create_hook(void) { - int ret, fds[3] = {0, 0, 0}; - char *cmd; - - if (!conf.post_create_hook_given) { - create_pid = 0; - snapshot_creation_status = HS_READY; - return 0; - } - cmd = make_message("%s %s/%s", conf.post_create_hook_arg, + char *cmd = make_message("%s %s/%s", conf.post_create_hook_arg, conf.dest_dir_arg, path_to_last_complete_snapshot); DSS_NOTICE_LOG("executing %s\n", cmd); - ret = dss_exec_cmdline_pid(&create_pid, cmd, fds); + dss_exec_cmdline_pid(&create_pid, cmd); free(cmd); - if (ret < 0) - return ret; snapshot_creation_status = HS_POST_RUNNING; - return ret; } -static int post_remove_hook(void) +static void post_remove_hook(void) { - int ret, fds[3] = {0, 0, 0}; char *cmd; struct snapshot *s = snapshot_currently_being_removed; assert(s); - if (!conf.post_remove_hook_given) { - snapshot_removal_status = HS_READY; - return 0; - } cmd = make_message("%s %s/%s", conf.post_remove_hook_arg, conf.dest_dir_arg, s->name); DSS_NOTICE_LOG("executing %s\n", cmd); - ret = dss_exec_cmdline_pid(&remove_pid, cmd, fds); + dss_exec_cmdline_pid(&remove_pid, cmd); free(cmd); - if (ret < 0) - return ret; snapshot_removal_status = HS_POST_RUNNING; - return ret; } -static void kill_process(pid_t pid) +static void dss_kill(pid_t pid, int sig, const char *msg) { - if (!pid) + const char *signame, *process_name; + + if (pid == 0) + return; + switch (sig) { + case SIGTERM: signame = "TERM"; break; + case SIGSTOP: signame = "STOP"; break; + case SIGCONT: signame = "CONT"; break; + default: signame = "????"; + } + + if (pid == create_pid) + process_name = "create"; + else if (pid == remove_pid) + process_name = "remove"; + else process_name = "??????"; + + if (msg) + DSS_INFO_LOG("%s\n", msg); + DSS_DEBUG_LOG("sending signal %d (%s) to pid %d (%s process)\n", + sig, signame, (int)pid, process_name); + if (kill(pid, sig) >= 0) return; - DSS_WARNING_LOG("sending SIGTERM to pid %d\n", (int)pid); - kill(pid, SIGTERM); + DSS_INFO_LOG("failed to send signal %d (%s) to pid %d (%s process)\n", + sig, signame, (int)pid, process_name); } static void stop_create_process(void) { - if (!create_pid || create_process_stopped) + if (create_process_stopped) return; - kill(SIGSTOP, create_pid); + dss_kill(create_pid, SIGSTOP, "suspending create process"); create_process_stopped = 1; } static void restart_create_process(void) { - if (!create_pid || !create_process_stopped) + if (!create_process_stopped) return; - kill (SIGCONT, create_pid); + dss_kill(create_pid, SIGCONT, "resuming create process"); create_process_stopped = 0; } @@ -607,8 +672,7 @@ static int wait_for_process(pid_t pid, int *status) } } /* SIGINT or SIGTERM */ - DSS_WARNING_LOG("sending SIGTERM to pid %d\n", (int)pid); - kill(pid, SIGTERM); + dss_kill(pid, SIGTERM, "killing child process"); } if (ret < 0) DSS_ERROR_LOG("failed to wait for process %d\n", (int)pid); @@ -638,10 +702,7 @@ static int handle_rm_exit(int status) snapshot_removal_status = HS_READY; return -E_BAD_EXIT_CODE; } - if (conf.post_remove_hook_given) - snapshot_removal_status = HS_SUCCESS; - else - snapshot_removal_status = HS_READY; + snapshot_removal_status = HS_SUCCESS; return 1; } @@ -778,18 +839,22 @@ static int handle_sigchld(void) if (pid == create_pid) { switch (snapshot_creation_status) { case HS_PRE_RUNNING: - return handle_pre_create_hook_exit(status); + ret = handle_pre_create_hook_exit(status); + break; case HS_RUNNING: - return handle_rsync_exit(status); + ret = handle_rsync_exit(status); + break; case HS_POST_RUNNING: snapshot_creation_status = HS_READY; - return 1; + ret = 1; + break; default: DSS_EMERG_LOG("BUG: create can't die in status %d\n", snapshot_creation_status); return -E_BUG; } create_pid = 0; + return ret; } if (pid == remove_pid) { ret = handle_remove_exit(status); @@ -881,7 +946,7 @@ static int parse_config_file(int override) conf.logfile_given = 1; } } - if (conf.logfile_given) { + if (conf.logfile_given && conf.run_given && conf.daemon_given) { logfile = open_log(conf.logfile_arg); log_welcome(conf.loglevel_arg); } @@ -900,24 +965,16 @@ static int change_to_dest_dir(void) return dss_chdir(conf.dest_dir_arg); } -static void dump_dss_config(const char *msg) -{ - if (conf.loglevel_arg > INFO) - return; - DSS_INFO_LOG("%s\n", msg); - cmdline_parser_dump(logfile? logfile : stderr, &conf); -} - static int handle_sighup(void) { int ret; DSS_NOTICE_LOG("SIGHUP, re-reading config\n"); - dump_dss_config("current config"); + dump_dss_config("old"); ret = parse_config_file(1); if (ret < 0) return ret; - dump_dss_config("new config"); + dump_dss_config("reloaded"); invalidate_next_snapshot_time(); return change_to_dest_dir(); } @@ -933,8 +990,8 @@ static int handle_signal(void) case SIGINT: case SIGTERM: restart_create_process(); - kill_process(create_pid); - kill_process(remove_pid); + dss_kill(create_pid, SIGTERM, NULL); + dss_kill(remove_pid, SIGTERM, NULL); ret = -E_SIGNAL; break; case SIGHUP: @@ -968,22 +1025,48 @@ static int use_rsync_locally(char *logname) static int rename_resume_snap(int64_t creation_time) { struct snapshot_list sl = {.num_snapshots = 0}; - struct snapshot *s; + struct snapshot *s = NULL; char *new_name = incomplete_name(creation_time); int ret; + const char *why; ret = 0; if (conf.no_resume_given) goto out; dss_get_snapshot_list(&sl); + /* + * Snapshot recycling: We first look at the newest snapshot. If this + * snapshot happens to be incomplete, the last rsync process was + * aborted and we reuse this one. Otherwise we look at snapshots which + * could be removed (outdated and redundant snapshots) as candidates + * for recycling. If no outdated/redundant snapshot exists, we check if + * there is an orphaned snapshot, which likely is useless anyway. + * + * Only if no existing snapshot is suitable for recycling, we bite the + * bullet and create a new one. + */ s = get_newest_snapshot(&sl); - if (!s) + if (!s) /* no snapshots at all */ goto out; - if ((s->flags & SS_COMPLETE) != 0) /* complete */ + /* re-use last snapshot if it is incomplete */ + why = "aborted"; + if ((s->flags & SS_COMPLETE) == 0) goto out; - DSS_INFO_LOG("resuming: reusing %s as destination dir\n", s->name); - ret = dss_rename(s->name, new_name); + why = "outdated"; + s = find_outdated_snapshot(&sl); + if (s) + goto out; + why = "redundant"; + s = find_redundant_snapshot(&sl); + if (s) + goto out; + why = "orphaned"; + s = find_orphaned_snapshot(&sl); out: + if (s) { + DSS_INFO_LOG("reusing %s snapshot %s\n", why, s->name); + ret = dss_rename(s->name, new_name); + } if (ret >= 0) DSS_NOTICE_LOG("creating new snapshot %s\n", new_name); free(new_name); @@ -1042,14 +1125,12 @@ static void free_rsync_argv(char **argv) static int create_snapshot(char **argv) { - int ret, fds[3] = {0, 0, 0}; + int ret; ret = rename_resume_snap(current_snapshot_creation_time); if (ret < 0) return ret; - ret = dss_exec(&create_pid, argv[0], argv, fds); - if (ret < 0) - return ret; + dss_exec(&create_pid, argv[0], argv); snapshot_creation_status = HS_RUNNING; return ret; } @@ -1074,7 +1155,6 @@ static int select_loop(void) } FD_ZERO(&rfds); FD_SET(signal_pipe, &rfds); - DSS_DEBUG_LOG("tvp: %p, tv_sec : %lu\n", tvp, (long unsigned) tv.tv_sec); ret = dss_select(signal_pipe + 1, &rfds, NULL, tvp); if (ret < 0) goto out; @@ -1092,9 +1172,7 @@ static int select_loop(void) continue; } if (snapshot_removal_status == HS_SUCCESS) { - ret = post_remove_hook(); - if (ret < 0) - goto out; + post_remove_hook(); continue; } ret = try_to_free_disk_space(); @@ -1109,9 +1187,7 @@ static int select_loop(void) case HS_READY: if (!next_snapshot_is_due()) continue; - ret = pre_create_hook(); - if (ret < 0) - goto out; + pre_create_hook(); continue; case HS_PRE_RUNNING: case HS_RUNNING: @@ -1122,7 +1198,10 @@ static int select_loop(void) free_rsync_argv(rsync_argv); create_rsync_argv(&rsync_argv, ¤t_snapshot_creation_time); } - /* fall through */ + ret = create_snapshot(rsync_argv); + if (ret < 0) + goto out; + continue; case HS_NEEDS_RESTART: if (!next_snapshot_is_due()) continue; @@ -1131,9 +1210,7 @@ static int select_loop(void) goto out; continue; case HS_SUCCESS: - ret = post_create_hook(); - if (ret < 0) - goto out; + post_create_hook(); continue; } } @@ -1143,14 +1220,15 @@ out: static void exit_hook(int exit_code) { - int fds[3] = {0, 0, 0}; - char *argv[] = {conf.exit_hook_arg, dss_strerror(-exit_code), NULL}; + char *argv[3]; pid_t pid; - if (!conf.exit_hook_given) - return; + argv[0] = conf.exit_hook_arg; + argv[1] = dss_strerror(-exit_code); + argv[2] = NULL; + DSS_NOTICE_LOG("executing %s %s\n", argv[0], argv[1]); - dss_exec(&pid, conf.exit_hook_arg, argv, fds); + dss_exec(&pid, conf.exit_hook_arg, argv); } static int com_run(void) @@ -1201,9 +1279,7 @@ rm: ret = 0; goto out; } - ret = pre_remove_hook(victim, why); - if (ret < 0) - goto out; + pre_remove_hook(victim, why); if (snapshot_removal_status == HS_PRE_RUNNING) { ret = wait_for_remove_process(); if (ret < 0) @@ -1219,9 +1295,7 @@ rm: goto out; if (snapshot_removal_status != HS_SUCCESS) goto out; - ret = post_remove_hook(); - if (ret < 0) - goto out; + post_remove_hook(); if (snapshot_removal_status != HS_POST_RUNNING) goto out; ret = wait_for_remove_process(); @@ -1253,9 +1327,7 @@ static int com_create(void) free(msg); return 1; } - ret = pre_create_hook(); - if (ret < 0) - return ret; + pre_create_hook(); if (create_pid) { ret = wait_for_process(create_pid, &status); if (ret < 0) @@ -1351,10 +1423,10 @@ int main(int argc, char **argv) } if (conf.daemon_given) daemon_init(); - dump_dss_config("dss configuration"); ret = change_to_dest_dir(); if (ret < 0) goto out; + dump_dss_config("startup"); ret = setup_signal_handling(); if (ret < 0) goto out;