~emersion/mrsh

b0cbe5e8b40e0cd61b734140d14cd8eb6806737f — Simon Ser 2 years ago 7b73bb8
Maintain a list of running jobs
M include/mrsh/shell.h => include/mrsh/shell.h +1 -0
@@ 94,6 94,7 @@ struct mrsh_state {
	bool job_control;
	pid_t pgid;
	struct termios term_modes;
	struct mrsh_array jobs;
};

void mrsh_function_destroy(struct mrsh_function *fn);

M include/shell/job.h => include/shell/job.h +19 -0
@@ 1,10 1,29 @@
#ifndef SHELL_JOB_H
#define SHELL_JOB_H

#include <mrsh/array.h>
#include <stdbool.h>
#include <sys/types.h>

struct mrsh_state;
struct process;

/**
 * A set of processes, comprising a shell pipeline, and any processes descended
 * from it, that are all in the same process group.
 */
struct job {
	pid_t pgid;
	struct mrsh_state *state;
	bool finished;
	struct mrsh_array processes; // struct process *
};

struct job *job_create(struct mrsh_state *state, pid_t pgid);
void job_destroy(struct job *job);
void job_add_process(struct job *job, struct process *proc);

bool job_init_process(struct mrsh_state *state);
void job_notify(struct mrsh_state *state, pid_t pid, int stat);

#endif

M include/shell/shell.h => include/shell/shell.h +5 -3
@@ 3,15 3,17 @@

#include <mrsh/shell.h>

struct job;

/**
 * A context holds state information and per-job information. A context is
 * guaranteed to be shared between all members of a job.
 */
struct context {
	struct mrsh_state *state;

	// Per-job information
	pid_t pgid;
	// When executing a pipeline, this is set to the job created for the
	// pipeline
	struct job *job;
};

#endif

M shell/job.c => shell/job.c +54 -0
@@ 10,6 10,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include "shell/job.h"
#include "shell/process.h"

static const int ignored_signals[] = {
	SIGINT,


@@ 90,3 91,56 @@ bool job_init_process(struct mrsh_state *state) {

	return true;
}

static void array_remove(struct mrsh_array *array, size_t i) {
	memmove(&array->data[i], &array->data[i + 1],
		(array->len - i - 1) * sizeof(void *));
	--array->len;
}

struct job *job_create(struct mrsh_state *state, pid_t pgid) {
	struct job *job = calloc(1, sizeof(struct job));
	job->state = state;
	job->pgid = pgid;
	mrsh_array_add(&state->jobs, job);
	return job;
}

void job_destroy(struct job *job) {
	if (job == NULL) {
		return;
	}

	struct mrsh_state *state = job->state;
	for (size_t i = 0; i < state->jobs.len; ++i) {
		if (state->jobs.data[i] == job) {
			array_remove(&state->jobs, i);
			break;
		}
	}
	mrsh_array_finish(&job->processes);
	free(job);
}

void job_add_process(struct job *job, struct process *proc) {
	mrsh_array_add(&job->processes, proc);
}

void job_notify(struct mrsh_state *state, pid_t pid, int stat) {
	process_notify(pid, stat);

	for (size_t i = 0; i < state->jobs.len; ++i) {
		struct job *job = state->jobs.data[i];
		for (ssize_t j = 0; j < (ssize_t)job->processes.len; ++j) {
			struct process *proc = job->processes.data[j];
			if (proc->finished) {
				array_remove(&job->processes, j);
				j -= 1;
			}
		}

		if (job->processes.len == 0) {
			job->finished = true;
		}
	}
}

M shell/process.c => shell/process.c +16 -4
@@ 1,3 1,4 @@
#include <assert.h>
#include <mrsh/array.h>
#include <stdbool.h>
#include <string.h>


@@ 42,13 43,24 @@ void process_finish(struct process *proc) {
}

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

	if (WIFEXITED(stat) || WIFSIGNALED(stat)) {
		proc->finished = true;
		proc->stat = stat;
		process_remove(proc);
	} else {
		assert(false);
	}
}

M shell/shell.c => shell/shell.c +5 -0
@@ 5,6 5,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "shell/job.h"
#include "shell/shell.h"
#include "shell/task.h"



@@ 72,6 73,10 @@ void mrsh_state_finish(struct mrsh_state *state) {
	mrsh_hashtable_for_each(&state->aliases,
		state_string_finish_iterator, NULL);
	mrsh_hashtable_finish(&state->aliases);
	for (size_t i = 0; i < state->jobs.len; ++i) {
		job_destroy(state->jobs.data[i]);
	}
	mrsh_array_finish(&state->jobs);
	struct mrsh_call_frame *args = state->args;
	while (args) {
		struct mrsh_call_frame *prev = args->prev;

M shell/task/command_process.c => shell/task/command_process.c +7 -5
@@ 19,11 19,12 @@ static void populate_env_iterator(const char *key, void *_var, void *_) {
 * 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 void put_into_process_group(struct context *ctx, pid_t pid) {
	if (ctx->pgid == 0) {
		ctx->pgid = pid;
static struct job *put_into_process_group(struct context *ctx, pid_t pid) {
	if (ctx->job == NULL) {
		ctx->job = job_create(ctx->state, pid);
	}
	setpgid(pid, ctx->pgid);
	setpgid(pid, ctx->job->pgid);
	return ctx->job;
}

static bool task_process_start(struct task_command *tc, struct context *ctx) {


@@ 90,9 91,10 @@ static bool task_process_start(struct task_command *tc, struct context *ctx) {
		exit(127);
	}

	put_into_process_group(ctx, pid);
	struct job *job = put_into_process_group(ctx, pid);

	process_init(&tc->process, pid);
	job_add_process(job, &tc->process);
	return true;
}


M shell/task/pipeline.c => shell/task/pipeline.c +6 -1
@@ 3,6 3,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "shell/job.h"
#include "shell/task.h"

struct task_pipeline {


@@ 96,7 97,7 @@ static int task_pipeline_poll(struct task *task, struct context *ctx) {
	if (!tp->started) {
		// All child processes should be put into the same process group
		tp->child_ctx = *ctx;
		tp->child_ctx.pgid = 0;
		tp->child_ctx.job = NULL;

		if (!task_pipeline_start(task, &tp->child_ctx)) {
			return TASK_STATUS_ERROR;


@@ 113,6 114,10 @@ static int task_pipeline_poll(struct task *task, struct context *ctx) {
		}
	}

	if (ret != TASK_STATUS_WAIT) {
		job_destroy(tp->child_ctx.job);
	}

	return ret;
}


M shell/task/task.c => shell/task/task.c +2 -2
@@ 4,7 4,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include "shell/process.h"
#include "shell/job.h"
#include "shell/task.h"

void task_init(struct task *task, const struct task_interface *impl) {


@@ 55,6 55,6 @@ int task_run(struct task *task, struct context *ctx) {
			return -1;
		}

		process_notify(pid, stat);
		job_notify(ctx->state, pid, stat);
	}
}