Make autoconf-2.66 happy.
[paraslash.git] / server.c
index 287b628b0d2e7ffee18886f8885f58882d6e155a..95ff25cf77afb9f772a4336626e2bf340a2da868 100644 (file)
--- a/server.c
+++ b/server.c
@@ -59,7 +59,7 @@
  *     - Ring buffer: \ref ringbuffer.c, \ref ringbuffer.h,
  *     - Hashing: \ref hash.h, \ref sha1.h, \ref sha1.c,
  *     - Crypto: \ref crypt.c.
- *
+ *     - Forward error correction: \ref fec.c
  */
 
 #include <signal.h>
@@ -85,6 +85,7 @@
 #include "sched.h"
 #include "signal.h"
 #include "user_list.h"
+#include "color.h"
 
 /** Define the array of error lists needed by para_server. */
 INIT_SERVER_ERRLISTS;
@@ -112,11 +113,8 @@ uint32_t afs_socket_cookie;
 /** The mutex protecting the shared memory area containing the mmd struct. */
 int mmd_mutex;
 
-/* global variables for server-internal use */
-static FILE *logfile;
 /** The file containing user information (public key, permissions). */
 static char *user_list_file = NULL;
-static int mmd_shm_id;
 
 
 /** The task responsible for server command handling. */
@@ -131,54 +129,48 @@ struct server_command_task {
        struct task task;
 };
 
-/**
- * Para_server's log function.
- *
- * \param ll The log level.
- * \param fmt The format string describing the log message.
- */
-__printf_2_3 void para_log(int ll, const char* fmt,...)
+static int want_colors(void)
+{
+       if (conf.color_arg == color_arg_no)
+               return 0;
+       if (conf.color_arg == color_arg_yes)
+               return 1;
+       if (conf.logfile_given)
+               return 0;
+       return isatty(STDERR_FILENO);
+}
+
+static void init_colors_or_die(void)
 {
-       va_list argp;
-       FILE *outfd;
-       struct tm *tm;
-       time_t t1;
-       char str[MAXLINE] = "";
-       pid_t mypid;
-
-       if (ll < conf.loglevel_arg)
+       int ret, i;
+
+       if (!want_colors())
                return;
-       outfd = logfile? logfile : stderr;
-       time(&t1);
-       tm = localtime(&t1);
-       strftime(str, MAXLINE, "%b %d %H:%M:%S", tm);
-       fprintf(outfd, "%s ", str);
-       if (conf.loglevel_arg <= INFO)
-               fprintf(outfd, "%i: ", ll);
-       mypid = getpid();
-       if (conf.loglevel_arg <= INFO)
-               fprintf(outfd, "(%d) ", (int)mypid);
-       va_start(argp, fmt);
-       vfprintf(outfd, fmt, argp);
-       va_end(argp);
+       daemon_set_flag(DF_COLOR_LOG);
+       daemon_set_default_log_colors();
+       for (i = 0; i < conf.log_color_given; i++) {
+               ret = daemon_set_log_color(conf.log_color_arg[i]);
+               if (ret < 0)
+                       exit(EXIT_FAILURE);
+       }
 }
 
 /*
  * setup shared memory area and get mutex for locking
  */
-static void shm_init(void)
+static void init_ipc_or_die(void)
 {
        void *shm;
-       int ret = shm_new(sizeof(struct misc_meta_data));
+       int shmid, ret = shm_new(sizeof(struct misc_meta_data));
 
        if (ret < 0)
                goto err_out;
-
-       ret = shm_attach(ret, ATTACH_RW, &shm);
+       shmid = ret;
+       ret = shm_attach(shmid, ATTACH_RW, &shm);
+       shm_destroy(shmid);
        if (ret < 0)
                goto err_out;
        mmd = shm;
-       mmd_shm_id = ret;
 
        ret = mutex_new();
        if (ret < 0)
@@ -198,13 +190,21 @@ err_out:
        exit(EXIT_FAILURE);
 }
 
