ffa7dafff288418aba6e604efee36463bc3cd8ad — Simon Ser 2 months ago 6d5e003
builtin: add fg
M builtin/builtin.c => builtin/builtin.c +1 -0
@@ 22,6 22,7 @@ { "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_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_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 @@ '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 @@ }
  
  	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_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 @@ 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 @@ } 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 @@   	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 @@ }
  
  			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 @@   		destroy_finished_jobs(ctx->state);
  
- 		errno = 0;
  		int stat;
  		pid_t pid = waitpid(-1, &stat, WUNTRACED);
  		if (pid == -1) {