X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=afs.c;h=0946b6df3b0766f82f95f0cd100f039753543350;hp=9cef049bdfff719c95aa8a4e0357da16fc926712;hb=52fb766fd183e555d8c85185e35a4dc365097756;hpb=2830b9f881898aaec073d5ba19e33482e30190bd diff --git a/afs.c b/afs.c index 9cef049b..0946b6df 100644 --- a/afs.c +++ b/afs.c @@ -1,15 +1,20 @@ /* - * Copyright (C) 2007-2012 Andre Noll + * Copyright (C) 2007 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file afs.c Paraslash's audio file selector. */ +#include +#include #include #include #include #include +#include +#include +#include #include "server.cmdline.h" #include "para.h" @@ -23,8 +28,8 @@ #include "ipc.h" #include "list.h" #include "sched.h" -#include "signal.h" #include "fd.h" +#include "signal.h" #include "mood.h" #include "sideband.h" #include "command.h" @@ -76,7 +81,7 @@ struct command_task { */ uint32_t cookie; /** The associated task structure. */ - struct task task; + struct task *task; }; extern int mmd_mutex; @@ -84,18 +89,19 @@ extern struct misc_meta_data *mmd; static int server_socket; static struct command_task command_task_struct; -static struct signal_task signal_task_struct; +static struct signal_task *signal_task; static enum play_mode current_play_mode; -static char *current_mop; /* mode or playlist specifier. NULL means dummy mooe */ +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 forking the afs process. The - * command handlers write this number together with the id of the shared memory - * area containing the query. This way, a malicious local user has to know this - * number to be able to cause the afs process to crash by sending fake queries. + * 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; @@ -113,7 +119,7 @@ extern uint32_t afs_socket_cookie; * area is written to the command socket. * * The afs process accepts connections on the command socket and reads the - * shared memory id, attaches the corresponing area, calls the given handler to + * shared memory id, attaches the corresponding area, calls the given handler to * perform the desired action and to optionally compute a result. * * The result and a \p callback_result structure is then written to another @@ -125,7 +131,7 @@ extern uint32_t afs_socket_cookie; */ struct callback_query { /** The function to be called. */ - callback_function *handler; + afs_callback *handler; /** The number of bytes of the query */ size_t query_size; }; @@ -133,7 +139,7 @@ struct callback_query { /** * Structure embedded in the result of a callback. * - * If the callback produced a result, an instance of that structure is embeeded + * If the callback produced a result, an instance of that structure is embedded * into the shared memory area holding the result, mainly to let the command * handler know the size of the result. * @@ -142,6 +148,8 @@ struct callback_query { struct callback_result { /** The number of bytes of the result. */ size_t result_size; + /** The band designator (loglevel for the result). */ + uint8_t band; }; static int dispatch_result(int result_shmid, callback_result_handler *handler, @@ -149,7 +157,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler, { struct osl_object result; void *result_shm; - int ret2, ret = shm_attach(result_shmid, ATTACH_RO, &result_shm); + /* must attach r/w as result.data might get encrypted in-place. */ + int ret2, ret = shm_attach(result_shmid, ATTACH_RW, &result_shm); struct callback_result *cr = result_shm; if (ret < 0) { @@ -158,13 +167,8 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler, } result.size = cr->result_size; result.data = result_shm + sizeof(*cr); - if (result.size) { - assert(handler); - ret = handler(&result, private_result_data); - if (ret < 0) - PARA_NOTICE_LOG("result handler error: %s\n", - para_strerror(-ret)); - } + assert(handler); + ret = handler(&result, cr->band, private_result_data); ret2 = shm_detach(result_shm); if (ret2 < 0) { PARA_ERROR_LOG("detach failed: %s\n", para_strerror(-ret2)); @@ -187,7 +191,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler, * copied. It then notifies the afs process that the callback function \a f * should be executed by sending the shared memory identifier (shmid) to the * socket. - + * * If the callback produces a result, it sends any number of shared memory * identifiers back via the socket. For each such identifier received, \a * result_handler is called. The contents of the sma identified by the received @@ -198,7 +202,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler, * * \sa send_option_arg_callback_request(), send_standard_callback_request(). */ -int send_callback_request(callback_function *f, struct osl_object *query, +int send_callback_request(afs_callback *f, struct osl_object *query, callback_result_handler *result_handler, void *private_result_data) { @@ -228,8 +232,8 @@ int send_callback_request(callback_function *f, struct osl_object *query, if (ret < 0) goto out; - *(uint32_t *) buf = afs_socket_cookie; - *(int *) (buf + sizeof(afs_socket_cookie)) = query_shmid; + *(uint32_t *)buf = afs_socket_cookie; + *(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid; ret = connect_local_socket(conf.afs_socket_arg); if (ret < 0) @@ -253,12 +257,10 @@ int send_callback_request(callback_function *f, struct osl_object *query, ret = *(int *) buf; assert(ret > 0); result_shmid = ret; - if (!dispatch_error) { - ret = dispatch_result(result_shmid, result_handler, - private_result_data); - if (ret < 0) - dispatch_error = 1; - } + ret = dispatch_result(result_shmid, result_handler, + private_result_data); + if (ret < 0 && dispatch_error >= 0) + dispatch_error = ret; ret = shm_destroy(result_shmid); if (ret < 0) PARA_CRIT_LOG("destroy result failed: %s\n", @@ -270,8 +272,11 @@ out: PARA_CRIT_LOG("shm destroy error\n"); if (fd >= 0) close(fd); -// PARA_DEBUG_LOG("callback_ret: %d\n", ret); - return ret < 0? ret : num_dispatched; + if (dispatch_error < 0) + return dispatch_error; + if (ret < 0) + return ret; + return num_dispatched; } /** @@ -284,10 +289,10 @@ out: * \param result_handler See \ref send_callback_request. * \param private_result_data See \ref send_callback_request. * - * Some commands have a couple of options that are parsed in child context for - * syntactic correctness and are stored in a special options structure for that - * command. This function allows to pass such a structure together with a list - * of further arguments (often a list of audio files) to the parent process. + * 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(). @@ -295,7 +300,7 @@ out: * \sa send_standard_callback_request(), send_callback_request(). */ int send_option_arg_callback_request(struct osl_object *options, - int argc, char * const * const argv, callback_function *f, + int argc, char * const * const argv, afs_callback *f, callback_result_handler *result_handler, void *private_result_data) { @@ -337,7 +342,7 @@ int send_option_arg_callback_request(struct osl_object *options, * send_option_arg_callback_request(). */ int send_standard_callback_request(int argc, char * const * const argv, - callback_function *f, callback_result_handler *result_handler, + afs_callback *f, callback_result_handler *result_handler, void *private_result_data) { return send_option_arg_callback_request(NULL, argc, argv, f, result_handler, @@ -357,8 +362,11 @@ static int action_if_pattern_matches(struct osl_row *row, void *data) name = (char *)name_obj.data; if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME)) return 1; - if (!pmd->patterns.size && (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING)) + if (pmd->patterns.size == 0 && + (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING)) { + pmd->num_matches++; return pmd->action(pmd->table, row, name, pmd->data); + } for (p = pattern_txt; p < pattern_txt + pmd->patterns.size; p += strlen(p) + 1) { ret = fnmatch(p, name, pmd->fnmatch_flags); @@ -416,7 +424,7 @@ static int pass_afd(int fd, char *buf, size_t size) { struct msghdr msg = {.msg_iov = NULL}; struct cmsghdr *cmsg; - char control[255]; + char control[255] __a_aligned(8); int ret; struct iovec iov; @@ -447,7 +455,7 @@ static int pass_afd(int fd, char *buf, size_t size) } /** - * Open the audio file with highest score. + * Pass the fd of the next audio file to the server process. * * This stores all information for streaming the "best" audio file in a shared * memory area. The id of that area and an open file descriptor for the next @@ -459,27 +467,15 @@ static int pass_afd(int fd, char *buf, size_t size) */ static int open_next_audio_file(void) { - struct osl_row *aft_row; struct audio_file_data afd; int ret, shmid; char buf[8]; - long score; -again: - PARA_NOTICE_LOG("getting next audio file\n"); - ret = score_get_best(&aft_row, &score); + + ret = open_and_update_audio_file(&afd); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); goto no_admissible_files; } - ret = open_and_update_audio_file(aft_row, score, &afd); - if (ret < 0) { - ret = score_delete(aft_row); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - goto no_admissible_files; - } - goto again; - } shmid = ret; if (!write_ok(server_socket)) { ret = -E_AFS_SOCKET; @@ -501,7 +497,7 @@ no_admissible_files: } /* Never fails if arg == NULL */ -static int activate_mood_or_playlist(char *arg, int *num_admissible) +static int activate_mood_or_playlist(const char *arg, int *num_admissible) { enum play_mode mode; int ret; @@ -543,68 +539,90 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible) return 1; } -static void com_select_callback(int fd, const struct osl_object *query) -{ - struct para_buffer pb = { - .max_size = shm_get_shmmax(), - .private_data = &(struct afs_max_size_handler_data) { - .fd = fd, - }, - .max_size_handler = afs_max_size_handler, - }; - char *arg = query->data; - int num_admissible, ret, ret2; - - ret = clear_score_table(); - if (ret < 0) { - ret2 = para_printf(&pb, "%s\n", para_strerror(-ret)); - goto out; - } - if (current_play_mode == PLAY_MODE_MOOD) - close_current_mood(); - else - playlist_close(); - ret = activate_mood_or_playlist(arg, &num_admissible); - if (ret < 0) { - ret2 = para_printf(&pb, "%s\nswitching back to %s\n", - para_strerror(-ret), current_mop? - current_mop : "dummy"); - ret = activate_mood_or_playlist(current_mop, &num_admissible); - if (ret < 0) { - if (ret2 >= 0) - ret2 = para_printf(&pb, "failed, switching to dummy\n"); - activate_mood_or_playlist(NULL, &num_admissible); - } - } else - ret2 = para_printf(&pb, "activated %s (%d admissible files)\n", current_mop? - current_mop : "dummy mood", num_admissible); -out: - if (ret2 >= 0 && pb.offset) - pass_buffer_as_shm(pb.buf, pb.offset, &fd); - free(pb.buf); -} - /** * Result handler for sending data to the para_client process. * * \param result The data to be sent. - * \param private Pointer to the context. + * \param band The band designator. + * \param private Pointer to the command context. * - * \return The return value of the underlying call to sc_send_bin_buffer(). + * \return The return value of the underlying call to \ref command.c::send_sb. * - * \sa \ref callback_result_handler, \ref sc_send_bin_buffer(). + * \sa \ref callback_result_handler, \ref command.c::send_sb. */ -int afs_cb_result_handler(struct osl_object *result, void *private) +int afs_cb_result_handler(struct osl_object *result, uint8_t band, + void *private) { struct command_context *cc = private; + + assert(cc); + switch (band) { + case SBD_OUTPUT: + case SBD_DEBUG_LOG: + case SBD_INFO_LOG: + case SBD_NOTICE_LOG: + case SBD_WARNING_LOG: + case SBD_ERROR_LOG: + case SBD_CRIT_LOG: + case SBD_EMERG_LOG: + assert(result->size > 0); + return send_sb(&cc->scc, result->data, result->size, band, true); + case SBD_AFS_CB_FAILURE: + return *(int *)(result->data); + default: + return -E_BAD_BAND; + } +} + +static void flush_and_free_pb(struct para_buffer *pb) +{ int ret; + struct afs_max_size_handler_data *amshd = pb->private_data; - if (!result->size) - return 1; - ret = sc_send_bin_buffer(&cc->scc, result->data, result->size); - if (ret < 0 || ret == result->size) + if (pb->buf && pb->size > 0) { + ret = pass_buffer_as_shm(amshd->fd, amshd->band, pb->buf, + pb->offset); + if (ret < 0) + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + } + free(pb->buf); +} + +static int com_select_callback(struct afs_callback_arg *aca) +{ + const char *arg = aca->query.data; + int num_admissible, ret; + + ret = clear_score_table(); + if (ret < 0) { + para_printf(&aca->pbout, "could not clear score table: %s\n", + para_strerror(-ret)); return ret; - return -E_SHORT_WRITE; + } + if (current_play_mode == PLAY_MODE_MOOD) + close_current_mood(); + else + playlist_close(); + ret = activate_mood_or_playlist(arg, &num_admissible); + if (ret >= 0) + goto out; + /* ignore subsequent errors (but log them) */ + para_printf(&aca->pbout, "could not activate %s\n", arg); + if (current_mop) { + int ret2; + para_printf(&aca->pbout, "switching back to %s\n", current_mop); + ret2 = activate_mood_or_playlist(current_mop, &num_admissible); + if (ret2 >= 0) + goto out; + 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); +out: + para_printf(&aca->pbout, "activated %s (%d admissible files)\n", + current_mop? current_mop : "dummy mood", num_admissible); + return ret; } int com_select(struct command_context *cc) @@ -629,25 +647,19 @@ static int setup_command_socket_or_die(void) { int ret, socket_fd; char *socket_name = conf.afs_socket_arg; - struct sockaddr_un unix_addr; unlink(socket_name); - ret = create_local_socket(socket_name, &unix_addr, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH); + ret = create_local_socket(socket_name, 0); if (ret < 0) { - PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name); - exit(EXIT_FAILURE); + 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); + } } socket_fd = ret; - if (listen(socket_fd , 5) < 0) { - PARA_EMERG_LOG("can not listen on socket\n"); - exit(EXIT_FAILURE); - } - ret = mark_fd_nonblocking(socket_fd); - if (ret < 0) { - close(socket_fd); - return ret; - } PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name, socket_fd); return socket_fd; @@ -684,7 +696,7 @@ static int make_database_dir(void) get_database_dir(); ret = para_mkdir(database_dir, 0777); - if (ret >= 0 || is_errno(-ret, EEXIST)) + if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST)) return 1; return ret; } @@ -694,7 +706,7 @@ static int open_afs_tables(void) int i, ret; get_database_dir(); - PARA_NOTICE_LOG("opening %u osl tables in %s\n", NUM_AFS_TABLES, + PARA_NOTICE_LOG("opening %d osl tables in %s\n", NUM_AFS_TABLES, database_dir); for (i = 0; i < NUM_AFS_TABLES; i++) { ret = afs_tables[i].open(database_dir); @@ -711,15 +723,9 @@ static int open_afs_tables(void) return ret; } -static void signal_pre_select(struct sched *s, struct task *t) -{ - struct signal_task *st = container_of(t, struct signal_task, task); - para_fd_set(st->fd, &s->rfds, &s->max_fileno); -} - -static void afs_signal_post_select(struct sched *s, struct task *t) +static int afs_signal_post_select(struct sched *s, __a_unused void *context) { - int signum; + int signum, ret; if (getppid() == 1) { PARA_EMERG_LOG("para_server died\n"); @@ -727,42 +733,42 @@ static void afs_signal_post_select(struct sched *s, struct task *t) } signum = para_next_signal(&s->rfds); if (signum == 0) - return; + return 0; if (signum == SIGHUP) { close_afs_tables(); parse_config_or_die(1); - t->error = open_afs_tables(); - if (t->error < 0) - return; + ret = open_afs_tables(); + if (ret < 0) + return ret; init_admissible_files(current_mop); - return; + return 0; } PARA_EMERG_LOG("terminating on signal %d\n", signum); shutdown: - sched_shutdown(s); - t->error = -E_AFS_SIGNAL; + task_notify_all(s, E_AFS_SIGNAL); + return -E_AFS_SIGNAL; } static void register_signal_task(struct sched *s) { - struct signal_task *st = &signal_task_struct; - para_sigaction(SIGPIPE, SIG_IGN); - st->fd = para_signal_init(); - PARA_INFO_LOG("signal pipe: fd %d\n", st->fd); + signal_task = signal_init_or_die(); para_install_sighandler(SIGINT); para_install_sighandler(SIGTERM); para_install_sighandler(SIGHUP); - st->task.pre_select = signal_pre_select; - st->task.post_select = afs_signal_post_select; - sprintf(st->task.status, "signal task"); - register_task(s, &st->task); + signal_task->task = task_register(&(struct task_info) { + .name = "signal", + .pre_select = signal_pre_select, + .post_select = afs_signal_post_select, + .context = signal_task, + + }, s); } static struct list_head afs_client_list; -/** Describes on connected afs client. */ +/** Describes one connected afs client. */ struct afs_client { /** Position in the afs client list. */ struct list_head node; @@ -772,9 +778,9 @@ struct afs_client { struct timeval connect_time; }; -static void command_pre_select(struct sched *s, struct task *t) +static void command_pre_select(struct sched *s, void *context) { - struct command_task *ct = container_of(t, struct command_task, task); + struct command_task *ct = context; struct afs_client *client; para_fd_set(server_socket, &s->rfds, &s->max_fileno); @@ -786,26 +792,30 @@ static void command_pre_select(struct sched *s, struct task *t) /** * Send data as shared memory to a file descriptor. * + * \param fd File descriptor to send the shmid to. + * \param band The band designator for this data. * \param buf The buffer holding the data to be sent. * \param size The size of \a buf. - * \param fd_ptr A pointer to the file descriptor. * * This function creates a shared memory area large enough to hold * the content given by \a buf and \a size and sends the identifier - * of this area to the file descriptor given by \a fd_ptr. + * of this area to the file descriptor \a fd. + * + * It is called by the AFS max_size handler as well as directly by the AFS + * command callbacks to send command output to the command handlers. * * \return Zero if \a buf is \p NULL or \a size is zero. Negative on errors, * and positive on success. */ -int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr) +int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size) { - int ret, shmid, fd = *(int *)fd_ptr; + int ret, shmid; void *shm; struct callback_result *cr; - if (!buf || !size) - return 0; - ret = shm_new(size + sizeof(struct callback_result)); + if (size == 0) + assert(band != SBD_OUTPUT); + ret = shm_new(size + sizeof(*cr)); if (ret < 0) return ret; shmid = ret; @@ -814,7 +824,9 @@ int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr) goto err; cr = shm; cr->result_size = size; - memcpy(shm + sizeof(*cr), buf, size); + cr->band = band; + if (size > 0) + memcpy(shm + sizeof(*cr), buf, size); ret = shm_detach(shm); if (ret < 0) goto err; @@ -827,26 +839,43 @@ err: return ret; } -/* - * On errors, negative value is written to fd. - * On success: If query produced a result, the result_shmid is written to fd. - * Otherwise, zero is written. - */ static int call_callback(int fd, int query_shmid) { void *query_shm; struct callback_query *cq; - struct osl_object query; - int ret; + int ret, ret2; + struct afs_callback_arg aca = {.fd = fd}; ret = shm_attach(query_shmid, ATTACH_RW, &query_shm); if (ret < 0) return ret; cq = query_shm; - query.data = (char *)query_shm + sizeof(*cq); - query.size = cq->query_size; - cq->handler(fd, &query); - return shm_detach(query_shm); + aca.query.data = (char *)query_shm + sizeof(*cq); + aca.query.size = cq->query_size; + aca.pbout.max_size = shm_get_shmmax(); + aca.pbout.max_size_handler = afs_max_size_handler; + aca.pbout.private_data = &(struct afs_max_size_handler_data) { + .fd = fd, + .band = SBD_OUTPUT + }; + ret = cq->handler(&aca); + ret2 = shm_detach(query_shm); + if (ret2 < 0) { + if (ret < 0) /* ignore (but log) detach error */ + PARA_ERROR_LOG("could not detach sma: %s\n", + para_strerror(-ret2)); + else + ret = ret2; + } + flush_and_free_pb(&aca.pbout); + if (ret < 0) { + ret2 = pass_buffer_as_shm(fd, SBD_AFS_CB_FAILURE, + (const char *)&ret, sizeof(ret)); + if (ret2 < 0) + PARA_ERROR_LOG("could not pass cb failure packet: %s\n", + para_strerror(-ret)); + } + return ret; } static int execute_server_command(fd_set *rfds) @@ -904,18 +933,21 @@ err: /** Shutdown connection if query has not arrived until this many seconds. */ #define AFS_CLIENT_TIMEOUT 3 -static void command_post_select(struct sched *s, struct task *t) +static int command_post_select(struct sched *s, void *context) { - struct command_task *ct = container_of(t, struct command_task, task); + struct command_task *ct = context; struct sockaddr_un unix_addr; struct afs_client *client, *tmp; int fd, ret; + ret = task_get_notification(ct->task); + if (ret < 0) + return ret; ret = execute_server_command(&s->rfds); if (ret < 0) { PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - sched_shutdown(s); - return; + task_notify_all(s, -ret); + return ret; } /* Check the list of connected clients. */ list_for_each_entry_safe(client, tmp, &afs_client_list, node) { @@ -936,17 +968,18 @@ static void command_post_select(struct sched *s, struct task *t) if (ret < 0) PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); if (ret <= 0) - return; + return 0; ret = mark_fd_nonblocking(fd); if (ret < 0) { PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); close(fd); - return; + return 0; } client = para_malloc(sizeof(*client)); client->fd = fd; client->connect_time = *now; para_list_add(&client->node, &afs_client_list); + return 0; } static void register_command_task(uint32_t cookie, struct sched *s) @@ -955,10 +988,12 @@ static void register_command_task(uint32_t cookie, struct sched *s) ct->fd = setup_command_socket_or_die(); ct->cookie = cookie; - ct->task.pre_select = command_pre_select; - ct->task.post_select = command_post_select; - sprintf(ct->task.status, "afs command task"); - register_task(s, &ct->task); + ct->task = task_register(&(struct task_info) { + .name = "afs command", + .pre_select = command_pre_select, + .post_select = command_post_select, + .context = ct, + }, s); } /** @@ -989,7 +1024,15 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd) register_command_task(cookie, &s); s.default_timeout.tv_sec = 0; s.default_timeout.tv_usec = 999 * 1000; + ret = write(socket_fd, "\0", 1); + if (ret != 1) { + if (ret == 0) + errno = EINVAL; + ret = -ERRNO_TO_PARA_ERROR(errno); + goto out_close; + } ret = schedule(&s); + sched_shutdown(&s); out_close: close_afs_tables(); out: @@ -998,11 +1041,10 @@ out: exit(EXIT_FAILURE); } -static void create_tables_callback(int fd, const struct osl_object *query) +static int com_init_callback(struct afs_callback_arg *aca) { - uint32_t table_mask = *(uint32_t *)query->data; + uint32_t table_mask = *(uint32_t *)aca->query.data; int i, ret; - struct para_buffer pb = {.buf = NULL}; close_afs_tables(); for (i = 0; i < NUM_AFS_TABLES; i++) { @@ -1013,17 +1055,19 @@ static void create_tables_callback(int fd, const struct osl_object *query) if (!t->create) continue; ret = t->create(database_dir); - if (ret < 0) + if (ret < 0) { + para_printf(&aca->pbout, "cannot create table %s\n", + t->name); goto out; - para_printf(&pb, "successfully created %s table\n", t->name); + } + para_printf(&aca->pbout, "successfully created %s table\n", + t->name); } ret = open_afs_tables(); -out: if (ret < 0) - para_printf(&pb, "%s\n", para_strerror(-ret)); - if (pb.buf) - pass_buffer_as_shm(pb.buf, pb.offset, &fd); - free(pb.buf); + para_printf(&aca->pbout, "cannot open afs tables\n"); +out: + return ret; } int com_init(struct command_context *cc) @@ -1051,12 +1095,8 @@ int com_init(struct command_context *cc) return -E_BAD_TABLE_NAME; } } - ret = send_callback_request(create_tables_callback, &query, + return send_callback_request(com_init_callback, &query, afs_cb_result_handler, cc); - if (ret < 0) - /* ignore return value */ - sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret)); - return ret; } /** @@ -1070,7 +1110,9 @@ enum com_check_flags { /** Check the mood table. */ CHECK_MOODS = 2, /** Check the playlist table. */ - CHECK_PLAYLISTS = 4 + CHECK_PLAYLISTS = 4, + /** Check the attribute table against the audio file table. */ + CHECK_ATTS = 8 }; int com_check(struct command_context *cc) @@ -1090,6 +1132,10 @@ int com_check(struct command_context *cc) flags |= CHECK_AFT; continue; } + if (!strcmp(arg, "-A")) { + flags |= CHECK_ATTS; + continue; + } if (!strcmp(arg, "-p")) { flags |= CHECK_PLAYLISTS; continue; @@ -1110,6 +1156,12 @@ int com_check(struct command_context *cc) if (ret < 0) return ret; } + if (flags & CHECK_ATTS) { + ret = send_callback_request(attribute_check_callback, NULL, + afs_cb_result_handler, cc); + if (ret < 0) + return ret; + } if (flags & CHECK_PLAYLISTS) { ret = send_callback_request(playlist_check_callback, NULL, afs_cb_result_handler, cc); @@ -1132,10 +1184,14 @@ int com_check(struct command_context *cc) * \param pb May be \p NULL. * \param data Type depends on \a event. * - * This function calls the table handlers of all tables and passes \a pb and \a - * data verbatim. It's up to the handlers to interpret the \a data pointer. + * This function calls each table event handler, passing \a pb and \a data + * verbatim. It's up to the handlers to interpret the \a data pointer. If a + * handler returns negative, the loop is aborted. + * + * \return The (negative) error code of the first handler that failed, or non-negative + * if all handlers succeeded. */ -void afs_event(enum afs_events event, struct para_buffer *pb, +__must_check int afs_event(enum afs_events event, struct para_buffer *pb, void *data) { int i, ret; @@ -1145,10 +1201,13 @@ void afs_event(enum afs_events event, struct para_buffer *pb, if (!t->event_handler) continue; ret = t->event_handler(event, pb, data); - if (ret < 0) - PARA_CRIT_LOG("table %s, event %d: %s\n", t->name, + if (ret < 0) { + PARA_CRIT_LOG("table %s, event %u: %s\n", t->name, event, para_strerror(-ret)); + return ret; + } } + return 1; } /**