PARA_ERROR(BTR_EOF, "buffer tree: end of file"), \
PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \
PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \
+ PARA_ERROR(CHILD_CONTEXT, "now running in child context"), \
PARA_ERROR(CHMOD, "failed to set socket mode"), \
PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
PARA_ERROR(CLIENT_WRITE, "client write error"), \
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. */
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;
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)
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;
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
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));
int ret;
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));
mutex_lock(mmd_mutex);
ret = schedule(&sched);
sched_shutdown(&sched);
+ signal_shutdown(signal_task);
+ if (sct->child_fd < 0) { /* parent (server) */
+ if (ret < 0)
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ } else { /* child (command handler) */
+ /*
+ * We hold the lock: it was re-acquired in server_select()
+ * after the select call.
+ */
+ mutex_unlock(mmd_mutex);
+ alarm(ALARM_TIMEOUT);
+ close_listed_fds();
+ ret = handle_connect(sct->child_fd);
+ }
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);
}