lsu: Add helper to merge config file options, convert server.
authorAndre Noll <maan@tuebingen.mpg.de>
Wed, 14 Mar 2018 19:42:57 +0000 (20:42 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 22 Apr 2018 18:30:37 +0000 (20:30 +0200)
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
lsu.h
server.c

diff --git a/lsu.c b/lsu.c
index de6f2cb..8ccf80d 100644 (file)
--- 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 baba14f..6141dfc 100644 (file)
--- 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);
index 66c93ab..d98009d 100644 (file)
--- 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;
 }
 
 /*