~emersion/mrsh

3aff241f4940942981776bf52c92168d10dccec3 — emersion 3 years ago 1b0e096
Add a task abstraction
M builtin.c => builtin.c +21 -5
@@ 4,6 4,9 @@
#include <stdlib.h>
#include <string.h>

typedef int (*mrsh_builtin_func_t)(struct mrsh_state *state,
	int argc, char *argv[]);

static const char exit_usage[] = "usage: exit [n]\n";

static int builtin_exit(struct mrsh_state *state, int argc, char *argv[]) {


@@ 26,13 29,26 @@ static int builtin_exit(struct mrsh_state *state, int argc, char *argv[]) {
	return EXIT_SUCCESS;
}

int mrsh_builtin(struct mrsh_state *state, int argc, char *argv[]) {
mrsh_builtin_func_t get_builtin(const char *name) {
	if (strcmp(name, "exit") == 0) {
		return builtin_exit;
	} else {
		return NULL;
	}
}

int mrsh_has_builtin(const char *name) {
	return get_builtin(name) != NULL;
}

int mrsh_run_builtin(struct mrsh_state *state, int argc, char *argv[]) {
	assert(argc > 0);

	const char *cmd = argv[0];
	if (strcmp(cmd, "exit") == 0) {
		return builtin_exit(state, argc, argv);
	const char *name = argv[0];
	mrsh_builtin_func_t builtin = get_builtin(name);
	if (builtin == NULL) {
		return -1;
	}

	return -1;
	return builtin(state, argc, argv);
}

M include/mrsh/builtin.h => include/mrsh/builtin.h +4 -3
@@ 1,8 1,9 @@
#ifndef _MRSH_BUILTINS_H
#define _MRSH_BUILTINS_H
#ifndef _MRSH_BUILTIN_H
#define _MRSH_BUILTIN_H

#include <mrsh/shell.h>

int mrsh_builtin(struct mrsh_state *state, int argc, char *argv[]);
int mrsh_has_builtin(const char *name);
int mrsh_run_builtin(struct mrsh_state *state, int argc, char **argv);

#endif

M include/shell.h => include/shell.h +47 -4
@@ 1,10 1,53 @@
#include <mrsh/shell.h>

#define CONTEXT_PIDS_CAP 64

struct context {
	struct mrsh_state *state;
	int stdin_fileno;
	int stdout_fileno;
	bool nohang;
	pid_t pids[CONTEXT_PIDS_CAP]; // TODO: make this dynamic
};

struct process {
	pid_t pid;
	int stat;
};

struct task;

struct task_interface {
	int (*poll)(struct task *task, struct context *ctx);
	void (*destroy)(struct task *task);
};

#define TASK_STATUS_WAIT -1
#define TASK_STATUS_ERROR -2

struct task {
	const struct task_interface *impl;
	int status;
};

void process_init(struct process *process, pid_t pid);
void process_finish(struct process *process);
int process_poll(struct process *process);
void process_notify(pid_t pid, int stat);

void task_init(struct task *task, const struct task_interface *impl);
void task_destroy(struct task *task);
int task_poll(struct task *task, struct context *ctx);
int task_run(struct task *task, struct context *ctx);

struct task *task_builtin_create(struct mrsh_simple_command *sc);

struct task *task_process_create(struct mrsh_simple_command *sc);

struct task *task_list_create(void);
void task_list_add(struct task *task, struct task *child);

struct task *task_if_clause_create(struct task *condition, struct task *body,
	struct task *else_part);

struct task *task_pipeline_create(void);
void task_pipeline_add(struct task *task, struct task *child);

struct task *task_binop_create(enum mrsh_binop_type type,
	struct task *left, struct task *right);

M meson.build => meson.build +9 -1
@@ 25,7 25,15 @@ executable(
		'builtin.c',
		'main.c',
		'parser.c',
		'shell.c',
		'shell/process.c',
		'shell/shell.c',
		'shell/task_binop.c',
		'shell/task_builtin.c',
		'shell/task_if_clause.c',
		'shell/task_list.c',
		'shell/task_pipeline.c',
		'shell/task_process.c',
		'shell/task.c',
	]),
	include_directories: [mrsh_inc],
	install: true,

A shell/process.c => shell/process.c +44 -0
@@ 0,0 1,44 @@
#include <mrsh/array.h>
#include <string.h>
#include <sys/wait.h>
#include "shell.h"

static struct mrsh_array running_processes = {0};

void process_init(struct process *proc, pid_t pid) {
	mrsh_array_add(&running_processes, proc);
	proc->pid = pid;
	proc->stat = 0;
}

int process_poll(struct process *proc) {
	if (proc->stat == 0) {
		return -1;
	}
	return WEXITSTATUS(proc->stat);
}

static void process_remove(struct process *proc) {
	for (size_t i = 0; i < running_processes.len; ++i) {
		if (running_processes.data[i] == proc) {
			memmove(&running_processes.data[i], &running_processes.data[i + 1],
				running_processes.len - i - 1);
			break;
		}
	}
}

void process_finish(struct process *proc) {
	process_remove(proc);
}

void process_notify(pid_t pid, int stat) {
	for (size_t i = 0; i < running_processes.len; ++i) {
		struct process *proc = running_processes.data[i];
		if (proc->pid == pid) {
			proc->stat = stat;
			process_remove(proc);
			break;
		}
	}
}

A shell/shell.c => shell/shell.c +114 -0
@@ 0,0 1,114 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include "shell.h"

void mrsh_state_init(struct mrsh_state *state) {
	state->exit = -1;
}

static struct task *run_simple_command(struct mrsh_simple_command *sc) {
	struct task *task = task_builtin_create(sc);
	if (task != NULL) {
		return task;
	}

	return task_process_create(sc);
}

static struct task *run_node(struct mrsh_node *node);

static struct task *run_command_list_array(struct mrsh_array *array) {
	struct task *task_list = task_list_create();

	for (size_t i = 0; i < array->len; ++i) {
		struct mrsh_command_list *list = array->data[i];
		// TODO: handle list->ampersand
		task_list_add(task_list, run_node(list->node));
	}

	return task_list;
}

static struct task *run_command(struct mrsh_command *cmd);

static struct task *run_if_clause(struct mrsh_if_clause *ic) {
	struct task *condition = run_command_list_array(&ic->condition);
	struct task *body = run_command_list_array(&ic->body);
	struct task *else_part = NULL;
	if (ic->else_part != NULL) {
		else_part = run_command(ic->else_part);
	}
	return task_if_clause_create(condition, body, else_part);
}

static struct task *run_command(struct mrsh_command *cmd) {
	switch (cmd->type) {
	case MRSH_SIMPLE_COMMAND:;
		struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
		assert(sc != NULL);
		return run_simple_command(sc);
	case MRSH_BRACE_GROUP:;
		struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
		assert(bg != NULL);
		return run_command_list_array(&bg->body);
	case MRSH_IF_CLAUSE:;
		struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
		assert(ic != NULL);
		return run_if_clause(ic);
	}
	assert(false);
}

static struct task *run_pipeline(struct mrsh_pipeline *pl) {
	struct task *task_pipeline = task_pipeline_create();

	for (size_t i = 0; i < pl->commands.len; ++i) {
		struct mrsh_command *cmd = pl->commands.data[i];
		task_pipeline_add(task_pipeline, run_command(cmd));
	}

	return task_pipeline;
}

static struct task *run_binop(struct mrsh_binop *binop) {
	struct task *left = run_node(binop->left);
	struct task *right = run_node(binop->right);
	return task_binop_create(binop->type, left, right);
}

static struct task *run_node(struct mrsh_node *node) {
	switch (node->type) {
	case MRSH_NODE_PIPELINE:;
		struct mrsh_pipeline *pl = mrsh_node_get_pipeline(node);
		assert(pl != NULL);
		return run_pipeline(pl);
	case MRSH_NODE_BINOP:;
		struct mrsh_binop *binop = mrsh_node_get_binop(node);
		assert(binop != NULL);
		return run_binop(binop);
	}
	assert(false);
}

int mrsh_run_command_list(struct mrsh_state *state,
		struct mrsh_command_list *list) {
	struct task *task = run_node(list->node);

	struct context ctx = {
		.state = state,
		.stdin_fileno = -1,
		.stdout_fileno = -1,
	};
	return task_run(task, &ctx);
}

int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {
	struct task *task = run_command_list_array(&prog->body);

	struct context ctx = {
		.state = state,
		.stdin_fileno = -1,
		.stdout_fileno = -1,
	};
	return task_run(task, &ctx);
}

A shell/task.c => shell/task.c +46 -0
@@ 0,0 1,46 @@
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "shell.h"

void task_init(struct task *task, const struct task_interface *impl) {
	assert(impl->poll);
	task->impl = impl;
	task->status = TASK_STATUS_WAIT;
}

void task_destroy(struct task *task) {
	if (task->impl->destroy) {
		task->impl->destroy(task);
	} else {
		free(task);
	}
}

int task_poll(struct task *task, struct context *ctx) {
	if (task->status == TASK_STATUS_WAIT) {
		task->status = task->impl->poll(task, ctx);
	}
	return task->status;
}

int task_run(struct task *task, struct context *ctx) {
	while (true) {
		int ret = task_poll(task, ctx);
		if (ret != TASK_STATUS_WAIT) {
			return ret;
		}

		int stat;
		pid_t pid = waitpid(0, &stat, 0);
		if (pid == -1) {
			if (errno == EINTR) {
				continue;
			}
			return -1;
		}

		process_notify(pid, stat);
	}
}

A shell/task_binop.c => shell/task_binop.c +46 -0
@@ 0,0 1,46 @@
#include <stdlib.h>
#include "shell.h"

struct task_binop {
	struct task task;
	enum mrsh_binop_type type;
	struct task *left, *right;
};

static int task_binop_poll(struct task *task, struct context *ctx) {
	struct task_binop *tb = (struct task_binop *)task;

	int left_status = task_poll(tb->left, ctx);
	if (left_status < 0) {
		return left_status;
	}

	switch (tb->type) {
	case MRSH_BINOP_AND:
		if (left_status != 0) {
			return left_status;
		}
		break;
	case MRSH_BINOP_OR:
		if (left_status == 0) {
			return 0;
		}
		break;
	}

	return task_poll(tb->right, ctx);
}

static const struct task_interface task_binop_impl = {
	.poll = task_binop_poll,
};

struct task *task_binop_create(enum mrsh_binop_type type,
		struct task *left, struct task *right) {
	struct task_binop *tb = calloc(1, sizeof(struct task_binop));
	task_init(&tb->task, &task_binop_impl);
	tb->type = type;
	tb->left = left;
	tb->right = right;
	return &tb->task;
}

A shell/task_builtin.c => shell/task_builtin.c +42 -0
@@ 0,0 1,42 @@
#include <string.h>
#include <stdlib.h>
#include <mrsh/builtin.h>
#include "shell.h"

struct task_builtin {
	struct task task;
	struct mrsh_simple_command *sc;
};

static int task_builtin_poll(struct task *task, struct context *ctx) {
	struct task_builtin *tb = (struct task_builtin *)task;
	struct mrsh_simple_command *sc = tb->sc;

	int argc = 1 + sc->arguments.len;
	char *argv[argc + 1];
	argv[0] = sc->name;
	memcpy(argv + 1, sc->arguments.data, sc->arguments.len * sizeof(void *));
	argv[argc] = NULL;

	// TODO: redirections
	int ret = mrsh_run_builtin(ctx->state, argc, argv);
	if (ret < 0) {
		return TASK_STATUS_ERROR;
	}
	return ret;
}

static const struct task_interface task_builtin_impl = {
	.poll = task_builtin_poll,
};

struct task *task_builtin_create(struct mrsh_simple_command *sc) {
	if (!mrsh_has_builtin(sc->name)) {
		return NULL;
	}

	struct task_builtin *tb = calloc(1, sizeof(struct task_builtin));
	task_init(&tb->task, &task_builtin_impl);
	tb->sc = sc;
	return &tb->task;
}

A shell/task_if_clause.c => shell/task_if_clause.c +39 -0
@@ 0,0 1,39 @@
#include <stdlib.h>
#include "shell.h"

struct task_if_clause {
	struct task task;
	struct task *condition, *body, *else_part;
};

static int task_if_clause_poll(struct task *task, struct context *ctx) {
	struct task_if_clause *tic = (struct task_if_clause *)task;

	int condition_status = task_poll(tic->condition, ctx);
	if (condition_status < 0) {
		return condition_status;
	}

	if (condition_status == 0) {
		return task_poll(tic->body, ctx);
	} else {
		if (tic->else_part == NULL) {
			return 0;
		}
		return task_poll(tic->else_part, ctx);
	}
}

static const struct task_interface task_if_clause_impl = {
	.poll = task_if_clause_poll,
};

struct task *task_if_clause_create(struct task *condition, struct task *body,
		struct task *else_part) {
	struct task_if_clause *tic = calloc(1, sizeof(struct task_if_clause));
	task_init(&tic->task, &task_if_clause_impl);
	tic->condition = condition;
	tic->body = body;
	tic->else_part = else_part;
	return &tic->task;
}

A shell/task_list.c => shell/task_list.c +48 -0
@@ 0,0 1,48 @@
#include <stdlib.h>
#include "shell.h"

struct task_list {
	struct task task;
	struct mrsh_array children;
	size_t current;
};

static void task_list_destroy(struct task *task) {
	struct task_list *tl = (struct task_list *)task;
	mrsh_array_finish(&tl->children);
	free(tl);
}

static int task_list_poll(struct task *task, struct context *ctx) {
	struct task_list *tl = (struct task_list *)task;

	int ret = 0;
	while (tl->current < tl->children.len) {
		struct task *current = tl->children.data[tl->current];

		ret = task_poll(current, ctx);
		if (ret < 0) {
			return ret;
		}

		++tl->current;
	}

	return ret;
}

static const struct task_interface task_list_impl = {
	.destroy = task_list_destroy,
	.poll = task_list_poll,
};

struct task *task_list_create(void) {
	struct task_list *tl = calloc(1, sizeof(struct task_list));
	task_init(&tl->task, &task_list_impl);
	return &tl->task;
}

void task_list_add(struct task *task, struct task *child) {
	struct task_list *tl = (struct task_list *)task;
	mrsh_array_add(&tl->children, child);
}

A shell/task_pipeline.c => shell/task_pipeline.c +81 -0
@@ 0,0 1,81 @@
#include <unistd.h>
#include <stdlib.h>
#include "shell.h"

struct task_pipeline {
	struct task task;
	struct mrsh_array children;
	bool started;
};

static bool task_pipeline_start(struct task *task, struct context *parent_ctx) {
	struct task_pipeline *tp = (struct task_pipeline *)task;

	struct context ctx = { .state = parent_ctx->state };

	int last_stdout = -1;
	for (size_t i = 0; i < tp->children.len; ++i) {
		ctx.stdin_fileno = -1;
		ctx.stdout_fileno = -1;

		if (i > 0) {
			ctx.stdin_fileno = last_stdout;
		} else {
			ctx.stdin_fileno = parent_ctx->stdin_fileno;
		}

		if (i < tp->children.len - 1) {
			int fds[2];
			pipe(fds);
			ctx.stdout_fileno = fds[1];
			last_stdout = fds[0];
		} else {
			ctx.stdout_fileno = parent_ctx->stdout_fileno;
		}

		struct task *child = tp->children.data[i];
		int ret = task_poll(child, &ctx);
		if (ret == TASK_STATUS_ERROR) {
			return false;
		}
	}

	return true;
}

static int task_pipeline_poll(struct task *task, struct context *ctx) {
	struct task_pipeline *tp = (struct task_pipeline *)task;

	if (!tp->started) {
		if (!task_pipeline_start(task, ctx)) {
			return TASK_STATUS_ERROR;
		}
		tp->started = true;
	}

	int ret = 0;
	for (size_t i = 0; i < tp->children.len; ++i) {
		struct task *child = tp->children.data[i];
		ret = task_poll(child, ctx);
		if (ret < 0) {
			return ret;
		}
	}

	return ret;
}

static const struct task_interface task_pipeline_impl = {
	.poll = task_pipeline_poll,
};

struct task *task_pipeline_create(void) {
	struct task_pipeline *tp = calloc(1, sizeof(struct task_pipeline));
	task_init(&tp->task, &task_pipeline_impl);
	return &tp->task;
}

void task_pipeline_add(struct task *task, struct task *child) {
	struct task_pipeline *tp = (struct task_pipeline *)task;
	mrsh_array_add(&tp->children, child);
}

R shell.c => shell/task_process.c +43 -194
@@ 1,18 1,24 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <mrsh/builtin.h>
#include <mrsh/shell.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "shell.h"

void mrsh_state_init(struct mrsh_state *state) {
	state->exit = -1;
struct task_process {
	struct task task;
	struct mrsh_simple_command *sc;
	bool started;
	struct process process;
};

static void task_process_destroy(struct task *task) {
	struct task_process *tp = (struct task_process *)task;
	process_finish(&tp->process);
	free(tp);
}

static int parse_fd(const char *str) {


@@ 30,24 36,21 @@ static int parse_fd(const char *str) {
	return fd;
}

static int run_simple_command(struct mrsh_state *state,
		struct mrsh_simple_command *sc, struct context *ctx) {
	int argc = 1 + sc->arguments.len;
	char *argv[argc + 1];
	argv[0] = sc->name;
	memcpy(argv + 1, sc->arguments.data, sc->arguments.len * sizeof(void *));
	argv[argc] = NULL;

	// TODO: redirections for builtins
	int ret = mrsh_builtin(state, argc, argv);
	if (ret != -1) {
		return ret;
	}
static bool task_process_start(struct task *task, struct context *ctx) {
	struct task_process *tp = (struct task_process *)task;
	struct mrsh_simple_command *sc = tp->sc;

	pid_t pid = fork();
	if (pid < 0) {
		return -1;
		fprintf(stderr, "failed to fork()\n");
		return NULL;
	} else if (pid == 0) {
		int argc = 1 + sc->arguments.len;
		char *argv[argc + 1];
		argv[0] = sc->name;
		memcpy(argv + 1, sc->arguments.data, sc->arguments.len * sizeof(void *));
		argv[argc] = NULL;

		for (size_t i = 0; i < sc->assignments.len; ++i) {
			struct mrsh_assignment *assign = sc->assignments.data[i];
			setenv(assign->name, assign->value, true);


@@ 124,186 127,32 @@ static int run_simple_command(struct mrsh_state *state,
			close(ctx->stdout_fileno);
		}

		for (size_t i = 0; i < CONTEXT_PIDS_CAP; ++i) {
			if (ctx->pids[i] == 0) {
				ctx->pids[i] = pid;
				break;
			}
		}

		if (ctx->nohang) {
			return 0;
		}

		int status;
		if (waitpid(pid, &status, 0) != pid) {
			return -1;
		}
		return WEXITSTATUS(status);
		process_init(&tp->process, pid);
		return true;
	}
}

static int run_node(struct mrsh_state *state, struct mrsh_node *node,
	struct context *ctx);

static int run_command_list_array(struct mrsh_state *state,
		struct mrsh_array *array, struct context *ctx) {
	int exit_status = 0;
	for (size_t i = 0; i < array->len; ++i) {
		struct mrsh_command_list *list = array->data[i];
		// TODO: handle list->ampersand
		exit_status = run_node(state, list->node, ctx);
	}
	return exit_status;
}

static int run_command(struct mrsh_state *state, struct mrsh_command *cmd,
	struct context *ctx);

static int run_if_clause(struct mrsh_state *state, struct mrsh_if_clause *ic,
		struct context *ctx) {
	assert(!ctx->nohang); // TODO

	int condition_status = run_command_list_array(state, &ic->condition, ctx);
	if (condition_status < 0) {
		return condition_status;
	}
static int task_process_poll(struct task *task, struct context *ctx) {
	struct task_process *tp = (struct task_process *)task;

	if (condition_status == 0) {
		return run_command_list_array(state, &ic->body, ctx);
	} else {
		if (ic->else_part == NULL) {
			return 0;
	if (!tp->started) {
		if (!task_process_start(task, ctx)) {
			return TASK_STATUS_ERROR;
		}
		return run_command(state, ic->else_part, ctx);
		tp->started = true;
	}
}

static int run_command(struct mrsh_state *state, struct mrsh_command *cmd,
		struct context *ctx) {
	switch (cmd->type) {
	case MRSH_SIMPLE_COMMAND:;
		struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
		assert(sc != NULL);
		return run_simple_command(state, sc, ctx);
	case MRSH_BRACE_GROUP:;
		struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
		assert(bg != NULL);
		return run_command_list_array(state, &bg->body, ctx);
	case MRSH_IF_CLAUSE:;
		struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
		assert(ic != NULL);
		return run_if_clause(state, ic, ctx);
	}
	assert(false);
	return process_poll(&tp->process);
}

static int run_pipeline(struct mrsh_state *state, struct mrsh_pipeline *pl,
		struct context *parent_ctx) {
	assert(!parent_ctx->nohang); // TODO

	struct context ctx = { .nohang = true };

	int exit_status = 0;
	int last_stdout = -1;
	for (size_t i = 0; i < pl->commands.len; ++i) {
		ctx.stdin_fileno = -1;
		ctx.stdout_fileno = -1;

		if (i > 0) {
			ctx.stdin_fileno = last_stdout;
		} else {
			ctx.stdin_fileno = parent_ctx->stdin_fileno;
		}

		if (i < pl->commands.len - 1) {
			int fds[2];
			pipe(fds);
			ctx.stdout_fileno = fds[1];
			last_stdout = fds[0];
		} else {
			ctx.stdout_fileno = parent_ctx->stdout_fileno;
		}

		struct mrsh_command *cmd = pl->commands.data[i];
		int ret = run_command(state, cmd, &ctx);
		if (ret < 0) {
			return ret;
		}
	}

	for (size_t i = 0; i < CONTEXT_PIDS_CAP; ++i) {
		pid_t pid = ctx.pids[i];
		if (pid == 0) {
			break;
		}

		int status;
		if (waitpid(pid, &status, 0) != pid) {
			return -1;
		}
		exit_status = WEXITSTATUS(status);
	}

	if (pl->bang) {
		exit_status = !exit_status;
	}
	return exit_status;
}

static int run_binop(struct mrsh_state *state, struct mrsh_binop *binop,
		struct context *ctx) {
	assert(!ctx->nohang); // TODO

	int left_status = run_node(state, binop->left, ctx);
	if (left_status < 0) {
		return left_status;
	}

	switch (binop->type) {
	case MRSH_BINOP_AND:
		if (left_status != 0) {
			return left_status;
		}
		break;
	case MRSH_BINOP_OR:
		if (left_status == 0) {
			return 0;
		}
		break;
	}

	return run_node(state, binop->right, ctx);
}

static int run_node(struct mrsh_state *state, struct mrsh_node *node,
		struct context *ctx) {
	switch (node->type) {
	case MRSH_NODE_PIPELINE:;
		struct mrsh_pipeline *pl = mrsh_node_get_pipeline(node);
		assert(pl != NULL);
		return run_pipeline(state, pl, ctx);
	case MRSH_NODE_BINOP:;
		struct mrsh_binop *binop = mrsh_node_get_binop(node);
		assert(binop != NULL);
		return run_binop(state, binop, ctx);
	}
	assert(false);
}

int mrsh_run_command_list(struct mrsh_state *state,
		struct mrsh_command_list *list) {
	struct context ctx = {
		.stdin_fileno = -1,
		.stdout_fileno = -1,
	};
	return run_node(state, list->node, &ctx);
}
static const struct task_interface task_process_impl = {
	.destroy = task_process_destroy,
	.poll = task_process_poll,
};

int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {
	struct context ctx = {
		.stdin_fileno = -1,
		.stdout_fileno = -1,
	};
	return run_command_list_array(state, &prog->body, &ctx);
struct task *task_process_create(struct mrsh_simple_command *sc) {
	struct task_process *tp = calloc(1, sizeof(struct task_process));
	task_init(&tp->task, &task_process_impl);
	tp->sc = sc;
	return &tp->task;
}