+int post_create_hook(void)
+{
+ int ret, fds[3] = {0, 0, 0};
+ char *cmd;
+
+ if (!conf.post_create_hook_given) {
+ snapshot_creation_status = SCS_READY;
+ compute_next_snapshot_time();
+ return 0;
+ }
+ cmd = make_message("%s %s", conf.post_create_hook_arg,
+ path_to_last_complete_snapshot);
+ DSS_NOTICE_LOG("executing %s\n", cmd);
+ ret = dss_exec_cmdline_pid(&post_create_hook_pid, cmd, fds);
+ free(cmd);
+ if (ret < 0)
+ return ret;
+ snapshot_creation_status = SCS_POST_HOOK_RUNNING;
+ return ret;
+}
+
+int create_snapshot(char **argv)
+{
+ int ret, fds[3] = {0, 0, 0};
+ char *name;
+
+ name = incomplete_name(current_snapshot_creation_time);
+ DSS_NOTICE_LOG("creating new snapshot %s\n", name);
+ free(name);
+ ret = dss_exec(&rsync_pid, argv[0], argv, fds);
+ if (ret < 0)
+ return ret;
+ snapshot_creation_status = SCS_RSYNC_RUNNING;
+ return ret;
+}
+
+int handle_pre_create_hook_exit(int status)
+{
+ int es, ret;
+
+ if (!WIFEXITED(status)) {
+ snapshot_creation_status = SCS_READY;
+ compute_next_snapshot_time();
+ ret = -E_INVOLUNTARY_EXIT;
+ goto out;
+ }
+ es = WEXITSTATUS(status);
+ if (es) {
+ snapshot_creation_status = SCS_READY;
+ compute_next_snapshot_time();
+ ret = -E_BAD_EXIT_CODE;
+ goto out;
+ }
+ snapshot_creation_status = SCS_PRE_HOOK_SUCCESS;
+ ret = 1;
+out:
+ pre_create_hook_pid = 0;
+ return ret;
+}
+
+int handle_sigchld()
+{
+ pid_t pid;
+ int status, ret = reap_child(&pid, &status);
+
+ if (ret <= 0)
+ return ret;
+ if (pid == rsync_pid)
+ return handle_rsync_exit(status);
+ if (pid == rm_pid)
+ return handle_rm_exit(status);
+ if (pid == pre_create_hook_pid)
+ return handle_pre_create_hook_exit(status);
+ if (pid == post_create_hook_pid) {
+ snapshot_creation_status = SCS_READY;
+ compute_next_snapshot_time();
+ return 1;
+ }
+ DSS_EMERG_LOG("BUG: unknown process %d died\n", (int)pid);
+ exit(EXIT_FAILURE);
+}
+
+void handle_signal(void)
+{
+ int sig, ret = next_signal();
+
+ if (ret <= 0)
+ goto out;
+ sig = ret;
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ restart_rsync_process();
+ kill_process(rsync_pid);
+ kill_process(rm_pid);
+ exit(EXIT_FAILURE);
+ case SIGHUP:
+ handle_sighup();
+ ret = 1;
+ break;
+ case SIGCHLD:
+ ret = handle_sigchld();
+ break;
+ }
+out:
+ if (ret < 0)
+ DSS_ERROR_LOG("%s\n", dss_strerror(-ret));
+}
+
+int remove_oldest_snapshot(struct snapshot_list *sl)
+{
+ struct snapshot *s = oldest_snapshot(sl);
+
+ if (!s) /* no snapshot found */
+ return 0;
+ 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. */
+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;
+ return 0;
+}
+
+int try_to_free_disk_space(int low_disk_space)
+{
+ int ret;
+ struct snapshot_list sl;
+
+ get_snapshot_list(&sl);
+ ret = remove_outdated_snapshot(&sl);
+ if (ret) /* error, or we are removing something */
+ goto out;
+ /* no outdated snapshot */
+ ret = remove_redundant_snapshot(&sl);
+ if (ret)
+ goto out;
+ ret = 0;
+ if (!low_disk_space)
+ goto out;
+ DSS_WARNING_LOG("disk space low and nothing obvious to remove\n");
+ ret = remove_oldest_snapshot(&sl);
+ if (ret)
+ goto out;
+ DSS_CRIT_LOG("uhuhu: not enough disk space for a single snapshot\n");
+ ret= -ENOSPC;
+out:
+ free_snapshot_list(&sl);
+ return ret;
+}
+
+int select_loop(void)