Merge branch 'refs/heads/t/clean_server_exit'
[paraslash.git] / server.c
index 4f1d5708f76e997ff8fc5ee477dd97d65a6b3785..f19fc996ecc7355ddf24379e242fcec4da287dd6 100644 (file)
--- a/server.c
+++ b/server.c
@@ -45,6 +45,7 @@
 #include "afh.h"
 #include "string.h"
 #include "afs.h"
+#include "net.h"
 #include "server.h"
 #include "list.h"
 #include "send.h"
@@ -52,7 +53,6 @@
 #include "vss.h"
 #include "config.h"
 #include "close_on_fork.h"
-#include "net.h"
 #include "daemon.h"
 #include "ipc.h"
 #include "fd.h"
@@ -100,12 +100,33 @@ uint32_t afs_socket_cookie;
 /** The mutex protecting the shared memory area containing the mmd struct. */
 int mmd_mutex;
 
+/* Serializes log output. */
+static int log_mutex;
+
 static struct sched sched;
 static struct signal_task *signal_task;
 
 /** The process id of the audio file selector process. */
 pid_t afs_pid = 0;
 
+/* The the main server process (parent of afs and the command handlers). */
+static pid_t server_pid;
+
+/**
+ * Tell whether the executing process is a command handler.
+ *
+ * Cleanup on exit must be performed differently for command handlers.
+ *
+ * \return True if the pid of the executing process is neither the server pid
+ * nor the afs pid.
+ */
+bool process_is_command_handler(void)
+{
+       pid_t pid = getpid();
+
+       return pid != afs_pid && pid != server_pid;
+}
+
 /** The task responsible for server command handling. */
 struct server_command_task {
        /** TCP port on which para_server listens for connections. */
@@ -133,9 +154,17 @@ char *server_get_tasks(void)
        return get_task_list(&sched);
 }
 
-/*
- * setup shared memory area and get mutex for locking
- */
+static void pre_log_hook(void)
+{
+       mutex_lock(log_mutex);
+}
+
+static void post_log_hook(void)
+{
+       mutex_unlock(log_mutex);
+}
+
+/* Setup shared memory area and init mutexes */
 static void init_ipc_or_die(void)
 {
        void *shm;
@@ -154,6 +183,10 @@ static void init_ipc_or_die(void)
        if (ret < 0)
                goto err_out;
        mmd_mutex = ret;
+       ret = mutex_new();
+       if (ret < 0)
+               goto destroy_mmd_mutex;
+       log_mutex = ret;
 
        mmd->num_played = 0;
        mmd->num_commands = 0;
@@ -163,6 +196,8 @@ static void init_ipc_or_die(void)
        mmd->vss_status_flags = VSS_NEXT;
        mmd->new_vss_status_flags = VSS_NEXT;
        return;
+destroy_mmd_mutex:
+       mutex_destroy(mmd_mutex);
 err_out:
        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        exit(EXIT_FAILURE);
@@ -248,7 +283,7 @@ success:
                daemon_set_flag(DF_LOG_TIMING);
        daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
        if (user_list_file)
-               init_user_list(user_list_file);
+               user_list_init(user_list_file);
        ret = 1;
 free_cf:
        free(cf);
@@ -307,27 +342,22 @@ static int signal_post_select(struct sched *s, __a_unused void *context)
                PARA_EMERG_LOG("terminating on signal %d\n", signum);
                kill(0, SIGTERM);
                /*
-                * We must wait for afs because afs catches SIGINT/SIGTERM.
-                * Before reacting to the signal, afs might want to use the
+                * We must wait for all of our children to die. For the afs
+                * process or a command handler 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)afs_pid);
-               waitpid(afs_pid, NULL, 0);
+               PARA_INFO_LOG("waiting for child processes to die\n");
+               mutex_unlock(mmd_mutex);
+               while (wait(NULL) != -1 || errno != ECHILD)
+                       ; /* still at least one child alive */
+               mutex_lock(mmd_mutex);
 cleanup:
                free(mmd->afd.afhi.chunk_table);
-               close_listed_fds();
-               mutex_destroy(mmd_mutex);
-               shm_detach(mmd);
-               exit(EXIT_FAILURE);
+               task_notify_all(s, E_DEADLY_SIGNAL);
+               return -E_DEADLY_SIGNAL;
        }
        return 0;
 }
@@ -475,6 +505,8 @@ static int init_afs(int argc, char **argv)
                int i;
 
                afs_pid = getpid();
+               crypt_shutdown();
+               user_list_deplete();
                for (i = argc - 1; i >= 0; i--)
                        memset(argv[i], 0, strlen(argv[i]));
                i = argc - lls_num_inputs(cmdline_lpr) - 1;
@@ -532,10 +564,12 @@ static void server_init(int argc, char **argv, struct server_command_task *sct)
        /* become daemon */
        if (OPT_GIVEN(DAEMON))
                daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
-       init_random_seed_or_die();
+       server_pid = getpid();
+       crypt_init();
        daemon_log_welcome("server");
-       init_ipc_or_die(); /* init mmd struct and mmd->lock */
+       init_ipc_or_die(); /* init mmd struct, mmd and log mutex */
        daemon_set_start_time();
+       daemon_set_hooks(pre_log_hook, post_log_hook);
        PARA_NOTICE_LOG("initializing audio format handlers\n");
        afh_init();
 
@@ -610,6 +644,21 @@ static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds,
        return ret;
 }
 
+/**
+ * Deallocate all lopsub parse results.
+ *
+ * The server allocates a parse result for command line options and optionally
+ * a second parse result for the effective configuration, defined by merging
+ * the command line options with the options stored in the configuration file.
+ * This function frees both structures.
+ */
+void free_lpr(void)
+{
+       lls_free_parse_result(server_lpr, CMD_PTR);
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+}
+
 /**
  * The main function of para_server.
  *
@@ -630,23 +679,29 @@ int main(int argc, char *argv[])
        server_init(argc, argv, sct);
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
+       /*
+        * We hold the mmd lock: it was re-acquired in server_select()
+        * after the select call.
+        */
+       mutex_unlock(mmd_mutex);
        sched_shutdown(&sched);
+       crypt_shutdown();
        signal_shutdown(signal_task);
-       if (sct->child_fd < 0) { /* parent (server) */
+       if (!process_is_command_handler()) { /* parent (server) */
+               mutex_destroy(mmd_mutex);
+               daemon_set_hooks(NULL, NULL); /* only one process remaining */
+               mutex_destroy(log_mutex);
+               deplete_close_on_fork_list();
                if (ret < 0)
                        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-       } else { /* child (command handler) */
-               /*
-                * We hold the lock: it was re-acquired in server_select()
-                * after the select call.
-                */
-               mutex_unlock(mmd_mutex);
+       } else {
                alarm(ALARM_TIMEOUT);
                close_listed_fds();
                ret = handle_connect(sct->child_fd);
        }
-       lls_free_parse_result(server_lpr, CMD_PTR);
-       if (server_lpr != cmdline_lpr)
-               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       vss_shutdown();
+       shm_detach(mmd);
+       user_list_deplete();
+       free_lpr();
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }