X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=afs.c;h=e73c668fb6d93b3a200eb841e4d4c34ce4b48e59;hp=ff3c95fe03853b0aaa80d873ea75748a10bf73d2;hb=684067afcc372644a7d000e7093549d6aa7597e9;hpb=ef1f130a1f9ac61a51da10ee56c5bd9ef1b52366 diff --git a/afs.c b/afs.c index ff3c95fe..e73c668f 100644 --- a/afs.c +++ b/afs.c @@ -1,15 +1,20 @@ /* - * Copyright (C) 2007-2011 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,9 +28,11 @@ #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" /** The osl tables used by afs. \sa blob.c. */ enum afs_table_num { @@ -74,7 +81,7 @@ struct command_task { */ uint32_t cookie; /** The associated task structure. */ - struct task task; + struct task *task; }; extern int mmd_mutex; @@ -82,10 +89,10 @@ 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. @@ -111,7 +118,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 @@ -131,7 +138,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. * @@ -140,6 +147,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, @@ -147,7 +156,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,7 +168,7 @@ static int dispatch_result(int result_shmid, callback_result_handler *handler, result.data = result_shm + sizeof(*cr); if (result.size) { assert(handler); - ret = handler(&result, private_result_data); + ret = handler(&result, cr->band, private_result_data); if (ret < 0) PARA_NOTICE_LOG("result handler error: %s\n", para_strerror(-ret)); @@ -185,14 +195,14 @@ 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 * 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 Standard. + * \return Number of shared memory areas dispatched on success, negative on errors. * * \sa send_option_arg_callback_request(), send_standard_callback_request(). */ @@ -205,7 +215,7 @@ int send_callback_request(callback_function *f, struct osl_object *query, void *query_shm; char buf[sizeof(afs_socket_cookie) + sizeof(int)]; size_t query_shm_size = sizeof(*cq); - int dispatch_error = 0; + int dispatch_error = 0, num_dispatched = 0; if (query) query_shm_size += query->size; @@ -226,14 +236,14 @@ 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) goto out; fd = ret; - ret = send_bin_buffer(fd, buf, sizeof(buf)); + ret = write_all(fd, buf, sizeof(buf)); if (ret < 0) goto out; /* @@ -261,6 +271,7 @@ int send_callback_request(callback_function *f, struct osl_object *query, if (ret < 0) PARA_CRIT_LOG("destroy result failed: %s\n", para_strerror(-ret)); + num_dispatched++; } out: if (shm_destroy(query_shmid) < 0) @@ -268,7 +279,7 @@ out: if (fd >= 0) close(fd); // PARA_DEBUG_LOG("callback_ret: %d\n", ret); - return ret; + return ret < 0? ret : num_dispatched; } /** @@ -281,10 +292,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(). @@ -494,7 +505,7 @@ destroy: no_admissible_files: *(uint32_t *)buf = NO_ADMISSIBLE_FILES; *(uint32_t *)(buf + 4) = (uint32_t)0; - return send_bin_buffer(server_socket, buf, 8); + return write_all(server_socket, buf, 8); } /* Never fails if arg == NULL */ @@ -540,12 +551,37 @@ static int activate_mood_or_playlist(char *arg, int *num_admissible) return 1; } +/** + * Result handler for sending data to the para_client process. + * + * \param result The data to be sent. + * \param band The band designator. + * \param private Pointer to the command context. + * + * \return The return value of the underlying call to \ref command.c::send_sb. + * + * \sa \ref callback_result_handler, \ref command.c::send_sb. + */ +int afs_cb_result_handler(struct osl_object *result, uint8_t band, + void *private) +{ + struct command_context *cc = private; + + assert(cc); + if (!result->size) + return 1; + return send_sb(&cc->scc, result->data, result->size, band, true); +} + static void com_select_callback(int fd, const struct osl_object *query) { struct para_buffer pb = { .max_size = shm_get_shmmax(), - .private_data = &fd, - .max_size_handler = pass_buffer_as_shm + .private_data = &(struct afs_max_size_handler_data) { + .fd = fd, + .band = SBD_OUTPUT + }, + .max_size_handler = afs_max_size_handler, }; char *arg = query->data; int num_admissible, ret, ret2; @@ -571,43 +607,25 @@ static void com_select_callback(int fd, const struct osl_object *query) 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); + 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); + pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset); 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. - * - * \return The return value of the underlying call to sc_send_bin_buffer(). - * - * \sa \ref callback_result_handler, \ref sc_send_bin_buffer(). - */ -int sc_send_result(struct osl_object *result, void *private) -{ - struct stream_cipher_context *scc = private; - - if (!result->size) - return 1; - return sc_send_bin_buffer(scc, result->data, result->size); -} - -int com_select(struct stream_cipher_context *scc, int argc, char * const * const argv) +int com_select(struct command_context *cc) { struct osl_object query; - if (argc != 2) + if (cc->argc != 2) return -E_AFS_SYNTAX; - query.data = argv[1]; - query.size = strlen(argv[1]) + 1; + query.data = cc->argv[1]; + query.size = strlen(cc->argv[1]) + 1; return send_callback_request(com_select_callback, &query, - &sc_send_result, scc); + &afs_cb_result_handler, cc); } static void init_admissible_files(char *arg) @@ -620,25 +638,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_IWOTH); + 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; @@ -702,15 +714,9 @@ static int open_afs_tables(void) return ret; } -static void signal_pre_select(struct sched *s, struct task *t) +static int afs_signal_post_select(struct sched *s, __a_unused void *context) { - 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) -{ - int signum; + int signum, ret; if (getppid() == 1) { PARA_EMERG_LOG("para_server died\n"); @@ -718,42 +724,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; @@ -763,9 +769,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); @@ -777,30 +783,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 is used as the \a max_size handler in a \ref para_buffer - * structure. If used this way, it is called by \ref para_printf() whenever - * the buffer passed to para_printf() is about to exceed its maximal size. * * 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)); + ret = shm_new(size + sizeof(*cr)); if (ret < 0) return ret; shmid = ret; @@ -809,11 +815,12 @@ int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr) goto err; cr = shm; cr->result_size = size; + cr->band = band; memcpy(shm + sizeof(*cr), buf, size); ret = shm_detach(shm); if (ret < 0) goto err; - ret = send_bin_buffer(fd, (char *)&shmid, sizeof(int)); + ret = write_all(fd, (char *)&shmid, sizeof(int)); if (ret >= 0) return ret; err: @@ -899,18 +906,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) { @@ -931,17 +941,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) @@ -950,10 +961,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); } /** @@ -985,6 +998,7 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd) s.default_timeout.tv_sec = 0; s.default_timeout.tv_usec = 999 * 1000; ret = schedule(&s); + sched_shutdown(&s); out_close: close_afs_tables(); out: @@ -1017,11 +1031,11 @@ out: if (ret < 0) para_printf(&pb, "%s\n", para_strerror(-ret)); if (pb.buf) - pass_buffer_as_shm(pb.buf, pb.offset, &fd); + pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset); free(pb.buf); } -int com_init(struct stream_cipher_context *scc, int argc, char * const * const argv) +int com_init(struct command_context *cc) { int i, j, ret; uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1; @@ -1031,13 +1045,13 @@ int com_init(struct stream_cipher_context *scc, int argc, char * const * const a ret = make_database_dir(); if (ret < 0) return ret; - if (argc != 1) { + if (cc->argc != 1) { table_mask = 0; - for (i = 1; i < argc; i++) { + for (i = 1; i < cc->argc; i++) { for (j = 0; j < NUM_AFS_TABLES; j++) { struct afs_table *t = &afs_tables[j]; - if (strcmp(argv[i], t->name)) + if (strcmp(cc->argv[i], t->name)) continue; table_mask |= (1 << j); break; @@ -1047,9 +1061,7 @@ int com_init(struct stream_cipher_context *scc, int argc, char * const * const a } } ret = send_callback_request(create_tables_callback, &query, - sc_send_result, scc); - if (ret < 0) - return sc_send_va_buffer(scc, "%s\n", para_strerror(-ret)); + afs_cb_result_handler, cc); return ret; } @@ -1067,13 +1079,13 @@ enum com_check_flags { CHECK_PLAYLISTS = 4 }; -int com_check(struct stream_cipher_context *scc, int argc, char * const * const argv) +int com_check(struct command_context *cc) { unsigned flags = 0; int i, ret; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + for (i = 1; i < cc->argc; i++) { + const char *arg = cc->argv[i]; if (arg[0] != '-') break; if (!strcmp(arg, "--")) { @@ -1094,25 +1106,25 @@ int com_check(struct stream_cipher_context *scc, int argc, char * const * const } return -E_AFS_SYNTAX; } - if (i < argc) + if (i < cc->argc) return -E_AFS_SYNTAX; if (!flags) flags = ~0U; if (flags & CHECK_AFT) { ret = send_callback_request(aft_check_callback, NULL, - sc_send_result, scc); + afs_cb_result_handler, cc); if (ret < 0) return ret; } if (flags & CHECK_PLAYLISTS) { ret = send_callback_request(playlist_check_callback, - NULL, sc_send_result, scc); + NULL, afs_cb_result_handler, cc); if (ret < 0) return ret; } if (flags & CHECK_MOODS) { ret = send_callback_request(mood_check_callback, NULL, - sc_send_result, scc); + afs_cb_result_handler, cc); if (ret < 0) return ret; }