+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;
+}
+
+static int change_to_dest_dir(void)
+{
+ int ret;
+ const char *dd = OPT_STRING_VAL(DSS, DEST_DIR);
+
+ DSS_INFO_LOG(("changing cwd to %s\n", dd));
+ if (chdir(dd) >= 0)
+ return 1;
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("could not change cwd to %s\n", dd));
+ return ret;
+}
+
+static int check_config(const struct lls_command *cmd)
+{
+ 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 (cmd == CMD_PTR(RUN) || cmd == CMD_PTR(CREATE))
+ if (!OPT_GIVEN(DSS, SOURCE_DIR)) {
+ DSS_ERROR_LOG(("--source-dir required\n"));
+ return -E_SYNTAX;
+ }
+ if (cmd == CMD_PTR(RUN) || cmd == CMD_PTR(CREATE)
+ || cmd == CMD_PTR(LS) || cmd == 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);
+ if (ret < 0) {
+ if (errno != ENOENT || OPT_GIVEN(DSS, CONFIG_FILE)) {
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("config file %s can not be opened\n",
+ config_file));
+ goto out;
+ }
+ /* no config file -- nothing to do */
+ ret = 0;
+ goto success;
+ }
+ fd = ret;
+ ret = fstat(fd, &statbuf);
+ if (ret < 0) {
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("failed to stat config file %s\n", config_file));
+ goto close_fd;
+ }
+ sz = statbuf.st_size;
+ if (sz == 0) { /* config file is empty -- nothing to do */
+ ret = 0;
+ goto success;
+ }
+ map = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ ret = -ERRNO_TO_DSS_ERROR(errno);
+ DSS_ERROR_LOG(("failed to mmap config file %s\n",
+ config_file));
+ goto close_fd;
+ }
+ if (cmd == CMD_PTR(DSS))
+ subcmd_name = NULL;
+ else
+ subcmd_name = lls_command_name(cmd);
+ ret = lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx);
+ munmap(map, sz);
+ if (ret < 0) {
+ DSS_ERROR_LOG(("failed to convert config file %s\n",
+ config_file));
+ ret = lopsub_error(ret, &errctx);
+ goto close_fd;
+ }
+ cf_argc = ret;
+ ret = lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx);
+ lls_free_argv(cf_argv);
+ if (ret < 0) {
+ ret = lopsub_error(ret, &errctx);
+ goto close_fd;
+ }
+ clpr = cmd == CMD_PTR(DSS)? cmdline_lpr : cmdline_sublpr;
+ if (sighup) /* config file overrides command line */
+ ret = lls_merge(cf_lpr, clpr, cmd, &merged_lpr, &errctx);
+ else /* command line options overrride config file options */
+ ret = lls_merge(clpr, cf_lpr, cmd, &merged_lpr, &errctx);
+ lls_free_parse_result(cf_lpr, cmd);
+ if (ret < 0) {
+ ret = lopsub_error(ret, &errctx);
+ goto close_fd;
+ }
+ ret = 1;
+success:
+ assert(ret >= 0);
+ DSS_DEBUG_LOG(("loglevel: %d\n", OPT_UINT32_VAL(DSS, LOGLEVEL)));
+ if (cmd != CMD_PTR(DSS)) {
+ if (ret > 0) {
+ if (sublpr != cmdline_sublpr)
+ lls_free_parse_result(sublpr, cmd);
+ sublpr = merged_lpr;
+ } else
+ sublpr = cmdline_sublpr;
+ } else {
+ if (ret > 0) {
+ if (lpr != cmdline_lpr)
+ lls_free_parse_result(lpr, cmd);
+ lpr = merged_lpr;
+ } else
+ lpr = cmdline_lpr;
+ }
+close_fd:
+ if (fd >= 0)
+ close(fd);
+out:
+ free(config_file);
+ if (ret < 0)
+ DSS_EMERG_LOG(("%s\n", dss_strerror(-ret)));
+ return ret;
+}
+
+static int handle_sighup(void)
+{
+ int ret;
+
+ DSS_NOTICE_LOG(("SIGHUP, re-reading config\n"));
+ dump_dss_config("old");
+ ret = parse_config_file(true /* SIGHUP */, CMD_PTR(DSS));
+ if (ret < 0)
+ return ret;
+ ret = parse_config_file(true /* SIGHUP */, CMD_PTR(RUN));
+ if (ret < 0)
+ return ret;
+ ret = check_config(CMD_PTR(RUN));
+ if (ret < 0)
+ return ret;
+ close_log(logfile);
+ logfile = NULL;
+ if (OPT_GIVEN(RUN, DAEMON) || daemonized) {
+ logfile = open_log(OPT_STRING_VAL(RUN, LOGFILE));
+ log_welcome(OPT_UINT32_VAL(DSS, LOGLEVEL));
+ daemonized = true;
+ }
+ dump_dss_config("reloaded");
+ invalidate_next_snapshot_time();
+ return 1;
+}
+
+static void kill_children(void)
+{
+ restart_create_process();
+ dss_kill(create_pid, SIGTERM, NULL);
+ dss_kill(remove_pid, SIGTERM, NULL);
+}
+
+static int handle_signal(void)
+{
+ int sig, ret = next_signal();
+
+ if (ret <= 0)
+ goto out;
+ sig = ret;
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ kill_children();
+ ret = -E_SIGNAL;
+ break;
+ case SIGHUP:
+ ret = handle_sighup();
+ break;
+ case SIGCHLD:
+ ret = handle_sigchld();
+ break;
+ }
+out:
+ if (ret < 0)
+ DSS_ERROR_LOG(("%s\n", dss_strerror(-ret)));
+ return ret;
+}
+
+/*
+ * We can not use rsync locally if the local user is different from the remote
+ * user or if the src dir is not on the local host (or both).
+ */
+static int use_rsync_locally(char *logname)
+{
+ const char *h = OPT_STRING_VAL(DSS, REMOTE_HOST);
+
+ if (strcmp(h, "localhost") && strcmp(h, "127.0.0.1"))
+ return 0;
+ if (OPT_GIVEN(DSS, REMOTE_USER) &&
+ strcmp(OPT_STRING_VAL(DSS, REMOTE_USER), logname))
+ return 0;
+ return 1;
+}
+
+static int rename_resume_snap(int64_t creation_time)