-static void parse_config(int override)
+/**
+ * (Re-)read the server configuration files.
+ *
+ * \param override Passed to gengetopt to activate the override feature.
+ *
+ * This function also re-opens the logfile and sets the global \a
+ * user_list_file variable.
+ */
+void parse_config_or_die(int override)
 {
        char *home = para_homedir();
-       struct stat statbuf;
        int ret;
        char *cf;
 
+       daemon_close_log();
        if (conf.config_file_given)
                cf = para_strdup(conf.config_file_arg);
        else
@@ -214,26 +214,33 @@ static void parse_config(int override)
                user_list_file = make_message("%s/.paraslash/server.users", home);
        else
                user_list_file = para_strdup(conf.user_list_arg);
-       ret = stat(cf, &statbuf);
-       if (ret && conf.config_file_given) {
+       ret = file_exists(cf);
+       if (conf.config_file_given && !ret)  {
                ret = -1;
-               PARA_EMERG_LOG("can not stat config file %s\n", cf);
+               PARA_EMERG_LOG("can not read config file %s\n", cf);
                goto out;
        }
-       if (!ret) {
+       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 = 1
+                       .print_errors = !conf.daemon_given
                };
                server_cmdline_parser_config_file(cf, &conf, &params);
                conf.daemon_given = tmp;
        }
-       if (conf.logfile_given)
-               logfile = open_log(conf.logfile_arg);
+       if (conf.logfile_given) {
+               daemon_set_logfile(conf.logfile_arg);
+               daemon_open_log_or_die();
+       }
+       daemon_set_loglevel(conf.loglevel_arg);
+       init_colors_or_die();
+       daemon_set_flag(DF_LOG_PID);
+       daemon_set_flag(DF_LOG_LL);
+       daemon_set_flag(DF_LOG_TIME);
        ret = 1;
 out:
        free(cf);
@@ -257,9 +264,7 @@ static void signal_pre_select(struct sched *s, struct task *t)
 static void handle_sighup(void)
 {
        PARA_NOTICE_LOG("SIGHUP\n");
-       close_log(logfile); /* gets reopened if necessary by parse_config */
-       logfile = NULL;
-       parse_config(1); /* reopens log */
+       parse_config_or_die(1); /* reopens log */
        init_user_list(user_list_file); /* reload user list */
        if (mmd->afs_pid)
                kill(mmd->afs_pid, SIGHUP);
@@ -286,19 +291,37 @@ static void signal_post_select(struct sched *s, struct task *t)
                        if (pid != mmd->afs_pid)
                                continue;
                        PARA_EMERG_LOG("fatal: afs died\n");
-                       goto genocide;
+                       kill(0, SIGTERM);
+                       goto cleanup;
                }
                break;
        /* die on sigint/sigterm. Kill all children too. */
        case SIGINT:
        case SIGTERM:
                PARA_EMERG_LOG("terminating on signal %d\n", st->signum);
-genocide:
                kill(0, SIGTERM);
+               /*
+                * We must wait for afs because afs catches SIGINT/SIGTERM.
+                * Before reacting to the signal, afs might want to use the
+                * shared memory area and the mmd mutex.  If we destroy this
+                * mutex too early and afs tries to lock the shared memory
+                * area, the call to mutex_lock() will fail and terminate the
+                * afs process. This leads to dirty osl tables.
+                *
+                * There's no such problem with the other children of the
+                * server process (the command handlers) as these reset their
+                * SIGINT/SIGTERM handlers to the default action, i.e.  these
+                * processes get killed immediately by the above kill().
+                */
+               PARA_INFO_LOG("waiting for afs (pid %d) to die\n",
+                       (int)mmd->afs_pid);
+               waitpid(mmd->afs_pid, NULL, 0);
+cleanup:
+               free(mmd->afd.afhi.chunk_table);
+               free(mmd->afd.afhi.info_string);
+               close_listed_fds();
                mutex_destroy(mmd_mutex);
                shm_detach(mmd);
-               shm_destroy(mmd_shm_id);
-
                exit(EXIT_FAILURE);
        }
 }
@@ -312,27 +335,15 @@ static void init_signal_task(void)
        st->task.post_select = signal_post_select;
        sprintf(st->task.status, "signal task");
 
+       PARA_NOTICE_LOG("setting up signal handling\n");
        st->fd = para_signal_init(); /* always successful */
-
-       PARA_NOTICE_LOG("setting up signal handlers\n");
-       if (para_install_sighandler(SIGINT) < 0)
-               goto err;
-       if (para_install_sighandler(SIGTERM) < 0)
-               goto err;
-       if (para_install_sighandler(SIGHUP) < 0)
-               goto err;
-       if (para_install_sighandler(SIGCHLD) < 0)
-               goto err;
-       if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
-               goto err;
-       if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
-               goto err;
+       para_install_sighandler(SIGINT);
+       para_install_sighandler(SIGTERM);
+       para_install_sighandler(SIGHUP);
+       para_install_sighandler(SIGCHLD);
+       para_sigaction(SIGPIPE, SIG_IGN);
        add_close_on_fork_list(st->fd);
        register_task(&st->task);
