Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions shell/job.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ static bool _job_wait(struct mrsh_state *state, pid_t pid, int options) {
// shell. Child processes want to block until their own children have
// terminated.
if (!priv->child) {
#ifdef WCONTINUED
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this the Archlinux build fails which is interesting since this was introduced in Linux afaik. It could be because of the build options though if they are excluding this somehow by specifying an older standard.

options |= WUNTRACED | WCONTINUED;
#else
options |= WUNTRACED;
#endif
}

while (true) {
Expand Down Expand Up @@ -294,12 +298,14 @@ bool refresh_jobs_status(struct mrsh_state *state) {

for (size_t i = 0; i < priv->jobs.len; ++i) {
struct mrsh_job *job = priv->jobs.data[i];
struct mrsh_process *proc = job_get_running_process(job);
if (proc == NULL) {
continue;
}
if (!_job_wait(job->state, proc->pid, WNOHANG)) {
return false;
for (size_t j = 0; j < job->processes.len; ++j) {
struct mrsh_process *proc = job->processes.data[j];
int status = process_poll(proc);
if (status == TASK_STATUS_WAIT || status == TASK_STATUS_STOPPED) {
if (!_job_wait(job->state, proc->pid, WNOHANG)) {
return false;
}
}
}
}

Expand All @@ -326,9 +332,7 @@ static void update_job(struct mrsh_state *state, pid_t pid, int stat) {
struct mrsh_job *job = priv->jobs.data[i];

int status = job_poll(job);
if (status >= 0) {
job_queue_notification(job);
}
job_queue_notification(job);
if (status != TASK_STATUS_WAIT && job->pgid > 0) {
job_set_foreground(job, false, false);
}
Expand Down
6 changes: 6 additions & 0 deletions shell/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,17 @@ void update_process(struct mrsh_state *state, pid_t pid, int stat) {
}

if (WIFEXITED(stat) || WIFSIGNALED(stat)) {
proc->stopped = false;
proc->terminated = true;
proc->stat = stat;
} else if (WIFSTOPPED(stat)) {
proc->stopped = true;
proc->signal = WSTOPSIG(stat);
#ifdef WCONTINUED
} else if (WIFCONTINUED(stat)) {
proc->stopped = false;
proc->signal = SIGCONT;
#endif
} else {
abort();
}
Expand Down
19 changes: 19 additions & 0 deletions shell/task/simple_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ static int run_process(struct mrsh_context *ctx, struct mrsh_simple_command *sc,
init_job_child_process(state);
}

// XXX Does this need to happen other places?
// Maybe we need a fork abstraction that handles job control??
if (ctx->background && !(state->options & MRSH_OPT_MONITOR)) {
// If job control is disabled, stdin is /dev/null
int fd = open("/dev/null", O_CLOEXEC | O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open /dev/null: %s\n",
strerror(errno));
exit(1);
}
if (fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(fd);
}
}

for (size_t i = 0; i < sc->assignments.len; ++i) {
struct mrsh_assignment *assign = sc->assignments.data[i];
uint32_t prev_attribs;
Expand Down Expand Up @@ -110,6 +126,9 @@ static int run_process(struct mrsh_context *ctx, struct mrsh_simple_command *sc,
free(path);

struct mrsh_process *process = init_child(ctx, pid);
if (ctx->background) {
return TASK_STATUS_WAIT;
}
return job_wait_process(process);
}

Expand Down
83 changes: 20 additions & 63 deletions shell/task/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ static int run_subshell(struct mrsh_context *ctx, struct mrsh_array *array) {
perror("fork");
return TASK_STATUS_ERROR;
} else if (pid == 0) {
ctx->background = false;
priv->child = true;

reset_caught_traps(ctx->state);

if (!(ctx->state->options & MRSH_OPT_MONITOR)) {
if (ctx->state->options & MRSH_OPT_MONITOR) {
// XXX create a shared init_child func, maybe job_init_child()??
struct mrsh_process *proc = process_create(ctx->state, getpid());
job_add_process(ctx->job, proc);
init_job_child_process(ctx->state);
} else {
// If job control is disabled, stdin is /dev/null
int fd = open("/dev/null", O_CLOEXEC | O_RDONLY);
int fd = open("/dev/null", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open /dev/null: %s\n",
strerror(errno));
Expand All @@ -50,6 +54,12 @@ static int run_subshell(struct mrsh_context *ctx, struct mrsh_array *array) {
}

struct mrsh_process *proc = process_create(ctx->state, pid);
if (ctx->background) {
if (ctx->state->options & MRSH_OPT_MONITOR) {
job_add_process(ctx->job, proc);
}
return TASK_STATUS_WAIT;
}
return job_wait_process(proc);
}

Expand Down Expand Up @@ -311,23 +321,8 @@ int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_li
abort();
}

/**
* Put the process into its job's process group. This has to be done both in the
* parent and the child because of potential race conditions.
*/
static struct mrsh_process *init_async_child(struct mrsh_context *ctx, pid_t pid) {
struct mrsh_process *proc = process_create(ctx->state, pid);

if (ctx->state->options & MRSH_OPT_MONITOR) {
job_add_process(ctx->job, proc);
}

return proc;
}

int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array) {
struct mrsh_state *state = ctx->state;
struct mrsh_state_priv *priv = state_get_priv(state);

int ret = 0;
for (size_t i = 0; i < array->len; ++i) {
Expand All @@ -338,53 +333,15 @@ int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array) {
if (child_ctx.job == NULL) {
child_ctx.job = job_create(state, &list->node);
}

pid_t pid = fork();
if (pid < 0) {
perror("fork");
return TASK_STATUS_ERROR;
} else if (pid == 0) {
ctx = NULL; // Use child_ctx instead
priv->child = true;

init_async_child(&child_ctx, getpid());
if (state->options & MRSH_OPT_MONITOR) {
init_job_child_process(state);
}

if (!(state->options & MRSH_OPT_MONITOR)) {
// If job control is disabled, stdin is /dev/null
int fd = open("/dev/null", O_CLOEXEC | O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open /dev/null: %s\n",
strerror(errno));
exit(1);
}
if (fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(fd);
}
}

int ret = run_and_or_list(&child_ctx, list->and_or_list);
if (ret < 0) {
exit(127);
}
exit(ret);
}

ret = 0;
init_async_child(&child_ctx, pid);
} else {
ret = run_and_or_list(ctx, list->and_or_list);
if (ret < 0) {
return ret;
}
run_and_or_list(&child_ctx, list->and_or_list);
continue;
}

if (ret >= 0) {
state->last_status = ret;
ret = run_and_or_list(ctx, list->and_or_list);
if (ret < 0) {
return ret;
}
state->last_status = ret;
}
return ret;
}
Expand Down
19 changes: 19 additions & 0 deletions test/jobs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/sh
set -me

sleep 10&
echo "$(jobs)"
echo "$(jobs -p %1)"
[ -z "$(jobs %1)" ]
jobs %1 | grep "Running"
kill -STOP $(jobs -p %1)
jobs %1 | grep "SIGSTOP"
kill $(jobs -p %1)
[ -z "$(jobs %1)" ]

sleep 10&
kill -STOP $(jobs -p %1)
kill -CONT $(jobs -p %1)
jobs %1 | grep "Running"
kill $(jobs -p %1)
[ -z "$(jobs %1)" ]