Merge branch 'refs/heads/t/clean_server_exit'
[paraslash.git] / afs.c
diff --git a/afs.c b/afs.c
index ef05a473e85a022568a785ecc8da140d2b19d1dc..4fe2140be607effea9d5c0a17c1eec8f41011b59 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -1,8 +1,4 @@
-/*
- * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
 /** \file afs.c Paraslash's audio file selector. */
 
@@ -26,8 +22,8 @@
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
-#include "server.h"
 #include "net.h"
+#include "server.h"
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
@@ -78,11 +74,6 @@ static struct afs_table afs_tables[NUM_AFS_TABLES] = {
 struct command_task {
        /** The file descriptor for the local socket. */
        int fd;
-       /**
-        * Value sent by the command handlers to identify themselves as
-        * children of the running para_server.
-        */
-       uint32_t cookie;
        /** The associated task structure. */
        struct task *task;
 };
@@ -97,15 +88,6 @@ static struct signal_task *signal_task;
 static enum play_mode current_play_mode;
 static char *current_mop; /* mode or playlist specifier. NULL means dummy mood */
 
-/**
- * A random number used to "authenticate" the connection.
- *
- * para_server picks this number by random before it forks the afs process. The
- * command handlers know this number as well and write it to the afs socket,
- * together with the id of the shared memory area which contains the payload of
- * the afs command. A local process has to know this number to abuse the afs
- * service provided by the local socket.
- */
 extern uint32_t afs_socket_cookie;
 
 /**
@@ -466,23 +448,30 @@ no_admissible_files:
 }
 
 /* Never fails if arg == NULL */
-static int activate_mood_or_playlist(const char *arg, int *num_admissible)
+static int activate_mood_or_playlist(const char *arg, int *num_admissible,
+               char **errmsg)
 {
        enum play_mode mode;
        int ret;
 
        if (!arg) {
-               ret = change_current_mood(NULL); /* always successful */
+               ret = change_current_mood(NULL, NULL); /* always successful */
                mode = PLAY_MODE_MOOD;
        } else {
                if (!strncmp(arg, "p/", 2)) {
                        ret = playlist_open(arg + 2);
+                       if (ret < 0 && errmsg)
+                               *errmsg = make_message( "could not open %s",
+                                       arg);
                        mode = PLAY_MODE_PLAYLIST;
                } else if (!strncmp(arg, "m/", 2)) {
-                       ret = change_current_mood(arg + 2);
+                       ret = change_current_mood(arg + 2, errmsg);
                        mode = PLAY_MODE_MOOD;
-               } else
+               } else {
+                       if (errmsg)
+                               *errmsg = make_message("%s: parse error", arg);
                        return -ERRNO_TO_PARA_ERROR(EINVAL);
+               }
                if (ret < 0)
                        return ret;
        }
@@ -564,6 +553,7 @@ static int com_select_callback(struct afs_callback_arg *aca)
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
        const char *arg;
        int num_admissible, ret;
+       char *errmsg;
 
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        assert(ret >= 0);
@@ -577,22 +567,27 @@ static int com_select_callback(struct afs_callback_arg *aca)
                close_current_mood();
        else
                playlist_close();
-       ret = activate_mood_or_playlist(arg, &num_admissible);
+       ret = activate_mood_or_playlist(arg, &num_admissible, &errmsg);
        if (ret >= 0)
                goto out;
        /* ignore subsequent errors (but log them) */
+       para_printf(&aca->pbout, "%s\n", errmsg);
+       free(errmsg);
        para_printf(&aca->pbout, "could not activate %s\n", arg);
        if (current_mop && strcmp(current_mop, arg) != 0) {
                int ret2;
                para_printf(&aca->pbout, "switching back to %s\n", current_mop);
-               ret2 = activate_mood_or_playlist(current_mop, &num_admissible);
+               ret2 = activate_mood_or_playlist(current_mop, &num_admissible,
+                       &errmsg);
                if (ret2 >= 0)
                        goto out;
+               para_printf(&aca->pbout, "%s\n", errmsg);
+               free(errmsg);
                para_printf(&aca->pbout, "could not reactivate %s: %s\n",
                        current_mop, para_strerror(-ret2));
        }
        para_printf(&aca->pbout, "activating dummy mood\n");
-       activate_mood_or_playlist(NULL, &num_admissible);
+       activate_mood_or_playlist(NULL, &num_admissible, NULL);
 out:
        para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
                current_mop? current_mop : "dummy mood", num_admissible);
