X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=afs.c;h=f29080290424d95506ec643df567e24c2d095a74;hb=08471ab23c0abdf0ddd7ff836a15bd81fbce7520;hp=e1639cc7c1447e0e9f31deff65f79fab1e3239e3;hpb=2bad70d84e763b4d866d7b97dbc1cdc8e030bd45;p=paraslash.git diff --git a/afs.c b/afs.c index e1639cc7..f2908029 100644 --- a/afs.c +++ b/afs.c @@ -24,6 +24,7 @@ #include "afs.h" #include "net.h" #include "server.h" +#include "daemon.h" #include "ipc.h" #include "list.h" #include "sched.h" @@ -39,11 +40,10 @@ enum afs_table_num { TBLNUM_AUDIO_FILES, /** The table for the paraslash attributes. See \ref attribute.c. */ TBLNUM_ATTRIBUTES, - /** - * Paraslash's scoring system is based on Gaussian normal - * distributions, and the relevant data is stored in the rbtrees of an - * osl table containing only volatile columns. See \ref score.c for - * details. + /* + * Moods and playlists organize the current set of admissible files in + * an osl table which contains only volatile columns. Each row consists + * of a pointer to an audio file and the score value of this file. */ TBLNUM_SCORES, /** @@ -91,26 +91,26 @@ static char *current_mop; /* mode or playlist specifier. NULL means dummy mood * extern uint32_t afs_socket_cookie; /** - * Struct to let command handlers execute a callback in afs context. - * - * Commands that need to change the state of afs can't change the relevant data - * structures directly because commands are executed in a child process, i.e. - * they get their own virtual address space. + * Passed from command handlers to afs. * - * This structure is used by \p send_callback_request() (executed from handler - * context) in order to let the afs process call the specified function. An - * instance of that structure is written to a shared memory area together with - * the arguments to the callback function. The identifier of the shared memory - * area is written to the command socket. + * Command handlers cannot change the afs database directly because they run in + * a separate process. The callback query structure circumvents this + * restriction as follows. To instruct the afs process to execute a particular + * function, the command hander writes an instance of this structure to a + * shared memory area, along with the arguments to the callback function. The + * identifier of the shared memory area is transferred to the afs process via + * the command socket. * - * The afs process accepts connections on the command socket and reads the - * shared memory id, attaches the corresponding area, calls the given handler to - * perform the desired action and to optionally compute a result. + * The afs process reads the shared memory id from the command socket, attaches + * the corresponding area, and calls the callback function whose address is + * stored in the area. * - * The result and a \p callback_result structure is then written to another - * shared memory area. The identifier for that area is written to the handler's - * command socket, so that the handler process can read the id, attach the - * shared memory area and use the result. + * The command output, if any, is transferred back to the command handler in + * the same way: The afs process writes the output to a second shared memory + * area together with a fixed size metadata header whose format corresponds to + * the \ref callback_result structure. The identifier of this area is sent back + * to the command handler which attaches the area and forwards the output to + * the remote client. * * \sa \ref struct callback_result. */ @@ -447,7 +447,6 @@ no_admissible_files: return write_all(server_socket, buf, 8); } -/* Never fails if arg == NULL */ static int activate_mood_or_playlist(const char *arg, int *num_admissible, char **errmsg) { @@ -455,8 +454,13 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible, int ret; if (!arg) { - ret = change_current_mood(NULL, NULL); /* always successful */ mode = PLAY_MODE_MOOD; + ret = change_current_mood(NULL, errmsg); + if (ret < 0) { + if (num_admissible) + *num_admissible = 0; + return ret; + } } else { if (!strncmp(arg, "p/", 2)) { ret = playlist_open(arg + 2); @@ -478,6 +482,12 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible, if (num_admissible) *num_admissible = ret; current_play_mode = mode; + /* + * We get called with arg == current_mop from the signal dispatcher + * after SIGHUP and from the error path of the select command to + * re-select the current mood or playlist. In this case the assignment + * to current_mop below would result in a use-after-free condition. + */ if (arg != current_mop) { free(current_mop); if (arg) { @@ -615,10 +625,10 @@ static void init_admissible_files(const char *arg) { 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, NULL); + if (arg) + activate_mood_or_playlist(NULL, NULL, NULL); } } @@ -644,7 +654,7 @@ static char *database_dir; static void close_afs_tables(void) { int i; - PARA_NOTICE_LOG("closing afs_tables\n"); + PARA_NOTICE_LOG("closing afs tables\n"); for (i = 0; i < NUM_AFS_TABLES; i++) afs_tables[i].close(); free(database_dir); @@ -698,7 +708,7 @@ static int open_afs_tables(void) return ret; } -static int afs_signal_post_select(struct sched *s, __a_unused void *context) +static int afs_signal_post_monitor(struct sched *s, __a_unused void *context) { int signum, ret; @@ -706,7 +716,7 @@ static int afs_signal_post_select(struct sched *s, __a_unused void *context) PARA_EMERG_LOG("para_server died\n"); goto shutdown; } - signum = para_next_signal(&s->rfds); + signum = para_next_signal(); if (signum == 0) return 0; if (signum == SIGHUP) { @@ -734,8 +744,8 @@ static void register_signal_task(struct sched *s) signal_task->task = task_register(&(struct task_info) { .name = "signal", - .pre_select = signal_pre_select, - .post_select = afs_signal_post_select, + .pre_monitor = signal_pre_monitor, + .post_monitor = afs_signal_post_monitor, .context = signal_task, }, s); @@ -753,15 +763,15 @@ struct afs_client { struct timeval connect_time; }; -static void command_pre_select(struct sched *s, void *context) +static void command_pre_monitor(struct sched *s, void *context) { struct command_task *ct = context; struct afs_client *client; - para_fd_set(server_socket, &s->rfds, &s->max_fileno); - para_fd_set(ct->fd, &s->rfds, &s->max_fileno); + sched_monitor_readfd(server_socket, s); + sched_monitor_readfd(ct->fd, s); list_for_each_entry(client, &afs_client_list, node) - para_fd_set(client->fd, &s->rfds, &s->max_fileno); + sched_monitor_readfd(client->fd, s); } /** @@ -853,11 +863,11 @@ static int call_callback(int fd, int query_shmid) return ret; } -static int execute_server_command(fd_set *rfds) +static int execute_server_command(void) { char buf[8]; size_t n; - int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, rfds, &n); + int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, &n); if (ret < 0 || n == 0) return ret; @@ -868,13 +878,13 @@ 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) +static int execute_afs_command(int fd) { uint32_t cookie; int query_shmid; char buf[sizeof(cookie) + sizeof(query_shmid)]; size_t n; - int ret = read_nonblock(fd, buf, sizeof(buf), rfds, &n); + int ret = read_nonblock(fd, buf, sizeof(buf), &n); if (ret < 0) goto err; @@ -908,7 +918,7 @@ err: /** Shutdown connection if query has not arrived until this many seconds. */ #define AFS_CLIENT_TIMEOUT 3 -static int command_post_select(struct sched *s, void *context) +static int command_post_monitor(struct sched *s, void *context) { struct command_task *ct = context; struct sockaddr_un unix_addr; @@ -918,7 +928,7 @@ static int command_post_select(struct sched *s, void *context) ret = task_get_notification(ct->task); if (ret < 0) return ret; - ret = execute_server_command(&s->rfds); + ret = execute_server_command(); if (ret < 0) { PARA_EMERG_LOG("%s\n", para_strerror(-ret)); task_notify_all(s, -ret); @@ -926,7 +936,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); + ret = execute_afs_command(client->fd); if (ret == 0) { /* prevent bogus connection flooding */ struct timeval diff; tv_diff(now, &client->connect_time, &diff); @@ -939,7 +949,7 @@ static int command_post_select(struct sched *s, void *context) free(client); } /* Accept connections on the local socket. */ - ret = para_accept(ct->fd, &s->rfds, &unix_addr, sizeof(unix_addr), &fd); + ret = para_accept(ct->fd, &unix_addr, sizeof(unix_addr), &fd); if (ret < 0) PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); if (ret <= 0) @@ -964,12 +974,20 @@ static void register_command_task(struct sched *s) ct->task = task_register(&(struct task_info) { .name = "afs command", - .pre_select = command_pre_select, - .post_select = command_post_select, + .pre_monitor = command_pre_monitor, + .post_monitor = command_post_monitor, .context = ct, }, s); } +static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + mutex_lock(mmd_mutex); + daemon_set_loglevel(mmd->loglevel); + mutex_unlock(mmd_mutex); + return xpoll(fds, nfds, timeout); +} + /** * Initialize the audio file selector process. * @@ -994,8 +1012,8 @@ __noreturn void afs_init(int socket_fd) PARA_INFO_LOG("server_socket: %d\n", server_socket); init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE)); register_command_task(&s); - s.default_timeout.tv_sec = 0; - s.default_timeout.tv_usec = 999 * 1000; + s.default_timeout = 1000; + s.poll_function = afs_poll; ret = write(socket_fd, "\0", 1); if (ret != 1) { if (ret == 0)