+/*
+ * called when server gets SIGHUP or when client invokes hup command.
+ */
+static void handle_sighup(void)
+{
+ PARA_NOTICE_LOG("SIGHUP\n");
+ parse_config_or_die(1); /* reopens log */
+ init_user_list(user_list_file); /* reload user list */
+ if (mmd->afs_pid)
+ kill(mmd->afs_pid, SIGHUP);
+}
+
+static void signal_post_select(struct sched *s, struct task *t)
+{
+ struct signal_task *st = container_of(t, struct signal_task, task);
+
+ if (!FD_ISSET(st->fd, &s->rfds))
+ return;
+
+ st->signum = para_next_signal();
+ switch (st->signum) {
+ case SIGHUP:
+ handle_sighup();
+ break;
+ case SIGCHLD:
+ for (;;) {
+ pid_t pid;
+ int ret = para_reap_child(&pid);
+ if (ret <= 0)
+ break;
+ if (pid != mmd->afs_pid)
+ continue;
+ PARA_EMERG_LOG("fatal: afs died\n");
+ kill(0, SIGTERM);
+ goto cleanup;
+ }
+ break;
+ /* die on sigint/sigterm. Kill all children too. */
+ case SIGINT:
+ case SIGTERM:
+ PARA_EMERG_LOG("terminating on signal %d\n", st->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
+ * 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)mmd->afs_pid);
+ waitpid(mmd->afs_pid, NULL, 0);
+cleanup:
+ free(mmd->afd.afhi.chunk_table);
+ close_listed_fds();
+ mutex_destroy(mmd_mutex);
+ shm_detach(mmd);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void init_signal_task(void)
+{
+ static struct signal_task signal_task_struct,
+ *st = &signal_task_struct;
+
+ st->task.pre_select = signal_pre_select;
+ st->task.post_select = signal_post_select;
+ sprintf(st->task.status, "signal task");
+
+ PARA_NOTICE_LOG("setting up signal handling\n");
+ st->fd = para_signal_init(); /* always successful */
+ para_install_sighandler(SIGINT);
+ para_install_sighandler(SIGTERM);
+ para_install_sighandler(SIGHUP);
+ para_install_sighandler(SIGCHLD);
+ para_sigaction(SIGPIPE, SIG_IGN);
+ add_close_on_fork_list(st->fd);
+ register_task(&st->task);
+}
+
+static void command_pre_select(struct sched *s, struct task *t)
+{
+ struct server_command_task *sct = container_of(t, struct server_command_task, task);
+ para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno);
+}
+
+static void command_post_select(struct sched *s, struct task *t)
+{
+ struct server_command_task *sct = container_of(t, struct server_command_task, task);
+
+ int new_fd, ret, i;
+ char *peer_name;
+ pid_t child_pid;
+ uint32_t *chunk_table;
+
+ if (!FD_ISSET(sct->listen_fd, &s->rfds))
+ return;
+ ret = para_accept(sct->listen_fd, NULL, 0);
+ if (ret < 0)
+ goto out;
+ new_fd = ret;
+ peer_name = remote_name(new_fd);
+ PARA_INFO_LOG("got connection from %s, forking\n", peer_name);
+ mmd->num_connects++;
+ mmd->active_connections++;
+ /*
+ * The chunk table is a pointer located in the mmd struct that points
+ * to dynamically allocated memory, i.e. it must be freed by the parent
+ * and the child. However, as the mmd struct is in a shared memory
+ * area, there's no guarantee that after the fork this pointer is still
+ * valid in child context. As it is not used in the child anyway, we
+ * save it to a local variable before the fork and free the memory via
+ * that copy in the child directly after the fork.
+ */
+ chunk_table = mmd->afd.afhi.chunk_table;
+ child_pid = fork();
+ if (child_pid < 0) {
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+ goto out;
+ }
+ if (child_pid) {
+ close(new_fd);
+ /* parent keeps accepting connections */
+ return;
+ }
+ /* mmd might already have changed at this point */
+ free(chunk_table);
+ alarm(ALARM_TIMEOUT);
+ close_listed_fds();
+ para_signal_shutdown();
+ /*
+ * put info on who we are serving into argv[0] to make
+ * client ip visible in top/ps
+ */
+ for (i = sct->argc - 1; i >= 0; i--)
+ memset(sct->argv[i], 0, strlen(sct->argv[i]));
+ sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
+ return handle_connect(new_fd, peer_name);
+out:
+ if (ret < 0)
+ PARA_CRIT_LOG("%s\n", para_strerror(-ret));