+static int select_loop(void)
+{
+ int ret;
+ /* check every 60 seconds for free disk space */
+ struct timeval tv;
+ char **rsync_argv = NULL;
+
+ for (;;) {
+ fd_set rfds;
+ struct timeval *tvp;
+
+ if (remove_pid)
+ tvp = NULL; /* sleep until rm hook/process dies */
+ else { /* sleep one minute */
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+ FD_ZERO(&rfds);
+ FD_SET(signal_pipe, &rfds);
+ ret = dss_select(signal_pipe + 1, &rfds, NULL, tvp);
+ if (ret < 0)
+ goto out;
+ if (FD_ISSET(signal_pipe, &rfds)) {
+ ret = handle_signal();
+ if (ret < 0)
+ goto out;
+ }
+ if (remove_pid)
+ continue;
+ if (snapshot_removal_status == HS_PRE_SUCCESS) {
+ ret = exec_rm();
+ if (ret < 0)
+ goto out;
+ continue;
+ }
+ if (snapshot_removal_status == HS_SUCCESS) {
+ post_remove_hook();
+ continue;
+ }
+ ret = try_to_free_disk_space();
+ if (ret < 0)
+ goto out;
+ if (snapshot_removal_status != HS_READY) {
+ stop_create_process();
+ continue;
+ }
+ restart_create_process();
+ switch (snapshot_creation_status) {
+ case HS_READY:
+ if (!next_snapshot_is_due())
+ continue;
+ pre_create_hook();
+ continue;
+ case HS_PRE_RUNNING:
+ case HS_RUNNING:
+ case HS_POST_RUNNING:
+ continue;
+ case HS_PRE_SUCCESS:
+ if (!name_of_reference_snapshot) {
+ free_rsync_argv(rsync_argv);
+ create_rsync_argv(&rsync_argv, ¤t_snapshot_creation_time);
+ }
+ ret = create_snapshot(rsync_argv);
+ if (ret < 0)
+ goto out;
+ continue;
+ case HS_NEEDS_RESTART:
+ if (!next_snapshot_is_due())
+ continue;
+ ret = create_snapshot(rsync_argv);
+ if (ret < 0)
+ goto out;
+ continue;
+ case HS_SUCCESS:
+ post_create_hook();
+ continue;
+ }
+ }
+out:
+ return ret;
+}
+
+static void exit_hook(int exit_code)
+{
+ const char *argv[3];
+ pid_t pid;
+
+ argv[0] = OPT_STRING_VAL(DSS, EXIT_HOOK);
+ argv[1] = dss_strerror(-exit_code);
+ argv[2] = NULL;
+
+ DSS_NOTICE_LOG(("executing %s %s\n", argv[0], argv[1]));
+ dss_exec(&pid, argv[0], (char **)argv);
+}
+
+static void lock_dss_or_die(void)
+{
+ char *config_file = get_config_file_name();
+ int ret = lock_dss(config_file);
+
+ free(config_file);
+ if (ret < 0) {
+ DSS_EMERG_LOG(("failed to lock: %s\n", dss_strerror(-ret)));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int com_run(void)
+{
+ int ret, fd = -1;
+ char *config_file;
+ pid_t pid;
+
+ if (OPT_GIVEN(DSS, DRY_RUN)) {
+ DSS_ERROR_LOG(("dry run not supported by this command\n"));
+ return -E_SYNTAX;
+ }
+ config_file = get_config_file_name();
+ ret = get_dss_pid(config_file, &pid);
+ free(config_file);
+ if (ret >= 0) {
+ DSS_ERROR_LOG(("pid %d\n", (int)pid));
+ return -E_ALREADY_RUNNING;
+ }
+ if (OPT_GIVEN(RUN, DAEMON)) {
+ fd = daemon_init();
+ daemonized = true;
+ logfile = open_log(OPT_STRING_VAL(RUN, LOGFILE));
+ }
+ lock_dss_or_die();
+ dump_dss_config("startup");
+ ret = install_sighandler(SIGHUP);
+ if (ret < 0)
+ return ret;
+ if (fd >= 0) {
+ ret = write(fd, "\0", 1);
+ if (ret != 1) {
+ DSS_ERROR_LOG(("write to daemon pipe returned %d\n",
+ ret));
+ if (ret < 0)
+ return -ERRNO_TO_DSS_ERROR(errno);
+ return -E_BUG;
+ }
+ }
+ ret = select_loop();
+ if (ret >= 0) /* impossible */
+ ret = -E_BUG;
+ kill_children();
+ exit_hook(ret);
+ return ret;
+}
+EXPORT_CMD_HANDLER(run);
+
+static int com_prune(void)
+{
+ int ret;
+ struct snapshot_list sl;
+ struct snapshot *victim;
+ struct disk_space ds;
+ const char *why;
+
+ lock_dss_or_die();
+ ret = get_disk_space(".", &ds);
+ if (ret < 0)
+ return ret;
+ log_disk_space(&ds);
+ dss_get_snapshot_list(&sl);
+ why = "outdated";
+ victim = find_outdated_snapshot(&sl);
+ if (victim)
+ goto rm;
+ why = "redundant";
+ victim = find_redundant_snapshot(&sl);
+ if (victim)
+ goto rm;
+ ret = 0;
+ goto out;
+rm:
+ if (OPT_GIVEN(DSS, DRY_RUN)) {
+ dss_msg("%s snapshot %s (interval = %i)\n",
+ why, victim->name, victim->interval);
+ ret = 0;
+ goto out;
+ }
+ pre_remove_hook(victim, why);
+ if (snapshot_removal_status == HS_PRE_RUNNING) {
+ ret = wait_for_remove_process();
+ if (ret < 0)
+ goto out;
+ if (snapshot_removal_status != HS_PRE_SUCCESS)
+ goto out;
+ }
+ ret = exec_rm();
+ if (ret < 0)
+ goto out;
+ ret = wait_for_remove_process();
+ if (ret < 0)
+ goto out;
+ if (snapshot_removal_status != HS_SUCCESS)
+ goto out;
+ post_remove_hook();
+ if (snapshot_removal_status != HS_POST_RUNNING)
+ goto out;
+ ret = wait_for_remove_process();
+ if (ret < 0)
+ goto out;
+ ret = 1;
+out:
+ free_snapshot_list(&sl);
+ return ret;
+}
+EXPORT_CMD_HANDLER(prune);
+
+static int com_create(void)