~emersion/mrsh

bae66ff12baba00898fae84b5173504754252248 — Simon Ser 1 year, 1 month ago ee80db4
Create a job when executing a subshell
4 files changed, 63 insertions(+), 44 deletions(-)

M include/shell/shell.h
M shell/shell.c
M shell/task/async.c
M shell/task/subshell.c
M include/shell/shell.h => include/shell/shell.h +4 -2
@@ 2,8 2,8 @@
#define SHELL_SHELL_H

#include <mrsh/shell.h>

struct mrsh_job;
#include "job.h"
#include "process.h"

/**
 * A context holds state information and per-job information. A context is


@@ 18,4 18,6 @@ struct context {
	bool background;
};

pid_t subshell_fork(struct context *ctx, struct process **process_ptr);

#endif

M shell/shell.c => shell/shell.c +55 -0
@@ 1,5 1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <mrsh/hashtable.h>
#include <mrsh/parser.h>
#include <stdlib.h>


@@ 157,3 158,57 @@ int mrsh_run_word(struct mrsh_state *state, struct mrsh_word **word) {
	state->last_status = last_status;
	return ret;
}

/**
 * Create a new process group. We need to do this in the parent and in the child
 * to protect aginst race conditions.
 */
static pid_t create_process_group(pid_t pid) {
	pid_t pgid = pid;
	if (setpgid(pid, pgid) != 0) {
		fprintf(stderr, "setpgid failed: %s\n", strerror(errno));
		return -1;
	}
	return pgid;
}

pid_t subshell_fork(struct context *ctx, struct process **process_ptr) {
	pid_t pid = fork();
	if (pid < 0) {
		fprintf(stderr, "fork failed: %s\n", strerror(errno));
		return false;
	} else if (pid == 0) {
		if (process_ptr != NULL) {
			*process_ptr = NULL;
		}

		if (ctx->state->options & MRSH_OPT_MONITOR) {
			// Create a job for all children processes
			pid_t pgid = create_process_group(getpid());
			if (pgid < 0) {
				exit(1);
			}
			ctx->job = job_create(ctx->state, pgid);
		}

		return 0;
	}

	struct process *proc = process_create(ctx->state, pid);
	if (process_ptr != NULL) {
		*process_ptr = proc;
	}

	if (ctx->state->options & MRSH_OPT_MONITOR) {
		pid_t pgid = create_process_group(pid);
		if (pgid < 0) {
			return false;
		}

		// Create a background job
		struct mrsh_job *job = job_create(ctx->state, pgid);
		job_add_process(job, proc);
	}

	return pid;
}

M shell/task/async.c => shell/task/async.c +1 -36
@@ 24,37 24,14 @@ static void task_async_destroy(struct task *task) {
	free(ta);
}

/**
 * Create a new process group. We need to do this in the parent and in the child
 * to protect aginst race conditions.
 */
static pid_t create_process_group(pid_t pid) {
	pid_t pgid = pid;
	if (setpgid(pid, pgid) != 0) {
		fprintf(stderr, "setpgid failed: %s\n", strerror(errno));
		return -1;
	}
	return pgid;
}

static bool task_async_start(struct task *task, struct context *ctx) {
	struct task_async *ta = (struct task_async *)task;

	// Start a subshell
	pid_t pid = fork();
	pid_t pid = subshell_fork(ctx, NULL);
	if (pid < 0) {
		fprintf(stderr, "fork failed: %s\n", strerror(errno));
		return false;
	} else if (pid == 0) {
		if (ctx->state->options & MRSH_OPT_MONITOR) {
			// Create a job for all children processes
			pid_t pgid = create_process_group(getpid());
			if (pgid < 0) {
				exit(1);
			}
			ctx->job = job_create(ctx->state, pgid);
		}

		if (!(ctx->state->options & MRSH_OPT_MONITOR)) {
			// If job control is disabled, stdin is /dev/null
			int fd = open("/dev/null", O_CLOEXEC | O_RDONLY);


@@ 75,18 52,6 @@ static bool task_async_start(struct task *task, struct context *ctx) {
		exit(ret);
	}

	if (ctx->state->options & MRSH_OPT_MONITOR) {
		pid_t pgid = create_process_group(pid);
		if (pgid < 0) {
			return false;
		}

		// Create a background job
		struct process *proc = process_create(ctx->state, pid);
		struct mrsh_job *job = job_create(ctx->state, pgid);
		job_add_process(job, proc);
	}

	return true;
}


M shell/task/subshell.c => shell/task/subshell.c +3 -6
@@ 1,3 1,4 @@
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>


@@ 19,16 20,13 @@ static void task_subshell_destroy(struct task *task) {
}

static bool task_subshell_start(struct task_subshell *ts, struct context *ctx) {
	pid_t pid = fork();
	pid_t pid = subshell_fork(ctx, &ts->process);
	if (pid < 0) {
		fprintf(stderr, "failed to fork(): %s\n", strerror(errno));
		return false;
	} else if (pid == 0) {
		// TODO: set process group ID

		errno = 0;
		int ret = task_run(ts->subtask, ctx);
		if (ret < 0) {
			assert(ret == TASK_STATUS_ERROR);
			fprintf(stderr, "failed to run task: %s\n", strerror(errno));
			exit(127);
		}


@@ 39,7 37,6 @@ static bool task_subshell_start(struct task_subshell *ts, struct context *ctx) {
		exit(ret);
	}

	ts->process = process_create(ctx->state, pid);
	return true;
}