+ if (snapshot_removal_status == HS_READY) {
+ free(s->name);
+ free(s);
+ snapshot_currently_being_removed = NULL;
+ }
+ remove_pid = 0;
+ return ret;
+}
+
+static int wait_for_remove_process(void)
+{
+ int status, ret;
+
+ assert(remove_pid);
+ assert(
+ snapshot_removal_status == HS_PRE_RUNNING ||
+ snapshot_removal_status == HS_RUNNING ||
+ snapshot_removal_status == HS_POST_RUNNING
+ );
+ ret = wait_for_process(remove_pid, &status);
+ if (ret < 0)
+ return ret;
+ return handle_remove_exit(status);
+}
+
+static int handle_rsync_exit(int status)
+{
+ int es, ret;
+
+ if (!WIFEXITED(status)) {
+ DSS_ERROR_LOG(("rsync process %d died involuntary\n", (int)create_pid));
+ ret = -E_INVOLUNTARY_EXIT;
+ snapshot_creation_status = HS_READY;
+ goto out;
+ }
+ es = WEXITSTATUS(status);
+ /*
+ * Restart rsync on non-fatal errors:
+ * 24: Partial transfer due to vanished source files
+ */
+ if (es != 0 && es != 24) {
+ DSS_WARNING_LOG(("rsync exit code %d, error count %d\n",
+ es, ++num_consecutive_rsync_errors));
+ if (!logfile) { /* called by com_run() */
+ ret = -E_BAD_EXIT_CODE;
+ goto out;
+ }
+ if (num_consecutive_rsync_errors >
+ OPT_UINT32_VAL(RUN, MAX_RSYNC_ERRORS)) {
+ ret = -E_TOO_MANY_RSYNC_ERRORS;
+ snapshot_creation_status = HS_READY;
+ goto out;
+ }
+ DSS_WARNING_LOG(("restarting rsync process\n"));
+ snapshot_creation_status = HS_NEEDS_RESTART;
+ next_snapshot_time = get_current_time() + 60;
+ ret = 1;
+ goto out;
+ }
+ num_consecutive_rsync_errors = 0;
+ ret = rename_incomplete_snapshot(current_snapshot_creation_time);
+ if (ret < 0)
+ goto out;
+ snapshot_creation_status = HS_SUCCESS;
+ free(name_of_reference_snapshot);
+ name_of_reference_snapshot = NULL;
+out:
+ create_process_stopped = 0;
+ return ret;
+}
+
+static int handle_pre_create_hook_exit(int status)
+{
+ int es, ret;
+ static int warn_count;
+
+ if (!WIFEXITED(status)) {
+ snapshot_creation_status = HS_READY;
+ ret = -E_INVOLUNTARY_EXIT;
+ goto out;
+ }
+ es = WEXITSTATUS(status);
+ if (es) {
+ if (!warn_count--) {
+ DSS_NOTICE_LOG(("pre_create_hook %s returned %d\n",
+ OPT_STRING_VAL(DSS, PRE_CREATE_HOOK), es));
+ DSS_NOTICE_LOG(("deferring snapshot creation...\n"));
+ warn_count = 60; /* warn only once per hour */
+ }
+ next_snapshot_time = get_current_time() + 60;
+ snapshot_creation_status = HS_READY;
+ ret = 0;
+ goto out;
+ }
+ warn_count = 0;
+ snapshot_creation_status = HS_PRE_SUCCESS;
+ ret = 1;
+out:
+ return ret;
+}
+
+static int handle_sigchld(void)
+{
+ pid_t pid;
+ int status, ret = reap_child(&pid, &status);
+
+ if (ret <= 0)
+ return ret;
+
+ if (pid == create_pid) {
+ switch (snapshot_creation_status) {
+ case HS_PRE_RUNNING:
+ ret = handle_pre_create_hook_exit(status);
+ break;
+ case HS_RUNNING:
+ ret = handle_rsync_exit(status);
+ break;
+ case HS_POST_RUNNING:
+ snapshot_creation_status = HS_READY;
+ 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);
+ if (ret < 0)
+ return ret;
+ return ret;
+ }
+ DSS_EMERG_LOG(("BUG: unknown process %d died\n", (int)pid));
+ return -E_BUG;
+}
+
+/* also checks if . is a mountpoint, if --mountpoint was given */
+static int change_to_dest_dir(void)
+{
+ int ret;
+ const char *dd = OPT_STRING_VAL(DSS, DEST_DIR);
+ struct stat dot, dotdot;
+
+ DSS_INFO_LOG(("changing cwd to %s\n", dd));
+ if (chdir(dd) < 0) {
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("could not change cwd to %s\n", dd));
+ return ret;
+ }
+ if (!OPT_GIVEN(DSS, MOUNTPOINT))
+ return 0;
+ if (stat(".", &dot) < 0) {
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("could not stat .\n"));
+ return ret;
+ }
+ if (stat("..", &dotdot) < 0) {
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("could not stat ..\n"));
+ return ret;
+ }
+ if (dot.st_dev == dotdot.st_dev && dot.st_ino != dotdot.st_ino) {
+ DSS_ERROR_LOG(("mountpoint check failed for %s\n", dd));
+ return -E_MOUNTPOINT;
+ }
+ return 1;
+}
+
+static int check_config(void)
+{
+ int ret;
+ uint32_t unit_interval = OPT_UINT32_VAL(DSS, UNIT_INTERVAL);
+ uint32_t num_intervals = OPT_UINT32_VAL(DSS, NUM_INTERVALS);
+
+ if (unit_interval == 0) {
+ DSS_ERROR_LOG(("bad unit interval: %i\n", unit_interval));
+ return -E_INVALID_NUMBER;
+ }
+ DSS_DEBUG_LOG(("unit interval: %i day(s)\n", unit_interval));
+
+ if (num_intervals == 0 || num_intervals > 30) {
+ DSS_ERROR_LOG(("bad number of intervals: %i\n", num_intervals));
+ return -E_INVALID_NUMBER;
+ }
+ if (subcmd == CMD_PTR(RUN) || subcmd == CMD_PTR(CREATE))
+ if (!OPT_GIVEN(DSS, SOURCE_DIR)) {
+ DSS_ERROR_LOG(("--source-dir required\n"));
+ return -E_SYNTAX;
+ }
+ if (subcmd == CMD_PTR(RUN) || subcmd == CMD_PTR(CREATE)
+ || subcmd == CMD_PTR(LS) || subcmd == CMD_PTR(PRUNE)) {
+ if (!OPT_GIVEN(DSS, DEST_DIR)) {
+ DSS_ERROR_LOG(("--dest-dir required\n"));
+ return -E_SYNTAX;
+ }
+ ret = change_to_dest_dir();
+ if (ret < 0)
+ return ret;
+ }
+ DSS_DEBUG_LOG(("number of intervals: %i\n", num_intervals));
+ return 1;
+}
+
+static int lopsub_error(int lopsub_ret, char **errctx)
+{
+ const char *msg = lls_strerror(-lopsub_ret);
+ if (*errctx)
+ DSS_ERROR_LOG(("%s: %s\n", *errctx, msg));
+ else
+ DSS_ERROR_LOG(("%s\n", msg));
+ free(*errctx);
+ *errctx = NULL;
+ return -E_LOPSUB;
+}
+
+static int parse_config_file(bool sighup, const struct lls_command *cmd)
+{
+ int ret, fd = -1;
+ char *config_file = get_config_file_name();
+ struct stat statbuf;
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv, *errctx = NULL;
+ struct lls_parse_result *cf_lpr, *merged_lpr, *clpr;
+ const char *subcmd_name;
+
+ ret = open(config_file, O_RDONLY);