X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=server.c;h=8b13d35b423cfac49e18e433aa31af09db4b4ad4;hp=2018b8eece1988206e2992f560b65fa73059ff5e;hb=089fb2fb2d9e2a3473aa6fac91681ca8ddfffff3;hpb=767a4a54c967bc4b80bd14d02e89fe91acd848dd diff --git a/server.c b/server.c index 2018b8ee..8b13d35b 100644 --- a/server.c +++ b/server.c @@ -86,22 +86,53 @@ struct lls_parse_result *server_lpr = NULL; /* Command line options (no config file options). Used in handle_sighup(). */ static struct lls_parse_result *cmdline_lpr; -/** A random value used in child context for authentication. */ +/** + * A random number used to "authenticate" the afs 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. + */ uint32_t afs_socket_cookie; /** The mutex protecting the shared memory area containing the mmd struct. */ int mmd_mutex; +/* Serializes log output. */ +static int log_mutex; + static struct sched sched; 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). */ +static pid_t server_pid; + +/** + * Tell whether the executing process is a command handler. + * + * Cleanup on exit must be performed differently for command handlers. + * + * \return True if the pid of the executing process is neither the server pid + * nor the afs pid. + */ +bool process_is_command_handler(void) +{ + pid_t pid = getpid(); + + return pid != afs_pid && pid != server_pid; +} + /** The task responsible for server command handling. */ struct server_command_task { /** TCP port on which para_server listens for connections. */ int listen_fd; + /* File descriptor for the accepted socket. */ + int child_fd; /** Copied from para_server's main function. */ int argc; /** Argument vector passed to para_server's main function. */ @@ -123,9 +154,17 @@ char *server_get_tasks(void) return get_task_list(&sched); } -/* - * setup shared memory area and get mutex for locking - */ +static void pre_log_hook(void) +{ + mutex_lock(log_mutex); +} + +static void post_log_hook(void) +{ + mutex_unlock(log_mutex); +} + +/* Setup shared memory area and init mutexes */ static void init_ipc_or_die(void) { void *shm; @@ -144,6 +183,10 @@ static void init_ipc_or_die(void) if (ret < 0) goto err_out; mmd_mutex = ret; + ret = mutex_new(); + if (ret < 0) + goto destroy_mmd_mutex; + log_mutex = ret; mmd->num_played = 0; mmd->num_commands = 0; @@ -153,6 +196,8 @@ static void init_ipc_or_die(void) mmd->vss_status_flags = VSS_NEXT; mmd->new_vss_status_flags = VSS_NEXT; return; +destroy_mmd_mutex: + mutex_destroy(mmd_mutex); err_out: PARA_EMERG_LOG("%s\n", para_strerror(-ret)); exit(EXIT_FAILURE); @@ -177,7 +222,6 @@ void parse_config_or_die(bool reload) struct lls_parse_result *cf_lpr, *merged_lpr; char *home = para_homedir(); - daemon_close_log(); if (OPT_GIVEN(CONFIG_FILE)) cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE)); else @@ -239,7 +283,7 @@ success: daemon_set_flag(DF_LOG_TIMING); daemon_set_priority(OPT_UINT32_VAL(PRIORITY)); if (user_list_file) - init_user_list(user_list_file); + user_list_init(user_list_file); ret = 1; free_cf: free(cf); @@ -267,8 +311,12 @@ static void handle_sighup(void) static int signal_post_select(struct sched *s, __a_unused void *context) { - int signum = para_next_signal(&s->rfds); + int ret, signum; + ret = task_get_notification(signal_task->task); + if (ret < 0) + return ret; + signum = para_next_signal(&s->rfds); switch (signum) { case 0: return 0; @@ -278,7 +326,7 @@ static int signal_post_select(struct sched *s, __a_unused void *context) case SIGCHLD: for (;;) { pid_t pid; - int ret = para_reap_child(&pid); + ret = para_reap_child(&pid); if (ret <= 0) break; if (pid != afs_pid) @@ -294,27 +342,22 @@ static int signal_post_select(struct sched *s, __a_unused void *context) PARA_EMERG_LOG("terminating on signal %d\n", signum); kill(0, SIGTERM); /* - * We must wait for afs because afs catches SIGINT/SIGTERM. - * Before reacting to the signal, afs might want to use the + * We must wait for all of our children to die. For the afs + * process or a command handler might want to use the * shared memory area and the mmd mutex. If we destroy this * mutex too early and afs tries to lock the shared memory * area, the call to mutex_lock() will fail and terminate the * afs process. This leads to dirty osl tables. - * - * There's no such problem with the other children of the - * server process (the command handlers) as these reset their - * SIGINT/SIGTERM handlers to the default action, i.e. these - * processes get killed immediately by the above kill(). */ - PARA_INFO_LOG("waiting for afs (pid %d) to die\n", - (int)afs_pid); - waitpid(afs_pid, NULL, 0); + PARA_INFO_LOG("waiting for child processes to die\n"); + mutex_unlock(mmd_mutex); + while (wait(NULL) != -1 || errno != ECHILD) + ; /* still at least one child alive */ + mutex_lock(mmd_mutex); cleanup: free(mmd->afd.afhi.chunk_table); - close_listed_fds(); - mutex_destroy(mmd_mutex); - shm_detach(mmd); - exit(EXIT_FAILURE); + task_notify_all(s, E_DEADLY_SIGNAL); + return -E_DEADLY_SIGNAL; } return 0; } @@ -346,12 +389,14 @@ static void command_pre_select(struct sched *s, void *context) static int command_post_select(struct sched *s, void *context) { 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); if (ret <= 0) goto out; @@ -384,9 +429,7 @@ static int command_post_select(struct sched *s, void *context) PARA_INFO_LOG("accepted connection from %s\n", peer_name); /* mmd might already have changed at this point */ free(chunk_table); - alarm(ALARM_TIMEOUT); - close_listed_fds(); - signal_shutdown(signal_task); + sct->child_fd = new_fd; /* * put info on who we are serving into argv[0] to make * client ip visible in top/ps @@ -395,21 +438,34 @@ static int command_post_select(struct sched *s, void *context) memset(sct->argv[i], 0, strlen(sct->argv[i])); i = sct->argc - 1 - lls_num_inputs(cmdline_lpr); sprintf(sct->argv[i], "para_server (serving %s)", peer_name); - handle_connect(new_fd); - /* never reached*/ + /* ask other tasks to terminate */ + task_notify_all(s, E_CHILD_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, + * 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. + */ + mutex_lock(mmd_mutex); + return -E_CHILD_CONTEXT; out: if (ret < 0) PARA_CRIT_LOG("%s\n", para_strerror(-ret)); return 0; } -static void init_server_command_task(int argc, char **argv) +static void init_server_command_task(struct server_command_task *sct, + int argc, char **argv) { int ret; - static struct server_command_task server_command_task_struct, - *sct = &server_command_task_struct; 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)); @@ -454,7 +510,7 @@ static int init_afs(int argc, char **argv) i = argc - lls_num_inputs(cmdline_lpr) - 1; sprintf(argv[i], "para_server (afs)"); close(afs_server_socket[0]); - afs_init(afs_socket_cookie, afs_server_socket[1]); + afs_init(afs_server_socket[1]); } close(afs_server_socket[1]); if (read(afs_server_socket[0], &c, 1) <= 0) { @@ -486,7 +542,7 @@ static void handle_help_flags(void) exit(EXIT_SUCCESS); } -static void server_init(int argc, char **argv) +static void server_init(int argc, char **argv, struct server_command_task *sct) { int ret, afs_socket, daemon_pipe = -1; char *errctx; @@ -506,10 +562,12 @@ static void server_init(int argc, char **argv) /* become daemon */ if (OPT_GIVEN(DAEMON)) daemon_pipe = daemonize(true /* parent waits for SIGTERM */); + server_pid = getpid(); init_random_seed_or_die(); daemon_log_welcome("server"); - init_ipc_or_die(); /* init mmd struct and mmd->lock */ + 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(); @@ -532,7 +590,7 @@ static void server_init(int argc, char **argv) para_unblock_signal(SIGCHLD); PARA_NOTICE_LOG("initializing virtual streaming system\n"); vss_init(afs_socket, &sched); - init_server_command_task(argc, argv); + init_server_command_task(sct, argc, argv); if (daemon_pipe >= 0) { if (write(daemon_pipe, "\0", 1) < 0) { PARA_EMERG_LOG("daemon_pipe: %s", strerror(errno)); @@ -595,18 +653,40 @@ static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds, int main(int argc, char *argv[]) { int ret; + struct server_command_task server_command_task_struct, + *sct = &server_command_task_struct; sched.default_timeout.tv_sec = 1; sched.select_function = server_select; - server_init(argc, argv); + 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. + */ + mutex_unlock(mmd_mutex); sched_shutdown(&sched); + 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)); + } else { + alarm(ALARM_TIMEOUT); + close_listed_fds(); + ret = handle_connect(sct->child_fd); + } + vss_shutdown(); + shm_detach(mmd); + user_list_deplete(); lls_free_parse_result(server_lpr, CMD_PTR); if (server_lpr != cmdline_lpr) lls_free_parse_result(cmdline_lpr, CMD_PTR); - if (ret < 0) - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS); }