From 9320868a1baadbd7050af133d184eb10572c5a65 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Wed, 14 Mar 2018 20:42:57 +0100 Subject: [PATCH] lsu: Add helper to merge config file options, convert server. After the command line options have been parsed, most paraslash executables read options from a config file. The two lopsub parse result structures are then merged in a way that command line options take preference over config file options. This logic is duplicated in all executables. This patch introduces a generic helper to eliminate the duplication. The new lsu_merge_config_file_options() will eventually be employed by all executables which need to parse the config file. This patch, however, only converts para_server. --- lsu.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lsu.h | 28 ++++++++++++++++ server.c | 83 ++++++++++++++---------------------------------- 3 files changed, 149 insertions(+), 59 deletions(-) diff --git a/lsu.c b/lsu.c index de6f2cbc..8ccf80d5 100644 --- a/lsu.c +++ b/lsu.c @@ -8,6 +8,8 @@ #include "para.h" #include "error.h" #include "string.h" +#include "lsu.h" +#include "fd.h" static int lsu_lopsub_error(int ret, char **errctx, char **result, unsigned *num_chars) { @@ -136,3 +138,98 @@ int lsu_com_help(bool long_help, const struct lls_parse_result *lpr, *num_chars = n; return 1; } + +/** + * Merge command line options and config file options. + * + * This function parses the options stored in the configuration file and merges + * them with the currently effective options. If the application supports + * config files, it is supposed to call this after the command line options + * have been parsed. If the application also supports config file reloading, + * the function will be called for that purpose. + * + * \param path Config file path, usually the argument to --config-file. + * \param dflt Relative to ~/.paraslash, ignored if path is not NULL. + * \param lpr Value-result pointer. + * \param cmd Passed to lls_parse() and lls_merge(). + * \param suite Needed to tell whether cmd is the supercommand. + * \param flags See enum \ref lsu_merge_cf_flags. + * + * The function does nothing if path is NULL and the default config file does + * not exist, or if path is an empty file. Otherwise, the options of the config + * file are parsed, the parse result is merged with lpr, and the merged parse + * result is returned via lpr. + * + * By default, lpr is freed if the merge was done, but this can be changed by + * including MCF_DONT_FREE flags. + * + * \return Zero if there was nothing to do, one if the config file options were + * merged successfully, negative error code on failure. It is considered an error + * if path is given, but the file does not exist. + */ +int lsu_merge_config_file_options(const char *path, const char *dflt, + struct lls_parse_result **lpr, const struct lls_command *cmd, + const struct lls_suite *suite, unsigned flags) +{ + int ret; + void *map; + size_t sz; + int cf_argc; + char *cf, **cf_argv, *errctx = NULL; + struct lls_parse_result *old_lpr = *lpr, *cf_lpr, *merged_lpr; + const char *subcmd_name; + + if (path) + cf = para_strdup(path); + else { + char *home = para_homedir(); + cf = make_message("%s/.paraslash/%s", home, dflt); + free(home); + } + ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); + if (ret < 0) { + if (ret == -E_EMPTY) + ret = 0; + else if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && !path) + ret = 0; + else + PARA_ERROR_LOG("failed to mmap config file %s\n", cf); + goto free_cf; + } + subcmd_name = (lls_cmd(0, suite) == cmd)? NULL : lls_command_name(cmd); + ret = lls(lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx)); + para_munmap(map, sz); + if (ret < 0) { + PARA_ERROR_LOG("failed to convert config file %s\n", cf); + goto lopsub_error; + } + cf_argc = ret; + ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx)); + lls_free_argv(cf_argv); + if (ret < 0) { + PARA_ERROR_LOG("failed to parse config file %s\n", cf); + goto lopsub_error; + } + if (flags & MCF_OVERRIDE) + ret = lls(lls_merge(cf_lpr, old_lpr, cmd, &merged_lpr, &errctx)); + else + ret = lls(lls_merge(old_lpr, cf_lpr, cmd, &merged_lpr, &errctx)); + lls_free_parse_result(cf_lpr, cmd); + if (ret < 0) { + PARA_ERROR_LOG("could not merge options in %s\n", cf); + goto lopsub_error; + } + if (!(flags & MCF_DONT_FREE)) + lls_free_parse_result(old_lpr, cmd); + *lpr = merged_lpr; + ret = 1; + goto free_cf; +lopsub_error: + assert(ret < 0); + if (errctx) + PARA_ERROR_LOG("lopsub error: %s\n", errctx); + free(errctx); +free_cf: + free(cf); + return ret; +} diff --git a/lsu.h b/lsu.h index baba14fd..6141dfca 100644 --- a/lsu.h +++ b/lsu.h @@ -5,3 +5,31 @@ int lsu_com_help(bool long_help, const struct lls_parse_result *lpr, const struct lls_suite *suite, const char *(*aux_info_cb)(unsigned cmd_num, bool verbose), char **result, unsigned *num_chars); + +/** Flags for \ref lsu_merge_config_file_options(). */ +enum lsu_merge_cf_flags { + /** + * Whether the options specified in the configuration file should + * override the currently effective options. At application startup + * this is usually unset so that command line options take precedence + * over config file options. However, if the application supports + * re-reading the configuration, it can make sense to enable this flag. + */ + MCF_OVERRIDE = 1, + /** + * After the two lopsub parse results have been merged, the merged + * parse result usually becomes the effective configuration and the + * parse result which corresponds to the former effective options is no + * longer needed. Therefore \ref lsu_merge_config_file_options() frees + * this former parse result by default. This flag instructs the + * function to keep it. This is mostly useful if the application + * supports re-reading the config file so that the parse result which + * corresponds to the command line options is kept for future calls to + * \ref lsu_merge_config_file_options(). + */ + MCF_DONT_FREE = 2, +}; + +int lsu_merge_config_file_options(const char *path, const char *dflt, + struct lls_parse_result **lpr, const struct lls_command *cmd, + const struct lls_suite *suite, unsigned flags); diff --git a/server.c b/server.c index 66c93ab2..d98009d0 100644 --- a/server.c +++ b/server.c @@ -41,6 +41,7 @@ #include "server.lsg.h" #include "para.h" #include "error.h" +#include "lsu.h" #include "crypt.h" #include "afh.h" #include "string.h" @@ -177,56 +178,20 @@ err_out: void parse_config_or_die(bool reload) { int ret; - char *cf = NULL, *errctx = NULL, *user_list_file = NULL; - void *map; - size_t sz; - int cf_argc; - char **cf_argv; - struct lls_parse_result *cf_lpr, *merged_lpr; - char *home = para_homedir(); - - if (OPT_GIVEN(CONFIG_FILE)) - cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE)); - else - cf = make_message("%s/.paraslash/server.conf", home); - if (!mmd || getpid() != afs_pid) { - if (OPT_GIVEN(USER_LIST)) - user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST)); - else - user_list_file = make_message("%s/.paraslash/server.users", home); - } - free(home); - ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); - if (ret < 0) { - if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT)) - goto free_cf; - if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE)) - goto free_cf; - server_lpr = cmdline_lpr; - goto success; - } - ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx)); - para_munmap(map, sz); - if (ret < 0) - goto free_cf; - cf_argc = ret; - ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx)); - lls_free_argv(cf_argv); - if (ret < 0) - goto free_cf; - if (reload) /* config file overrides command line */ - ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr, - &errctx)); - else /* command line options override config file options */ - ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr, - &errctx)); - lls_free_parse_result(cf_lpr, CMD_PTR); - if (ret < 0) - goto free_cf; + unsigned flags = MCF_DONT_FREE; + if (server_lpr != cmdline_lpr) lls_free_parse_result(server_lpr, CMD_PTR); - server_lpr = merged_lpr; -success: + server_lpr = cmdline_lpr; + if (reload) + flags |= MCF_OVERRIDE; + ret = lsu_merge_config_file_options(OPT_STRING_VAL(CONFIG_FILE), + "server.conf", &server_lpr, CMD_PTR, server_suite, flags); + if (ret < 0) { + PARA_EMERG_LOG("failed to parse config file: %s\n", + para_strerror(-ret)); + exit(EXIT_FAILURE); + } daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL)); if (OPT_GIVEN(LOGFILE)) { daemon_set_logfile(OPT_STRING_VAL(LOGFILE)); @@ -245,19 +210,19 @@ success: if (OPT_GIVEN(LOG_TIMING)) daemon_set_flag(DF_LOG_TIMING); daemon_set_priority(OPT_UINT32_VAL(PRIORITY)); - if (user_list_file) + if (!reload || getpid() != afs_pid) { + char *user_list_file; + if (OPT_GIVEN(USER_LIST)) + user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST)); + else { + char *home = para_homedir(); + user_list_file = make_message("%s/.paraslash/server.users", home); + free(home); + } init_user_list(user_list_file); - ret = 1; -free_cf: - free(cf); - free(user_list_file); - if (ret < 0) { - if (errctx) - PARA_ERROR_LOG("%s\n", errctx); - free(errctx); - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); + free(user_list_file); } + return; } /* -- 2.39.5