X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=server.c;h=e0d50f4f8689e1de283ef80f8341504bcf386d33;hp=593d92c5d3717fd3965336ca1c57d9a218377521;hb=HEAD;hpb=659b46df9a49e0677fd7ff85a9842adac4abec84 diff --git a/server.c b/server.c index 593d92c5..ea9cc9c0 100644 --- a/server.c +++ b/server.c @@ -2,31 +2,6 @@ /** \file server.c Paraslash's main server. */ -/** - * \mainpage Main data structures and selected APIs: - * - * - Senders: \ref sender, - * - Audio file selector: \ref afs_info, \ref afs_table, - * - Audio format handler: \ref audio_format_handler, \ref afh_info - * - Receivers/filters/writers: \ref receiver, \ref receiver_node, - * \ref filter, \ref filter_node, \ref writer_node, \ref writer. - * - Scheduling: \ref sched.h, - * - Buffer trees: \ref buffer_tree.h, - * - Sideband API: \ref sideband.h, - * - Crypto: \ref crypt.h, \ref crypt_backend.h, - * - Error subsystem: \ref error.h, - * - Inter process communication: \ref ipc.h, - * - Forward error correction: \ref fec.h, - * - Daemons: \ref daemon.h, - * - Mixer API: \ref mix.h, - * - Interactive sessions: \ref interactive.h, - * - File descriptors: \ref fd.h, - * - Signals: \ref signal.h, - * - Networking: \ref net.h, - * - Time: \ref time.c, - * - Doubly linked lists: \ref list.h. - */ - #include #include #include @@ -41,18 +16,19 @@ #include "server.lsg.h" #include "para.h" #include "error.h" +#include "lsu.h" #include "crypt.h" #include "afh.h" #include "string.h" #include "afs.h" +#include "net.h" #include "server.h" #include "list.h" -#include "send.h" #include "sched.h" +#include "send.h" #include "vss.h" #include "config.h" #include "close_on_fork.h" -#include "net.h" #include "daemon.h" #include "ipc.h" #include "fd.h" @@ -109,7 +85,7 @@ static struct signal_task *signal_task; /** The process id of the audio file selector process. */ pid_t afs_pid = 0; -/* The the main server process (parent of afs and the command handlers). */ +/* The main server process (parent of afs and the command handlers). */ static pid_t server_pid; /** @@ -129,8 +105,9 @@ bool process_is_command_handler(void) /** The task responsible for server command handling. */ struct server_command_task { - /** TCP port on which para_server listens for connections. */ - int listen_fd; + unsigned num_listen_fds; /* only one by default */ + /** TCP socket(s) on which para_server listens for connections. */ + int *listen_fds; /* File descriptor for the accepted socket. */ int child_fd; /** Copied from para_server's main function. */ @@ -195,6 +172,7 @@ static void init_ipc_or_die(void) mmd->active_connections = 0; mmd->vss_status_flags = VSS_NEXT; mmd->new_vss_status_flags = VSS_NEXT; + mmd->loglevel = OPT_UINT32_VAL(LOGLEVEL); return; destroy_mmd_mutex: mutex_destroy(mmd_mutex); @@ -203,6 +181,9 @@ err_out: exit(EXIT_FAILURE); } +/** Get a reference to the supercommand of para_server. */ +#define CMD_PTR (lls_cmd(0, server_suite)) + /** * (Re-)read the server configuration files. * @@ -214,57 +195,21 @@ err_out: void parse_config_or_die(bool reload) { int ret; - char *cf = NULL, *errctx = NULL, *user_list_file = NULL; - void *map; - size_t sz; - int cf_argc; - char **cf_argv; - struct lls_parse_result *cf_lpr, *merged_lpr; - char *home = para_homedir(); - - if (OPT_GIVEN(CONFIG_FILE)) - cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE)); - else - cf = make_message("%s/.paraslash/server.conf", home); - if (!mmd || getpid() != afs_pid) { - if (OPT_GIVEN(USER_LIST)) - user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST)); - else - user_list_file = make_message("%s/.paraslash/server.users", home); - } - free(home); - ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); - if (ret < 0) { - if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT)) - goto free_cf; - if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE)) - goto free_cf; - server_lpr = cmdline_lpr; - goto success; - } - ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx)); - para_munmap(map, sz); - if (ret < 0) - goto free_cf; - cf_argc = ret; - ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx)); - lls_free_argv(cf_argv); - if (ret < 0) - goto free_cf; - if (reload) /* config file overrides command line */ - ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr, - &errctx)); - else /* command line options override config file options */ - ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr, - &errctx)); - lls_free_parse_result(cf_lpr, CMD_PTR); - if (ret < 0) - goto free_cf; + unsigned flags = MCF_DONT_FREE; + if (server_lpr != cmdline_lpr) lls_free_parse_result(server_lpr, CMD_PTR); - server_lpr = merged_lpr; -success: - daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL)); + server_lpr = cmdline_lpr; + if (reload) + flags |= MCF_OVERRIDE; + ret = lsu_merge_config_file_options(OPT_STRING_VAL(CONFIG_FILE), + "server.conf", &server_lpr, CMD_PTR, server_suite, flags); + if (ret < 0) { + PARA_EMERG_LOG("failed to parse config file: %s\n", + para_strerror(-ret)); + exit(EXIT_FAILURE); + } + daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL)); if (OPT_GIVEN(LOGFILE)) { daemon_set_logfile(OPT_STRING_VAL(LOGFILE)); daemon_open_log_or_die(); @@ -282,19 +227,19 @@ success: if (OPT_GIVEN(LOG_TIMING)) daemon_set_flag(DF_LOG_TIMING); daemon_set_priority(OPT_UINT32_VAL(PRIORITY)); - if (user_list_file) - init_user_list(user_list_file); - ret = 1; -free_cf: - free(cf); - free(user_list_file); - if (ret < 0) { - if (errctx) - PARA_ERROR_LOG("%s\n", errctx); - free(errctx); - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); + if (!reload || getpid() != afs_pid) { + char *user_list_file; + if (OPT_GIVEN(USER_LIST)) + user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST)); + else { + char *home = para_homedir(); + user_list_file = make_message("%s/.paraslash/server.users", home); + free(home); + } + user_list_init(user_list_file); + free(user_list_file); } + return; } /* @@ -309,14 +254,14 @@ static void handle_sighup(void) kill(afs_pid, SIGHUP); } -static int signal_post_select(struct sched *s, __a_unused void *context) +static int signal_post_monitor(struct sched *s, __a_unused void *context) { int ret, signum; ret = task_get_notification(signal_task->task); if (ret < 0) return ret; - signum = para_next_signal(&s->rfds); + signum = para_next_signal(); switch (signum) { case 0: return 0; @@ -332,14 +277,14 @@ static int signal_post_select(struct sched *s, __a_unused void *context) if (pid != afs_pid) continue; PARA_EMERG_LOG("fatal: afs died\n"); - kill(0, SIGTERM); - goto cleanup; + goto genocide; } break; /* die on sigint/sigterm. Kill all children too. */ case SIGINT: case SIGTERM: PARA_EMERG_LOG("terminating on signal %d\n", signum); +genocide: kill(0, SIGTERM); /* * We must wait for all of our children to die. For the afs @@ -354,7 +299,6 @@ static int signal_post_select(struct sched *s, __a_unused void *context) while (wait(NULL) != -1 || errno != ECHILD) ; /* still at least one child alive */ mutex_lock(mmd_mutex); -cleanup: free(mmd->afd.afhi.chunk_table); task_notify_all(s, E_DEADLY_SIGNAL); return -E_DEADLY_SIGNAL; @@ -373,31 +317,31 @@ static void init_signal_task(void) add_close_on_fork_list(signal_task->fd); signal_task->task = task_register(&(struct task_info) { .name = "signal", - .pre_select = signal_pre_select, - .post_select = signal_post_select, + .pre_monitor = signal_pre_monitor, + .post_monitor = signal_post_monitor, .context = signal_task, }, &sched); } -static void command_pre_select(struct sched *s, void *context) +static void command_pre_monitor(struct sched *s, void *context) { + unsigned n; struct server_command_task *sct = context; - para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno); + + for (n = 0; n < sct->num_listen_fds; n++) + sched_monitor_readfd(sct->listen_fds[n], s); } -static int command_post_select(struct sched *s, void *context) +static int command_task_accept(unsigned listen_idx, struct sched *s, + struct server_command_task *sct) { - struct server_command_task *sct = context; int new_fd, ret, i; char *peer_name; pid_t child_pid; uint32_t *chunk_table; - ret = task_get_notification(sct->task); - if (ret < 0) - return ret; - ret = para_accept(sct->listen_fd, &s->rfds, NULL, 0, &new_fd); + ret = para_accept(sct->listen_fds[listen_idx], NULL, 0, &new_fd); if (ret <= 0) goto out; mmd->num_connects++; @@ -443,13 +387,13 @@ static int command_post_select(struct sched *s, void *context) /* * After we return, the scheduler calls server_select() with a minimal * timeout value, because the remaining tasks have a notification - * pending. Next it calls the ->post_select method of these tasks, + * pending. Next it calls the ->post_monitor method of these tasks, * which will return negative in view of the notification. This causes * schedule() to return as there are no more runnable tasks. * * Note that semaphores are not inherited across a fork(), so we don't - * hold the lock at this point. Since server_select() drops the lock - * prior to calling para_select(), we need to acquire it here. + * hold the lock at this point. Since server_poll() drops the lock + * prior to calling poll(), we need to acquire it here. */ mutex_lock(mmd_mutex); return -E_CHILD_CONTEXT; @@ -459,29 +403,77 @@ out: return 0; } +static int command_post_monitor(struct sched *s, void *context) +{ + struct server_command_task *sct = context; + unsigned n; + int ret; + + ret = task_get_notification(sct->task); + if (ret < 0) + goto fail; + for (n = 0; n < sct->num_listen_fds; n++) { + ret = command_task_accept(n, s, sct); + if (ret < 0) + goto fail; + } + return 0; +fail: + free(sct->listen_fds); + return ret; +} + static void init_server_command_task(struct server_command_task *sct, int argc, char **argv) { int ret; + unsigned n; + uint32_t port = OPT_UINT32_VAL(PORT); PARA_NOTICE_LOG("initializing tcp command socket\n"); sct->child_fd = -1; sct->argc = argc; sct->argv = argv; - ret = para_listen_simple(IPPROTO_TCP, OPT_UINT32_VAL(PORT)); - if (ret < 0) - goto err; - sct->listen_fd = ret; - ret = mark_fd_nonblocking(sct->listen_fd); - if (ret < 0) - goto err; - add_close_on_fork_list(sct->listen_fd); /* child doesn't need the listener */ + if (!OPT_GIVEN(LISTEN_ADDRESS)) { + sct->num_listen_fds = 1; + sct->listen_fds = alloc(sizeof(int)); + ret = para_listen_simple(IPPROTO_TCP, port); + if (ret < 0) + goto err; + sct->listen_fds[0] = ret; + } else { + sct->num_listen_fds = OPT_GIVEN(LISTEN_ADDRESS); + sct->listen_fds = alloc(sct->num_listen_fds * sizeof(int)); + for (n = 0; n < OPT_GIVEN(LISTEN_ADDRESS); n++) { + const char *arg; + arg = lls_string_val(n, OPT_RESULT(LISTEN_ADDRESS)); + ret = para_listen(IPPROTO_TCP, arg, port); + if (ret < 0) + goto err; + sct->listen_fds[n] = ret; + } + } + for (n = 0; n < sct->num_listen_fds; n++) { + ret = mark_fd_nonblocking(sct->listen_fds[n]); + if (ret < 0) + goto err; + /* child doesn't need the listener */ + add_close_on_fork_list(sct->listen_fds[n]); + } + sct->task = task_register(&(struct task_info) { .name = "server command", - .pre_select = command_pre_select, - .post_select = command_post_select, + .pre_monitor = command_pre_monitor, + .post_monitor = command_post_monitor, .context = sct, }, &sched); + /* + * Detect whether the abstract Unix domain socket space is supported, + * but do not create the socket. We check this once in server context + * so that the command handlers inherit this bit of information and + * don't need to check again. + */ + create_local_socket(NULL); return; err: PARA_EMERG_LOG("%s\n", para_strerror(-ret)); @@ -505,6 +497,8 @@ static int init_afs(int argc, char **argv) int i; afs_pid = getpid(); + crypt_shutdown(); + user_list_deplete(); for (i = argc - 1; i >= 0; i--) memset(argv[i], 0, strlen(argv[i])); i = argc - lls_num_inputs(cmdline_lpr) - 1; @@ -553,7 +547,7 @@ static void server_init(int argc, char **argv, struct server_command_task *sct) if (ret < 0) goto fail; server_lpr = cmdline_lpr; - daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL)); + daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL)); daemon_drop_privileges_or_die(OPT_STRING_VAL(USER), OPT_STRING_VAL(GROUP)); version_handle_flag("server", OPT_GIVEN(VERSION)); @@ -563,14 +557,11 @@ static void server_init(int argc, char **argv, struct server_command_task *sct) if (OPT_GIVEN(DAEMON)) daemon_pipe = daemonize(true /* parent waits for SIGTERM */); server_pid = getpid(); - init_random_seed_or_die(); + crypt_init(); daemon_log_welcome("server"); init_ipc_or_die(); /* init mmd struct, mmd and log mutex */ daemon_set_start_time(); daemon_set_hooks(pre_log_hook, post_log_hook); - PARA_NOTICE_LOG("initializing audio format handlers\n"); - afh_init(); - /* * Although afs uses its own signal handling we must ignore SIGUSR1 * _before_ the afs child process gets born by init_afs() below. It's @@ -630,18 +621,33 @@ out: killpg(0, SIGUSR1); } -static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds, - struct timeval *timeout_tv) +static int server_poll(struct pollfd *fds, nfds_t nfds, int timeout) { int ret; + daemon_set_loglevel(mmd->loglevel); status_refresh(); mutex_unlock(mmd_mutex); - ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv); + ret = xpoll(fds, nfds, timeout); mutex_lock(mmd_mutex); return ret; } +/** + * Deallocate all lopsub parse results. + * + * The server allocates a parse result for command line options and optionally + * a second parse result for the effective configuration, defined by merging + * the command line options with the options stored in the configuration file. + * This function frees both structures. + */ +void free_lpr(void) +{ + lls_free_parse_result(server_lpr, CMD_PTR); + if (server_lpr != cmdline_lpr) + lls_free_parse_result(cmdline_lpr, CMD_PTR); +} + /** * The main function of para_server. * @@ -656,36 +662,36 @@ int main(int argc, char *argv[]) struct server_command_task server_command_task_struct, *sct = &server_command_task_struct; - sched.default_timeout.tv_sec = 1; - sched.select_function = server_select; + sched.default_timeout = 1000; + sched.poll_function = server_poll; server_init(argc, argv, sct); mutex_lock(mmd_mutex); ret = schedule(&sched); /* - * We hold the mmd lock: it was re-acquired in server_select() - * after the select call. + * We hold the mmd lock: it was re-acquired in server_poll() + * after the poll(2) call. */ mutex_unlock(mmd_mutex); sched_shutdown(&sched); + crypt_shutdown(); signal_shutdown(signal_task); if (!process_is_command_handler()) { /* parent (server) */ mutex_destroy(mmd_mutex); daemon_set_hooks(NULL, NULL); /* only one process remaining */ mutex_destroy(log_mutex); - shm_detach(mmd); deplete_close_on_fork_list(); if (ret < 0) PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + vss_shutdown(); } else { + vss_shutdown(); alarm(ALARM_TIMEOUT); close_listed_fds(); ret = handle_connect(sct->child_fd); } - vss_shutdown(); shm_detach(mmd); - lls_free_parse_result(server_lpr, CMD_PTR); - if (server_lpr != cmdline_lpr) - lls_free_parse_result(cmdline_lpr, CMD_PTR); + user_list_deplete(); + free_lpr(); exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS); }