-       return;
-err:
-       PARA_EMERG_LOG("could not install signal handlers\n");
-       exit(EXIT_FAILURE);
 }
 
 static void command_pre_select(struct sched *s, struct task *t)
@@ -348,6 +359,8 @@ static void command_post_select(struct sched *s, struct task *t)
        int new_fd, ret, i;
        char *peer_name;
        pid_t child_pid;
+       uint32_t *chunk_table;
+       char *info_string;
 
        if (!FD_ISSET(sct->listen_fd, &s->rfds))
                return;
@@ -360,6 +373,16 @@ static void command_post_select(struct sched *s, struct task *t)
        mmd->num_connects++;
        mmd->active_connections++;
        random();
+       /* The chunk table and the info_string are pointers located in the
+        * mmd struct that point to dynamically allocated memory that must be
+        * freed by the parent and the child. However, as the mmd struct is in
+        * a shared memory area, there's no guarantee that after the fork these
+        * pointers are still valid in child context. As these two pointers are
+        * not used in the child anyway, we save them to local variables and
+        * free the memory via that copy in the child.
+        */
+       info_string = mmd->afd.afhi.info_string;
+       chunk_table = mmd->afd.afhi.chunk_table;
        child_pid = fork();
        if (child_pid < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
@@ -370,6 +393,9 @@ static void command_post_select(struct sched *s, struct task *t)
                /* parent keeps accepting connections */
                return;
        }
+       /* mmd might already have changed at this point */
+       free(info_string);
+       free(chunk_table);
        alarm(ALARM_TIMEOUT);
        close_listed_fds();
        para_signal_shutdown();
@@ -444,18 +470,20 @@ err:
 static int init_afs(void)
 {
        int ret, afs_server_socket[2];
+       pid_t afs_pid;
 
        ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket);
        if (ret < 0)
                exit(EXIT_FAILURE);
        afs_socket_cookie = para_random((uint32_t)-1);
-       mmd->afs_pid = fork();
-       if (mmd->afs_pid < 0)
+       afs_pid = fork();
+       if (afs_pid < 0)
                exit(EXIT_FAILURE);
-       if (!mmd->afs_pid) { /* child (afs) */
+       if (afs_pid == 0) { /* child (afs) */
                close(afs_server_socket[0]);
                afs_init(afs_socket_cookie, afs_server_socket[1]);
        }
+       mmd->afs_pid = afs_pid;
        close(afs_server_socket[1]);
        ret = mark_fd_nonblocking(afs_server_socket[0]);
        if (ret < 0)
@@ -466,6 +494,12 @@ static int init_afs(void)
        return afs_server_socket[0];
 }
 
+__noreturn static void tmp_sigchld_handler(__a_unused int s)
+{
+       PARA_EMERG_LOG("caught early SIGCHLD\n");
+       exit(EXIT_FAILURE);
+}
+
 static void server_init(int argc, char **argv)
 {
        struct server_cmdline_parser_params params = {
@@ -482,20 +516,35 @@ static void server_init(int argc, char **argv)
        /* parse command line options */
        server_cmdline_parser_ext(argc, argv, &conf, &params);
        HANDLE_VERSION_FLAG("server", conf);
-       para_drop_privileges(conf.user_arg, conf.group_arg);
+       drop_privileges_or_die(conf.user_arg, conf.group_arg);
        /* parse config file, open log and set defaults */
-       parse_config(0);
-       log_welcome("para_server", conf.loglevel_arg);
-       shm_init(); /* init mmd struct */
+       parse_config_or_die(0);
+       log_welcome("para_server");
+       init_ipc_or_die(); /* init mmd struct and mmd->lock */
        /* make sure, the global now pointer is uptodate */
        gettimeofday(now, NULL);
        server_uptime(UPTIME_SET); /* reset server uptime */
        init_user_list(user_list_file);
        /* become daemon */
        if (conf.daemon_given)
-               daemon_init();
+               daemonize();
        PARA_NOTICE_LOG("initializing audio format handlers\n");
        afh_init();
+
+       /*
+        * 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 install a SIGCHLD handler before the afs process is being
+        * forked off. Otherwise, para_server does not notice if afs dies before
+        * the SIGCHLD handler has been installed by init_signal_task() below.
+        */
+       para_sigaction(SIGCHLD, tmp_sigchld_handler);
        PARA_NOTICE_LOG("initializing the audio file selector\n");
        afs_socket = init_afs();
        init_signal_task();