X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=afs.c;h=4fe2140be607effea9d5c0a17c1eec8f41011b59;hp=761a637cfe00caf14bc1850219cf139282bfc3bc;hb=6e2812e8cb09f4e5d9d12b9d768d134b36c3dd17;hpb=15942e4836b212fd7f092f9df353ec54c872e2b8 diff --git a/afs.c b/afs.c index 761a637c..4fe2140b 100644 --- a/afs.c +++ b/afs.c @@ -1,8 +1,4 @@ -/* - * Copyright (C) 2007 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 2007 Andre Noll , see file COPYING. */ /** \file afs.c Paraslash's audio file selector. */ @@ -18,16 +14,16 @@ #include #include +#include "server.lsg.h" #include "server_cmd.lsg.h" -#include "server.cmdline.h" #include "para.h" #include "error.h" #include "crypt.h" #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" @@ -37,22 +33,22 @@ #include "sideband.h" #include "command.h" -/** The osl tables used by afs. \sa blob.c. */ +/** The osl tables used by afs. \sa \ref blob.c. */ enum afs_table_num { - /** Contains audio file information. See aft.c. */ + /** Contains audio file information. See \ref aft.c. */ TBLNUM_AUDIO_FILES, - /** The table for the paraslash attributes. See attribute.c. */ + /** 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 score.c for + * osl table containing only volatile columns. See \ref score.c for * details. */ TBLNUM_SCORES, /** * A standard blob table containing the mood definitions. For details - * see mood.c. + * see \ref mood.c. */ TBLNUM_MOODS, /** A blob table containing lyrics on a per-song basis. */ @@ -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; /** @@ -130,7 +112,7 @@ extern uint32_t afs_socket_cookie; * command socket, so that the handler process can read the id, attach the * shared memory area and use the result. * - * \sa struct callback_result. + * \sa \ref struct callback_result. */ struct callback_query { /** The function to be called. */ @@ -146,7 +128,7 @@ struct callback_query { * into the shared memory area holding the result, mainly to let the command * handler know the size of the result. * - * \sa struct callback_query. + * \sa \ref struct callback_query. */ struct callback_result { /** The number of bytes of the result. */ @@ -201,9 +183,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler, * shmid are passed to that function as an osl object. The private_result_data * pointer is passed as the second argument to \a result_handler. * - * \return Number of shared memory areas dispatched on success, negative on errors. - * - * \sa send_option_arg_callback_request(), send_standard_callback_request(). + * \return Number of shared memory areas dispatched on success, negative on + * errors. */ int send_callback_request(afs_callback *f, struct osl_object *query, callback_result_handler *result_handler, @@ -238,7 +219,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query, *(uint32_t *)buf = afs_socket_cookie; *(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid; - ret = connect_local_socket(conf.afs_socket_arg); + ret = connect_local_socket(OPT_STRING_VAL(AFS_SOCKET)); if (ret < 0) goto out; fd = ret; @@ -312,106 +293,31 @@ int send_lls_callback_request(afs_callback *f, return ret; } -/** - * Send a callback request passing an options structure and an argument vector. - * - * \param options pointer to an arbitrary data structure. - * \param argc Argument count. - * \param argv Standard argument vector. - * \param f The callback function. - * \param result_handler See \ref send_callback_request. - * \param private_result_data See \ref send_callback_request. - * - * Some command handlers pass command-specific options to a callback, together - * with a list of further arguments (often a list of audio files). This - * function allows to pass an arbitrary structure (given as an osl object) and - * a usual argument vector to the specified callback. - * - * \return The return value of the underlying call to \ref - * send_callback_request(). - * - * \sa send_standard_callback_request(), send_callback_request(). - */ -int send_option_arg_callback_request(struct osl_object *options, - int argc, char * const * const argv, afs_callback *f, - callback_result_handler *result_handler, - void *private_result_data) -{ - char *p; - int i, ret; - struct osl_object query = {.size = options? options->size : 0}; - - for (i = 0; i < argc; i++) - query.size += strlen(argv[i]) + 1; - query.data = para_malloc(query.size); - p = query.data; - if (options) { - memcpy(query.data, options->data, options->size); - p += options->size; - } - for (i = 0; i < argc; i++) { - strcpy(p, argv[i]); /* OK */ - p += strlen(argv[i]) + 1; - } - ret = send_callback_request(f, &query, result_handler, - private_result_data); - free(query.data); - return ret; -} - -/** - * Send a callback request with an argument vector only. - * - * \param argc The same meaning as in send_option_arg_callback_request(). - * \param argv The same meaning as in send_option_arg_callback_request(). - * \param f The same meaning as in send_option_arg_callback_request(). - * \param result_handler See \ref send_callback_request. - * \param private_result_data See \ref send_callback_request. - * - * This is similar to send_option_arg_callback_request(), but no options buffer - * is passed to the parent process. - * - * \return The return value of the underlying call to - * send_option_arg_callback_request(). - */ -int send_standard_callback_request(int argc, char * const * const argv, - afs_callback *f, callback_result_handler *result_handler, - void *private_result_data) -{ - return send_option_arg_callback_request(NULL, argc, argv, f, result_handler, - private_result_data); -} - static int action_if_pattern_matches(struct osl_row *row, void *data) { struct pattern_match_data *pmd = data; struct osl_object name_obj; const char *p, *name; - int i, ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj)); - const char *pattern_txt = (const char *)pmd->patterns.data; + int i, ret; + ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, + &name_obj)); if (ret < 0) return ret; name = (char *)name_obj.data; if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME)) return 1; - if ((pmd->lpr && lls_num_inputs(pmd->lpr) == 0) || pmd->patterns.size == 0) { + if (lls_num_inputs(pmd->lpr) == 0) { if (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING) { pmd->num_matches++; return pmd->action(pmd->table, row, name, pmd->data); } } - p = pattern_txt; i = pmd->input_skip; for (;;) { - if (pmd->lpr) { - if (i >= lls_num_inputs(pmd->lpr)) - break; - p = lls_input(i, pmd->lpr); - } else { - if (p >= pattern_txt + pmd->patterns.size) - break; - } + if (i >= lls_num_inputs(pmd->lpr)) + break; + p = lls_input(i, pmd->lpr); ret = fnmatch(p, name, pmd->fnmatch_flags); if (ret != FNM_NOMATCH) { if (ret != 0) @@ -422,10 +328,7 @@ static int action_if_pattern_matches(struct osl_row *row, void *data) return ret; } - if (pmd->lpr) - i++; - else - p += strlen(p) + 1; + i++; } return 1; } @@ -459,7 +362,7 @@ int for_each_matching_row(struct pattern_match_data *pmd) * \a obj1 is found, respectively, to be less than, to match, or be greater than * obj2. * - * \sa strcmp(3), strncmp(3), osl_compare_func. + * \sa strcmp(3), strncmp(3). */ int string_compare(const struct osl_object *obj1, const struct osl_object *obj2) { @@ -511,7 +414,7 @@ static int pass_afd(int fd, char *buf, size_t size) * * \return Standard. * - * \sa open_and_update_audio_file(). + * \sa \ref open_and_update_audio_file(). */ static int open_next_audio_file(void) { @@ -545,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 - return -E_AFS_SYNTAX; + } else { + if (errmsg) + *errmsg = make_message("%s: parse error", arg); + return -ERRNO_TO_PARA_ERROR(EINVAL); + } if (ret < 0) return ret; } @@ -576,10 +486,12 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible) strncpy(mmd->afs_mode_string, arg, sizeof(mmd->afs_mode_string)); mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0'; + mmd->events++; mutex_unlock(mmd_mutex); } else { mutex_lock(mmd_mutex); strcpy(mmd->afs_mode_string, "dummy"); + mmd->events++; mutex_unlock(mmd_mutex); current_mop = NULL; } @@ -641,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); @@ -654,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) { + 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); @@ -692,27 +610,27 @@ static int com_select(struct command_context *cc, struct lls_parse_result *lpr) } EXPORT_SERVER_CMD_HANDLER(select); -static void init_admissible_files(char *arg) +static void init_admissible_files(const char *arg) { - if (activate_mood_or_playlist(arg, NULL) < 0) - activate_mood_or_playlist(NULL, NULL); /* always successful */ + 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); + } } static int setup_command_socket_or_die(void) { int ret, socket_fd; - char *socket_name = conf.afs_socket_arg; + const char *socket_name = OPT_STRING_VAL(AFS_SOCKET); unlink(socket_name); - ret = create_local_socket(socket_name, 0); + ret = create_local_socket(socket_name); if (ret < 0) { - ret = create_local_socket(socket_name, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IROTH); - if (ret < 0) { - PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), - socket_name); - exit(EXIT_FAILURE); - } + PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name); + exit(EXIT_FAILURE); } socket_fd = ret; PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name, @@ -720,21 +638,23 @@ 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) { - if (conf.afs_database_dir_given) - database_dir = para_strdup(conf.afs_database_dir_arg); + if (OPT_GIVEN(AFS_DATABASE_DIR)) + database_dir = para_strdup(OPT_STRING_VAL(AFS_DATABASE_DIR)); else { char *home = para_homedir(); database_dir = make_message( @@ -767,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) @@ -948,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; @@ -966,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)); @@ -1006,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); @@ -1037,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", @@ -1054,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; @@ -1073,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); - init_admissible_files(conf.afs_initial_mode_arg); - register_command_task(cookie, &s); + 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; ret = write(socket_fd, "\0", 1); @@ -1088,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); @@ -1102,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];