Merge branch 'refs/heads/t/si_conversion'
[paraslash.git] / server.c
index a0eb50e2308e0a77ab4bc142cb2da845229bea11..c5f9ede61a92be19c4a8580b0eaf01ca4bf4e319 100644 (file)
--- a/server.c
+++ b/server.c
 #include <netdb.h>
 #include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
-#include "server.cmdline.h"
 #include "afh.h"
 #include "string.h"
 #include "afs.h"
@@ -63,7 +63,6 @@
 #include "signal.h"
 #include "user_list.h"
 #include "color.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
@@ -81,12 +80,15 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 struct misc_meta_data *mmd;
 
 /**
- * The configuration of para_server
+ * The active value for all config options of para_server.
  *
- * It also contains the options for the audio file selector, audio format
- * handler and all supported senders.
+ * It is computed by merging the parse result of the command line options with
+ * the parse result of the config file.
  */
-struct server_args_info conf;
+struct lls_parse_result *server_lpr = NULL;
+
+/* Command line options (no config file options). Used in handle_sighup(). */
+static struct lls_parse_result *cmdline_lpr;
 
 /** A random value used in child context for authentication. */
 uint32_t afs_socket_cookie;
@@ -94,9 +96,6 @@ uint32_t afs_socket_cookie;
 /** The mutex protecting the shared memory area containing the mmd struct. */
 int mmd_mutex;
 
-/** The file containing user information (public key, permissions). */
-static char *user_list_file = NULL;
-
 static struct sched sched;
 static struct signal_task *signal_task;
 
@@ -163,71 +162,96 @@ err_out:
 /**
  * (Re-)read the server configuration files.
  *
- * \param override Passed to gengetopt to activate the override feature.
+ * \param reload Whether config file overrides command line.
  *
- * This function also re-opens the logfile and sets the global \a
- * user_list_file variable.
+ * This function also re-opens the logfile and the user list. On SIGHUP it is
+ * called from both server and afs context.
  */
-void parse_config_or_die(int override)
+void parse_config_or_die(bool reload)
 {
-       char *home = para_homedir();
        int ret;
-       char *cf;
+       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();
 
        daemon_close_log();
-       if (conf.config_file_given)
-               cf = para_strdup(conf.config_file_arg);
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else
                cf = make_message("%s/.paraslash/server.conf", home);
-       free(user_list_file);
-       if (!conf.user_list_given)
-               user_list_file = make_message("%s/.paraslash/server.users", home);
-       else
-               user_list_file = para_strdup(conf.user_list_arg);
-       ret = file_exists(cf);
-       if (conf.config_file_given && !ret)  {
-               ret = -1;
-               PARA_EMERG_LOG("can not read config file %s\n", cf);
-               goto out;
+       if (!mmd || getpid() != mmd->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);
        }
-       if (ret) {
-               int tmp = conf.daemon_given;
-               struct server_cmdline_parser_params params = {
-                       .override = override,
-                       .initialize = 0,
-                       .check_required = 1,
-                       .check_ambiguity = 0,
-                       .print_errors = !conf.daemon_given
-               };
-               server_cmdline_parser_config_file(cf, &conf, &params);
-               daemon_set_loglevel(conf.loglevel_arg);
-               conf.daemon_given = tmp;
+       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;
        }
-       if (conf.logfile_given) {
-               daemon_set_logfile(conf.logfile_arg);
+       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;
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(server_lpr, CMD_PTR);
+       server_lpr = merged_lpr;
+success:
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       if (OPT_GIVEN(LOGFILE)) {
+               daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
        }
-
-       if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
-                       conf.logfile_given)) {
+       if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+                       COLOR_NO, OPT_GIVEN(LOGFILE))) {
                int i;
-               for (i = 0; i < conf.log_color_given; i++)
-                       daemon_set_log_color_or_die(conf.log_color_arg[i]);
+               for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+                       daemon_set_log_color_or_die(lls_string_val(i,
+                               OPT_RESULT(LOG_COLOR)));
        }
        daemon_set_flag(DF_LOG_PID);
        daemon_set_flag(DF_LOG_LL);
        daemon_set_flag(DF_LOG_TIME);
-       if (conf.log_timing_given)
+       if (OPT_GIVEN(LOG_TIMING))
                daemon_set_flag(DF_LOG_TIMING);
+       daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
+       if (user_list_file)
+               init_user_list(user_list_file);
        ret = 1;
-out:
+free_cf:
        free(cf);
-       free(home);
-       if (ret > 0)
-               return;
        free(user_list_file);
-       user_list_file = NULL;
-       exit(EXIT_FAILURE);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
 }
 
 /*
@@ -235,9 +259,9 @@ out:
  */
 static void handle_sighup(void)
 {
+
        PARA_NOTICE_LOG("SIGHUP\n");
-       parse_config_or_die(1); /* reopens log */
-       init_user_list(user_list_file); /* reload user list */
+       parse_config_or_die(true);
        if (mmd->afs_pid)
                kill(mmd->afs_pid, SIGHUP);
 }
@@ -332,8 +356,6 @@ static int command_post_select(struct sched *s, void *context)
        ret = para_accept(sct->listen_fd, &s->rfds, NULL, 0, &new_fd);
        if (ret <= 0)
                goto out;
-       peer_name = remote_name(new_fd);
-       PARA_INFO_LOG("got connection from %s, forking\n", peer_name);
        mmd->num_connects++;
        mmd->active_connections++;
        /*
@@ -359,6 +381,8 @@ static int command_post_select(struct sched *s, void *context)
                /* parent keeps accepting connections */
                return 0;
        }
