struct gengetopt_args_info conf;
-char *dss_error_txt = NULL;
static FILE *logfile;
static int signal_pipe;
static pid_t rm_pid;
/** When the next snapshot is due. */
struct timeval next_snapshot_time;
-
+/** The pid of the pre-create hook. */
pid_t pre_create_hook_pid;
+/** The pid of the post-create hook. */
pid_t post_create_hook_pid;
/* Creation time of the snapshot currently being created. */
static char *path_to_last_complete_snapshot;
enum {
+ /** We are ready to take the next snapshot. */
SCS_READY,
+ /** The pre-creation hook has been started. */
SCS_PRE_HOOK_RUNNING,
+ /** The pre-creation hook exited successfully. */
SCS_PRE_HOOK_SUCCESS,
+ /** The rsync process is running. */
SCS_RSYNC_RUNNING,
+ /** The rsync process exited successfully. */
SCS_RSYNC_SUCCESS,
+ /** The post-create hook has been started- */
SCS_POST_HOOK_RUNNING,
};
unsigned interval;
};
-/*
- * An edge snapshot is either the oldest one or the newest one.
- *
- * We need to find either of them occasionally: The create code
- * needs to know the newest snapshot because that is the one
- * used as the link destination dir. The pruning code needs to
- * find the oldest one in case disk space becomes low.
- */
-struct edge_snapshot_data {
- int64_t now;
- struct snapshot snap;
-};
-
__printf_2_3 void dss_log(int ll, const char* fmt,...)
{
va_list argp;
tmp[i] = '\0';
ret = dss_atoi64(tmp, &num);
free(tmp);
- if (ret < 0) {
- free(dss_error_txt);
+ if (ret < 0)
return 0;
- }
assert(num >= 0);
if (num > now)
return 0;
tmp[i] = '\0';
ret = dss_atoi64(tmp, &num);
free(tmp);
- if (ret < 0) {
- free(dss_error_txt);
+ if (ret < 0)
return 0;
- }
if (num > now)
return 0;
s->completion_time = num;
time_t *end_seconds = (time_t *) (uint64_t *)&end; /* STFU, gcc */
char start_str[200], end_str[200];
- if (!localtime_r(start_seconds, &start_tm)) {
- make_err_msg("%lli", (long long)start);
+ if (!localtime_r(start_seconds, &start_tm))
return -E_LOCALTIME;
- }
- if (!localtime_r(end_seconds, &end_tm)) {
- make_err_msg("%lli", (long long)end);
+ if (!localtime_r(end_seconds, &end_tm))
return -E_LOCALTIME;
- }
- if (!strftime(start_str, sizeof(start_str), "%a_%b_%d_%Y_%H_%M_%S", &start_tm)) {
- make_err_msg("%lli", (long long)start);
+ if (!strftime(start_str, sizeof(start_str), "%a_%b_%d_%Y_%H_%M_%S", &start_tm))
return -E_STRFTIME;
- }
- if (!strftime(end_str, sizeof(end_str), "%a_%b_%d_%Y_%H_%M_%S", &end_tm)) {
- make_err_msg("%lli", (long long)end);
+ if (!strftime(end_str, sizeof(end_str), "%a_%b_%d_%Y_%H_%M_%S", &end_tm))
return -E_STRFTIME;
- }
*result = make_message("%lli-%lli.%s-%s", (long long) start, (long long) end,
start_str, end_str);
return 1;
#define FOR_EACH_SNAPSHOT(s, i, sl) \
for ((i) = 0; (i) < (sl)->num_snapshots && ((s) = (sl)->snapshots[(i)]); (i)++)
+#define FOR_EACH_SNAPSHOT_REVERSE(s, i, sl) \
+ for ((i) = (sl)->num_snapshots; (i) > 0 && ((s) = (sl)->snapshots[(i - 1)]); (i)--)
+static inline struct snapshot *oldest_snapshot(struct snapshot_list *sl)
+{
+ if (!sl->num_snapshots)
+ return NULL;
+ return sl->snapshots[0];
+}
#define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
kill(pid, SIGTERM);
}
if (ret < 0)
- make_err_msg("failed to wait for process %d", (int)pid);
+ DSS_ERROR_LOG("failed to wait for process %d\n", (int)pid);
else
log_termination_msg(pid, *status);
return ret;
int handle_rm_exit(int status)
{
- int es, ret;
-
- if (!WIFEXITED(status)) {
- make_err_msg("rm process %d died involuntary", (int)rm_pid);
- ret = -E_INVOLUNTARY_EXIT;
- goto out;
- }
- es = WEXITSTATUS(status);
- if (es) {
- make_err_msg("rm process %d returned %d", (int)rm_pid, es);
- ret = -E_BAD_EXIT_CODE;
- goto out;
- }
- ret = 1;
rm_pid = 0;
-out:
- return ret;
+ if (!WIFEXITED(status))
+ return -E_INVOLUNTARY_EXIT;
+ if (WEXITSTATUS(status))
+ return -E_BAD_EXIT_CODE;
+ return 1;
}
int wait_for_rm_process(void)
int check_config(void)
{
if (conf.unit_interval_arg <= 0) {
- make_err_msg("bad unit interval: %i", conf.unit_interval_arg);
+ DSS_ERROR_LOG("bad unit interval: %i\n", conf.unit_interval_arg);
return -E_INVALID_NUMBER;
}
DSS_DEBUG_LOG("unit interval: %i day(s)\n", conf.unit_interval_arg);
if (conf.num_intervals_arg <= 0) {
- make_err_msg("bad number of intervals %i", conf.num_intervals_arg);
+ DSS_ERROR_LOG("bad number of intervals %i\n", conf.num_intervals_arg);
return -E_INVALID_NUMBER;
}
DSS_DEBUG_LOG("number of intervals: %i\n", conf.num_intervals_arg);
ret = stat(config_file, &statbuf);
if (ret && conf.config_file_given) {
ret = -ERRNO_TO_DSS_ERROR(errno);
- make_err_msg("failed to stat config file %s", config_file);
+ DSS_ERROR_LOG("failed to stat config file %s\n", config_file);
goto out;
}
if (!ret) {
free(config_file);
if (ret >= 0)
return;
- log_err_msg(EMERG, -ret);
+ DSS_EMERG_LOG("%s\n", dss_strerror(-ret));
exit(EXIT_FAILURE);
}
int es, ret;
if (!WIFEXITED(status)) {
- make_err_msg("rsync process %d died involuntary", (int)rsync_pid);
+ DSS_ERROR_LOG("rsync process %d died involuntary\n", (int)rsync_pid);
ret = -E_INVOLUNTARY_EXIT;
snapshot_creation_status = SCS_READY;
compute_next_snapshot_time();
}
es = WEXITSTATUS(status);
if (es != 0 && es != 23 && es != 24) {
- make_err_msg("rsync process %d returned %d", (int)rsync_pid, es);
+ DSS_ERROR_LOG("rsync process %d returned %d\n", (int)rsync_pid, es);
ret = -E_BAD_EXIT_CODE;
snapshot_creation_status = SCS_READY;
compute_next_snapshot_time();
return ret;
}
-int get_newest_complete(const char *dirname, void *private)
+__malloc char *name_of_newest_complete_snapshot(void)
{
- struct edge_snapshot_data *esd = private;
- struct snapshot s;
- int ret = is_snapshot(dirname, esd->now, &s);
+ struct snapshot_list sl;
+ struct snapshot *s;
+ int i;
+ char *name = NULL;
- if (ret <= 0)
- return 1;
- if (s.flags != SS_COMPLETE) /* incomplete or being deleted */
- return 1;
- if (s.creation_time < esd->snap.creation_time)
- return 1;
- free(esd->snap.name);
- esd->snap = s;
- return 1;
-}
+ get_snapshot_list(&sl);
-__malloc char *name_of_newest_complete_snapshot(void)
-{
- struct edge_snapshot_data esd = {
- .now = get_current_time(),
- .snap = {.creation_time = -1}
- };
- for_each_subdir(get_newest_complete, &esd);
- return esd.snap.name;
+ FOR_EACH_SNAPSHOT_REVERSE(s, i, &sl) {
+ if (s->flags != SS_COMPLETE) /* incomplete or being deleted */
+ continue;
+ name = dss_strdup(s->name);
+ break;
+ }
+ free_snapshot_list(&sl);
+ return name;
}
void create_rsync_argv(char ***argv, int64_t *num)
int es, ret;
if (!WIFEXITED(status)) {
- make_err_msg("pre-create-hook %d died involuntary",
- (int)pre_create_hook_pid);
snapshot_creation_status = SCS_READY;
compute_next_snapshot_time();
ret = -E_INVOLUNTARY_EXIT;
}
es = WEXITSTATUS(status);
if (es) {
- make_err_msg("pre-create-hook %d returned %d",
- (int)pre_create_hook_pid, es);
snapshot_creation_status = SCS_READY;
compute_next_snapshot_time();
ret = -E_BAD_EXIT_CODE;
}
out:
if (ret < 0)
- log_err_msg(ERROR, -ret);
+ DSS_ERROR_LOG("%s\n", dss_strerror(-ret));
}
-int get_oldest(const char *dirname, void *private)
+int remove_oldest_snapshot(struct snapshot_list *sl)
{
- struct edge_snapshot_data *esd = private;
- struct snapshot s;
- int ret = is_snapshot(dirname, esd->now, &s);
+ struct snapshot *s = oldest_snapshot(sl);
- if (ret <= 0)
- return 1;
- if (s.creation_time > esd->snap.creation_time)
- return 1;
- free(esd->snap.name);
- esd->snap = s;
- return 1;
-}
-
-int remove_oldest_snapshot()
-{
- int ret;
- struct edge_snapshot_data esd = {
- .now = get_current_time(),
- .snap = {.creation_time = LLONG_MAX}
- };
- for_each_subdir(get_oldest, &esd);
- if (!esd.snap.name) /* no snapshot found */
+ if (!s) /* no snapshot found */
return 0;
- DSS_INFO_LOG("oldest snapshot: %s\n", esd.snap.name);
- ret = 0;
- if (esd.snap.creation_time == current_snapshot_creation_time)
- goto out; /* do not remove the snapshot currently being created */
- ret = remove_snapshot(&esd.snap);
-out:
- free(esd.snap.name);
- return ret;
+ DSS_INFO_LOG("oldest snapshot: %s\n", s->name);
+ if (s->creation_time == current_snapshot_creation_time)
+ return 0; /* do not remove the snapshot currently being created */
+ return remove_snapshot(s);
}
/* TODO: Also consider number of inodes. */
if (!low_disk_space)
goto out;
DSS_WARNING_LOG("disk space low and nothing obvious to remove\n");
- ret = remove_oldest_snapshot();
+ ret = remove_oldest_snapshot(&sl);
if (ret)
goto out;
- make_err_msg("uhuhu: not enough disk space for a single snapshot");
+ DSS_CRIT_LOG("uhuhu: not enough disk space for a single snapshot\n");
ret= -ENOSPC;
out:
free_snapshot_list(&sl);
int ret;
if (conf.dry_run_given) {
- make_err_msg("dry_run not supported by this command");
+ DSS_ERROR_LOG("dry_run not supported by this command\n");
return -E_SYNTAX;
}
ret = install_sighandler(SIGHUP);
return select_loop();
}
-void log_disk_space(struct disk_space *ds)
-{
- DSS_INFO_LOG("free: %uM/%uM (%u%%), %u%% inodes unused\n",
- ds->free_mb, ds->total_mb, ds->percent_free,
- ds->percent_free_inodes);
-}
-
int com_prune(void)
{
int ret;
return 1;
}
-__noreturn void clean_exit(int status)
-{
- free(dss_error_txt);
- exit(status);
-}
static void setup_signal_handling(void)
{
int ret;
setup_signal_handling();
ret = call_command_handler();
if (ret < 0)
- log_err_msg(EMERG, -ret);
- clean_exit(ret >= 0? EXIT_SUCCESS : EXIT_FAILURE);
+ DSS_EMERG_LOG("%s\n", dss_strerror(-ret));
+ exit(ret >= 0? EXIT_SUCCESS : EXIT_FAILURE);
}