@@ -617,12 +612,12 @@ EXPORT_SERVER_CMD_HANDLER(select);
 
 static void init_admissible_files(const char *arg)
 {
-       int ret = activate_mood_or_playlist(arg, NULL);
+       int ret = activate_mood_or_playlist(arg, NULL, NULL);
        if (ret < 0) {
                assert(arg);
                PARA_WARNING_LOG("could not activate %s: %s\n", arg,
                        para_strerror(-ret));
-               activate_mood_or_playlist(NULL, NULL); /* always successful */
+               activate_mood_or_playlist(NULL, NULL, NULL);
        }
 }
 
@@ -643,16 +638,18 @@ static int setup_command_socket_or_die(void)
        return socket_fd;
 }
 
+static char *database_dir;
+
 static void close_afs_tables(void)
 {
        int i;
        PARA_NOTICE_LOG("closing afs_tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
                afs_tables[i].close();
+       free(database_dir);
+       database_dir = NULL;
 }
 
-static char *database_dir;
-
 static void get_database_dir(void)
 {
        if (!database_dir) {
@@ -690,8 +687,7 @@ static int open_afs_tables(void)
                ret = afs_tables[i].open(database_dir);
                if (ret >= 0)
                        continue;
-               PARA_ERROR_LOG("%s init: %s\n", afs_tables[i].name,
-                       para_strerror(-ret));
+               PARA_ERROR_LOG("could not open %s\n", afs_tables[i].name);
                break;
        }
        if (ret >= 0)
@@ -871,7 +867,7 @@ static int execute_server_command(fd_set *rfds)
 }
 
 /* returns 0 if no data available, 1 else */
-static int execute_afs_command(int fd, fd_set *rfds, uint32_t expected_cookie)
+static int execute_afs_command(int fd, fd_set *rfds)
 {
        uint32_t cookie;
        int query_shmid;
@@ -889,9 +885,9 @@ static int execute_afs_command(int fd, fd_set *rfds, uint32_t expected_cookie)
                return 1;
        }
        cookie = *(uint32_t *)buf;
-       if (cookie != expected_cookie) {
+       if (cookie != afs_socket_cookie) {
                PARA_NOTICE_LOG("received invalid cookie (got %u, expected %u)\n",
-                       (unsigned)cookie, (unsigned)expected_cookie);
+                       (unsigned)cookie, (unsigned)afs_socket_cookie);
                return 1;
        }
        query_shmid = *(int *)(buf + sizeof(cookie));
@@ -929,7 +925,7 @@ static int command_post_select(struct sched *s, void *context)
        }
        /* Check the list of connected clients. */
        list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
-               ret = execute_afs_command(client->fd, &s->rfds, ct->cookie);
+               ret = execute_afs_command(client->fd, &s->rfds);
                if (ret == 0) { /* prevent bogus connection flooding */
                        struct timeval diff;
                        tv_diff(now, &client->connect_time, &diff);
@@ -960,11 +956,10 @@ static int command_post_select(struct sched *s, void *context)
        return 0;
 }
 
-static void register_command_task(uint32_t cookie, struct sched *s)
+static void register_command_task(struct sched *s)
 {
        struct command_task *ct = &command_task_struct;
        ct->fd = setup_command_socket_or_die();
-       ct->cookie = cookie;
 
        ct->task = task_register(&(struct task_info) {
                .name = "afs command",
@@ -977,10 +972,9 @@ static void register_command_task(uint32_t cookie, struct sched *s)
 /**
  * Initialize the audio file selector process.
  *
- * \param cookie The value used for "authentication".
  * \param socket_fd File descriptor used for communication with the server.
  */
-__noreturn void afs_init(uint32_t cookie, int socket_fd)
+__noreturn void afs_init(int socket_fd)
 {
        static struct sched s;
        int i, ret;
@@ -996,10 +990,9 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
        ret = mark_fd_nonblocking(server_socket);
        if (ret < 0)
                goto out_close;
-       PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
-               server_socket, (unsigned) cookie);
+       PARA_INFO_LOG("server_socket: %d\n", server_socket);
        init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
-       register_command_task(cookie, &s);
+       register_command_task(&s);
        s.default_timeout.tv_sec = 0;
        s.default_timeout.tv_usec = 999 * 1000;
        ret = write(socket_fd, "\0", 1);
@@ -1011,9 +1004,14 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
        }
        ret = schedule(&s);
        sched_shutdown(&s);
+       close_current_mood();
 out_close:
        close_afs_tables();
 out:
+       signal_shutdown(signal_task);
+       free_status_items();
+       free(current_mop);
+       free_lpr();
        if (ret < 0)
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        exit(EXIT_FAILURE);
@@ -1025,6 +1023,7 @@ static int com_init_callback(struct afs_callback_arg *aca)
        int i, ret;
 
        close_afs_tables();
+       get_database_dir();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
                struct afs_table *t = &afs_tables[i];