+       peer_name = remote_name(new_fd);
+       PARA_INFO_LOG("accepted connection from %s\n", peer_name);
        /* mmd might already have changed at this point */
        free(chunk_table);
        alarm(ALARM_TIMEOUT);
@@ -370,7 +394,8 @@ static int command_post_select(struct sched *s, void *context)
         */
        for (i = sct->argc - 1; i >= 0; i--)
                memset(sct->argv[i], 0, strlen(sct->argv[i]));
-       sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
+       i = sct->argc - 1 - lls_num_inputs(cmdline_lpr);
+       sprintf(sct->argv[i], "para_server (serving %s)", peer_name);
        handle_connect(new_fd, peer_name);
        /* never reached*/
 out:
@@ -388,7 +413,7 @@ static void init_server_command_task(int argc, char **argv)
        PARA_NOTICE_LOG("initializing tcp command socket\n");
        sct->argc = argc;
        sct->argv = argv;
-       ret = para_listen_simple(IPPROTO_TCP, conf.port_arg);
+       ret = para_listen_simple(IPPROTO_TCP, OPT_UINT32_VAL(PORT));
        if (ret < 0)
                goto err;
        sct->listen_fd = ret;
@@ -427,7 +452,8 @@ static int init_afs(int argc, char **argv)
 
                for (i = argc - 1; i >= 0; i--)
                        memset(argv[i], 0, strlen(argv[i]));
-               sprintf(argv[0], "para_server (afs)");
+               i = argc - lls_num_inputs(cmdline_lpr) - 1;
+               sprintf(argv[i], "para_server (afs)");
                close(afs_server_socket[0]);
                afs_init(afs_socket_cookie, afs_server_socket[1]);
        }
@@ -446,45 +472,46 @@ static int init_afs(int argc, char **argv)
        return afs_server_socket[0];
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(server);
-       bool d = conf.detailed_help_given;
+       char *help;
+       bool d = OPT_GIVEN(DETAILED_HELP);
 
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
+       if (d)
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
 }
 
 static void server_init(int argc, char **argv)
 {
-       struct server_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
-       int afs_socket, daemon_pipe = -1;
+       int ret, afs_socket, daemon_pipe = -1;
+       char *errctx;
 
        valid_fd_012();
-       init_random_seed_or_die();
        /* parse command line options */
-       server_cmdline_parser_ext(argc, argv, &conf, &params);
-       daemon_set_loglevel(conf.loglevel_arg);
-       version_handle_flag("server", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       daemon_set_priority(conf.priority_arg);
-       daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
-       /* parse config file, open log and set defaults */
-       parse_config_or_die(0);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       server_lpr = cmdline_lpr;
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+               OPT_STRING_VAL(GROUP));
+       version_handle_flag("server", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       parse_config_or_die(false);
+       /* become daemon */
+       if (OPT_GIVEN(DAEMON))
+               daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
+       init_random_seed_or_die();
        daemon_log_welcome("server");
        init_ipc_or_die(); /* init mmd struct and mmd->lock */
        daemon_set_start_time();
-       init_user_list(user_list_file);
-       /* become daemon */
-       if (conf.daemon_given)
-               daemon_pipe = daemonize(true /* parent waits for us */);
        PARA_NOTICE_LOG("initializing audio format handlers\n");
        afh_init();
 
@@ -492,23 +519,21 @@ static void server_init(int argc, char **argv)
         * Although afs uses its own signal handling we must ignore SIGUSR1
         * _before_ the afs child process gets born by init_afs() below.  It's
         * racy to do this in the child because the parent might send SIGUSR1
-        * before the child gets a chance to ignore this signal -- only the
-        * good die young.
-        */
-       para_sigaction(SIGUSR1, SIG_IGN);
-       /*
-        * We have to block SIGCHLD before the afs process is being forked off.
-        * Otherwise, para_server does not notice if afs dies before the
+        * before the child gets a chance to ignore this signal.
+        *
+        * We also have to block SIGCHLD before the afs process is created
+        * because otherwise para_server does not notice if afs dies before the
         * SIGCHLD handler has been installed for the parent process by
         * init_signal_task() below.
         */
+       para_sigaction(SIGUSR1, SIG_IGN);
        para_block_signal(SIGCHLD);
        PARA_NOTICE_LOG("initializing the audio file selector\n");
        afs_socket = init_afs(argc, argv);
        init_signal_task();
        para_unblock_signal(SIGCHLD);
        PARA_NOTICE_LOG("initializing virtual streaming system\n");
-       init_vss_task(afs_socket, &sched);
+       vss_init(afs_socket, &sched);
        init_server_command_task(argc, argv);
        if (daemon_pipe >= 0) {
                if (write(daemon_pipe, "\0", 1) < 0) {
@@ -518,6 +543,13 @@ static void server_init(int argc, char **argv)
                close(daemon_pipe);
        }
        PARA_NOTICE_LOG("server init complete\n");
+       return;
+fail:
+       assert(ret < 0);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 static void status_refresh(void)
@@ -573,9 +605,10 @@ int main(int argc, char *argv[])
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
        sched_shutdown(&sched);
-       if (ret < 0) {
+       lls_free_parse_result(server_lpr, CMD_PTR);
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       if (ret < 0)
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-               exit(EXIT_FAILURE);
-       }
-       exit(EXIT_SUCCESS);
+       exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }