~emersion/mrsh

ffa7dafff288418aba6e604efee36463bc3cd8ad — Simon Ser 1 year, 1 month ago 6d5e003
builtin: add fg
M builtin/builtin.c => builtin/builtin.c +1 -0
@@ 22,6 22,7 @@ static const struct builtin builtins[] = {
	{ "exit", builtin_exit, true },
	{ "export", builtin_export, true },
	{ "false", builtin_false, false },
	{ "fg", builtin_fg, false },
	{ "getopts", builtin_getopts, false },
	{ "pwd", builtin_pwd, false },
	{ "read", builtin_read, false },

A builtin/fg.c => builtin/fg.c +44 -0
@@ 0,0 1,44 @@
#include <mrsh/getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include "builtin.h"
#include "shell/job.h"
#include "shell/shell.h"

// TODO: fg [job_id]
static const char fg_usage[] = "usage: fg\n";

int builtin_fg(struct mrsh_state *state, int argc, char *argv[]) {
	mrsh_optind = 0;
	int opt;
	while ((opt = mrsh_getopt(argc, argv, ":")) != -1) {
		switch (opt) {
		default:
			fprintf(stderr, "fg: unknown option -- %c\n", mrsh_optopt);
			fprintf(stderr, fg_usage);
			return EXIT_FAILURE;
		}
	}
	if (mrsh_optind < argc) {
		fprintf(stderr, fg_usage);
		return EXIT_FAILURE;
	}

	struct mrsh_job *stopped = NULL;
	for (ssize_t i = state->jobs.len - 1; i >= 0; --i) {
		struct mrsh_job *job = state->jobs.data[i];
		if (job_stopped(job)) {
			stopped = job;
			break;
		}
	}
	if (stopped == NULL) {
		fprintf(stderr, "fg: no current job");
		return EXIT_FAILURE;
	}

	if (!job_set_foreground(stopped, true, true)) {
		return EXIT_FAILURE;
	}
	return job_wait(stopped);
}

M include/builtin.h => include/builtin.h +1 -0
@@ 16,6 16,7 @@ int builtin_eval(struct mrsh_state *state, int argc, char *argv[]);
int builtin_exit(struct mrsh_state *state, int argc, char *argv[]);
int builtin_export(struct mrsh_state *state, int argc, char *argv[]);
int builtin_false(struct mrsh_state *state, int argc, char *argv[]);
int builtin_fg(struct mrsh_state *state, int argc, char *argv[]);
int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]);
int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]);
int builtin_read(struct mrsh_state *state, int argc, char *argv[]);

M include/shell/job.h => include/shell/job.h +6 -1
@@ 25,7 25,12 @@ void job_destroy(struct mrsh_job *job);
void job_add_process(struct mrsh_job *job, struct process *proc);
bool job_terminated(struct mrsh_job *job);
bool job_stopped(struct mrsh_job *job);
void job_set_foreground(struct mrsh_job *job, bool foreground);
int job_wait(struct mrsh_job *job);
/**
 * Put the job in the foreground or in the background. If the job is stopped and
 * cont is set to true, it will be continued.
 */
bool job_set_foreground(struct mrsh_job *job, bool foreground, bool cont);

bool init_job_child_process(struct mrsh_state *state);
void update_job(struct mrsh_state *state, pid_t pid, int stat);

M meson.build => meson.build +1 -0
@@ 81,6 81,7 @@ lib_mrsh = library(
		'builtin/exit.c',
		'builtin/export.c',
		'builtin/false.c',
		'builtin/fg.c',
		'builtin/getopts.c',
		'builtin/pwd.c',
		'builtin/read.c',

M shell/job.c => shell/job.c +45 -3
@@ 11,6 11,7 @@
#include <unistd.h>
#include "shell/job.h"
#include "shell/process.h"
#include "shell/task.h"

static const int ignored_signals[] = {
	SIGINT,


@@ 95,7 96,7 @@ void job_destroy(struct mrsh_job *job) {
	}

	if (job->state->foreground_job == job) {
		job_set_foreground(job, false);
		job_set_foreground(job, false, false);
	}

	struct mrsh_state *state = job->state;


@@ 128,21 129,30 @@ bool job_terminated(struct mrsh_job *job) {
}

bool job_stopped(struct mrsh_job *job) {
	bool stopped = false;
	for (size_t j = 0; j < job->processes.len; ++j) {
		struct process *proc = job->processes.data[j];
		if (!proc->terminated && !proc->stopped) {
			return false;
		}
		stopped |= proc->stopped;
	}
	return true;
	return stopped;
}

void job_set_foreground(struct mrsh_job *job, bool foreground) {
bool job_set_foreground(struct mrsh_job *job, bool foreground, bool cont) {
	struct mrsh_state *state = job->state;

	if (!job_stopped(job)) {
		cont = false;
	}

	if (foreground && state->foreground_job != job) {
		assert(state->foreground_job == NULL);
		tcsetpgrp(state->fd, job->pgid);
		if (cont) {
			tcsetattr(state->fd, TCSADRAIN, &job->term_modes);
		}
		state->foreground_job = job;
	}



@@ 154,6 164,38 @@ void job_set_foreground(struct mrsh_job *job, bool foreground) {
		tcsetattr(state->fd, TCSADRAIN, &state->term_modes);
		state->foreground_job = NULL;
	}

	if (cont) {
		if (kill(-job->pgid, SIGCONT) != 0) {
			fprintf(stderr, "kill failed: %s\n", strerror(errno));
			return false;
		}

		for (size_t j = 0; j < job->processes.len; ++j) {
			struct process *proc = job->processes.data[j];
			proc->stopped = false;
		}
	}

	return true;
}

int job_wait(struct mrsh_job *job) {
	while (!job_stopped(job) && !job_terminated(job)) {
		int stat;
		pid_t pid = waitpid(-1, &stat, WUNTRACED);
		if (pid == -1) {
			if (errno == EINTR) {
				continue;
			}
			fprintf(stderr, "failed to waitpid(): %s\n", strerror(errno));
			return TASK_STATUS_ERROR;
		}

		update_job(job->state, pid, stat);
	}

	return 0; // TODO: return the job's status
}

bool init_job_child_process(struct mrsh_state *state) {

M shell/task/command_process.c => shell/task/command_process.c +2 -2
@@ 43,7 43,7 @@ static bool task_process_start(struct task_command *tc, struct context *ctx) {
	} else if (pid == 0) {
		struct mrsh_job *job = put_into_process_group(ctx, getpid());
		if (ctx->state->interactive && !ctx->background) {
			job_set_foreground(job, true);
			job_set_foreground(job, true, false);
		}
		init_job_child_process(ctx->state);



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

	struct mrsh_job *job = put_into_process_group(ctx, pid);
	if (ctx->state->interactive && !ctx->background) {
		job_set_foreground(job, true);
		job_set_foreground(job, true, false);
	}

	tc->process = process_create(ctx->state, pid);

M shell/task/task.c => shell/task/task.c +1 -2
@@ 60,7 60,7 @@ int task_run(struct task *task, struct context *ctx) {
			}

			if (ctx->state->foreground_job != NULL) {
				job_set_foreground(ctx->state->foreground_job, false);
				job_set_foreground(ctx->state->foreground_job, false, false);
			}

			destroy_finished_jobs(ctx->state);


@@ 70,7 70,6 @@ int task_run(struct task *task, struct context *ctx) {

		destroy_finished_jobs(ctx->state);

		errno = 0;
		int stat;
		pid_t pid = waitpid(-1, &stat, WUNTRACED);
		if (pid == -1) {