Browse Source

job control

K. Lange 3 years ago
parent
commit
6c88956fa4

+ 1 - 0
apps/getty.c

@@ -45,6 +45,7 @@ int main(int argc, char * argv[]) {
 	pid_t child = fork();
 
 	if (!child) {
+		setsid();
 		dup2(fd_slave, 0);
 		dup2(fd_slave, 1);
 		dup2(fd_slave, 2);

+ 88 - 2
apps/sh.c

@@ -35,6 +35,7 @@
 #include <sys/stat.h>
 
 #include <toaru/list.h>
+#include <toaru/hashmap.h>
 #include <toaru/kbd.h>
 #include <toaru/rline.h>
 #include <toaru/rline_exp.h>
@@ -308,6 +309,8 @@ void draw_prompt(void) {
 
 volatile int break_while = 0;
 uint32_t child = 0;
+pid_t suspended_pid = 0;
+hashmap_t * job_hash = NULL;
 
 void sig_pass(int sig) {
 	/* Interrupt handler */
@@ -1157,20 +1160,25 @@ _nope:
 		argv[tokenid-1] = NULL;
 	}
 
+	int pgid = 0;
 	if (cmdi > 0) {
 		int last_output[2];
 		pipe(last_output);
 		child_pid = fork();
 		if (!child_pid) {
+			setpgid(0,0);
 			dup2(last_output[1], STDOUT_FILENO);
 			close(last_output[0]);
 			run_cmd(arg_starts[0]);
 		}
 
+		pgid = child_pid;
+
 		for (int j = 1; j < cmdi; ++j) {
 			int tmp_out[2];
 			pipe(tmp_out);
 			if (!fork()) {
+				setpgid(0,pgid);
 				dup2(tmp_out[1], STDOUT_FILENO);
 				dup2(last_output[0], STDIN_FILENO);
 				close(tmp_out[0]);
@@ -1185,6 +1193,7 @@ _nope:
 
 		last_child = fork();
 		if (!last_child) {
+			setpgid(0,pgid);
 			if (output_files[cmdi]) {
 				int fd = open(output_files[cmdi], file_args[cmdi], 0666);
 				if (fd < 0) {
@@ -1209,6 +1218,7 @@ _nope:
 		} else {
 			child_pid = fork();
 			if (!child_pid) {
+				setpgid(0,0);
 				if (output_files[cmdi]) {
 					int fd = open(output_files[cmdi], file_args[cmdi], 0666);
 					if (fd < 0) {
@@ -1220,19 +1230,26 @@ _nope:
 				}
 				run_cmd(arg_starts[0]);
 			}
+			pgid = child_pid;
 			last_child = child_pid;
 		}
 	}
 
-	tcsetpgrp(STDIN_FILENO, child_pid);
+	tcsetpgrp(STDIN_FILENO, pgid);
 	int ret_code = 0;
 	if (!nowait) {
 		child = child_pid;
 		int pid;
 		int tmp;
 		do {
-			pid = waitpid(-1, &tmp, 0);
+			pid = waitpid(-pgid, &tmp, 0);
 			if (pid == last_child) ret_code = tmp;
+			if (WIFSTOPPED(tmp)) {
+				suspended_pid = pid;
+				hashmap_set(job_hash, (void*)pid, strdup(arg_starts[0][0]));
+				fprintf(stderr, "pid %d reports STOPPED (fg %d to resume)\n", pid, pid);
+				break;
+			}
 		} while (pid != -1 || (pid == -1 && errno != ECHILD));
 		child = 0;
 	}
@@ -1372,6 +1389,8 @@ int main(int argc, char ** argv) {
 	signal(SIGINT, sig_pass);
 	signal(SIGWINCH, sig_pass);
 
+	job_hash = hashmap_create_int(10);
+
 	getuser();
 	gethost();
 
@@ -1836,6 +1855,71 @@ uint32_t shell_cmd_read(int argc, char * argv[]) {
 	return 0;
 }
 
+uint32_t shell_cmd_fg(int argc, char * argv[]) {
+	int ret_code;
+	int pid;
+	if (argc < 2) {
+		if (!suspended_pid) {
+			list_t * keys = hashmap_keys(job_hash);
+			foreach(node, keys) {
+				suspended_pid = (int)node->value;
+				break;
+			}
+			list_free(keys);
+			free(keys);
+			if (!suspended_pid) {
+				fprintf(stderr, "no current job\n");
+				return 1;
+			}
+		}
+		pid = suspended_pid;
+	} else {
+		pid = atoi(argv[1]);
+	}
+
+	if (!hashmap_has(job_hash, (void*)pid)) {
+		fprintf(stderr, "invalid job");
+		return 0;
+	}
+
+	if (kill(pid, SIGCONT) < 0) {
+		fprintf(stderr, "no current job / bad pid\n");
+		hashmap_remove(job_hash, (void*)pid);
+		return 1;
+	}
+	tcsetpgrp(STDIN_FILENO, pid);
+	child = pid;
+
+	int outpid;
+	do {
+		outpid = waitpid(-pid, &ret_code, 0);
+		if (WIFSTOPPED(ret_code)) {
+			suspended_pid = pid;
+			fprintf(stderr, "pid %d reports STOPPED (fg %d to resume)\n", pid, pid);
+			break;
+		} else {
+			suspended_pid = 0;
+			hashmap_remove(job_hash, (void*)pid);
+		}
+	} while (outpid != -1 || (outpid == -1 && errno != ECHILD));
+	child = 0;
+	tcsetpgrp(STDIN_FILENO, getpid());
+	handle_status(ret_code);
+	return WEXITSTATUS(ret_code);
+}
+
+uint32_t shell_cmd_jobs(int argc, char * argv[]) {
+	list_t * keys = hashmap_keys(job_hash);
+	foreach(node, keys) {
+		int pid = (int)node->value;
+		char * c = hashmap_get(job_hash, (void*)pid);
+		fprintf(stdout, "%5d %s\n", pid, c);
+	}
+	list_free(keys);
+	free(keys);
+	return 0;
+}
+
 void install_commands() {
 	shell_commands = malloc(sizeof(char *) * SHELL_COMMANDS);
 	shell_pointers = malloc(sizeof(shell_command_t) * SHELL_COMMANDS);
@@ -1857,4 +1941,6 @@ void install_commands() {
 	shell_install_command("not",     shell_cmd_not, "invert status of command");
 	shell_install_command("unset",   shell_cmd_unset, "unset variable");
 	shell_install_command("read",    shell_cmd_read, "read user input");
+	shell_install_command("fg",      shell_cmd_fg, "resume a suspended job");
+	shell_install_command("jobs",    shell_cmd_jobs, "list stopped jobs");
 }

+ 1 - 0
apps/terminal-vga.c

@@ -1204,6 +1204,7 @@ int main(int argc, char ** argv) {
 	uint32_t f = fork();
 
 	if (getpid() != pid) {
+		setsid();
 		dup2(fd_slave, 0);
 		dup2(fd_slave, 1);
 		dup2(fd_slave, 2);

+ 1 - 0
apps/terminal.c

@@ -2483,6 +2483,7 @@ int main(int argc, char ** argv) {
 	child_pid = fork();
 
 	if (!child_pid) {
+		setsid();
 		/* Prepare stdin/out/err */
 		dup2(fd_slave, 0);
 		dup2(fd_slave, 1);

+ 1 - 0
base/usr/include/kernel/process.h

@@ -106,6 +106,7 @@ typedef struct process {
 	int           awoken_index;
 	node_t *      timeout_node;
 	struct timeval start;
+	uint8_t       suspended;
 } process_t;
 
 typedef struct {

+ 2 - 0
base/usr/include/syscall.h

@@ -117,6 +117,8 @@ DECL_SYSCALL3(waitpid, int, int *, int);
 DECL_SYSCALL5(mount, char *, char *, char *, unsigned long, void *);
 DECL_SYSCALL1(pipe,  int *);
 DECL_SYSCALL3(readlink, char *, char *, int);
+DECL_SYSCALL0(setsid);
+DECL_SYSCALL2(setpgid,int,int);
 
 _End_C_Header
 

+ 2 - 0
base/usr/include/syscall_nums.h

@@ -50,3 +50,5 @@
 #define SYS_FSWAIT 59
 #define SYS_FSWAIT2 60
 #define SYS_CHOWN 61
+#define SYS_SETSID 62
+#define SYS_SETPGID 63

+ 3 - 0
base/usr/include/unistd.h

@@ -83,4 +83,7 @@ extern char * getlogin(void);
 extern int gethostname(char * name, size_t len);
 extern int sethostname(const char * name, size_t len);
 
+extern pid_t setsid(void);
+extern int setpgid(pid_t, pid_t);
+
 _End_C_Header

+ 9 - 13
kernel/fs/tty.c

@@ -122,19 +122,16 @@ void tty_input_process(pty_t * pty, uint8_t c) {
 		return;
 	}
 	if (pty->tios.c_lflag & ISIG) {
+		int sig = -1;
 		if (c == pty->tios.c_cc[VINTR]) {
-			if (pty->tios.c_lflag & ECHO) {
-				output_process(pty, '^');
-				output_process(pty, ('@' + c) % 128);
-				output_process(pty, '\n');
-			}
-			clear_input_buffer(pty);
-			if (pty->fg_proc) {
-				send_signal(pty->fg_proc, SIGINT, 1);
-			}
-			return;
+			sig = SIGINT;
+		} else if (c == pty->tios.c_cc[VQUIT]) {
+			sig = SIGQUIT;
+		} else if (c == pty->tios.c_cc[VSUSP]) {
+			sig = SIGTSTP;
 		}
-		if (c == pty->tios.c_cc[VQUIT]) {
+		/* VSUSP */
+		if (sig != -1) {
 			if (pty->tios.c_lflag & ECHO) {
 				output_process(pty, '^');
 				output_process(pty, ('@' + c) % 128);
@@ -142,11 +139,10 @@ void tty_input_process(pty_t * pty, uint8_t c) {
 			}
 			clear_input_buffer(pty);
 			if (pty->fg_proc) {
-				send_signal(pty->fg_proc, SIGQUIT, 1);
+				send_signal(pty->fg_proc, sig, 1);
 			}
 			return;
 		}
-		/* VSUSP */
 	}
 #if 0
 	if (pty->tios.c_lflag & IXON ) {

+ 14 - 8
kernel/sys/process.c

@@ -263,12 +263,13 @@ process_t * spawn_init(void) {
 	 * of the process' entry in the process tree. */
 	init->tree_entry = process_tree->root;
 	init->id      = 1;       /* Init is PID 1 */
-	init->group   = 0;
+	init->group   = 0; /* thread group id (real PID) */
+	init->job     = 1; /* process group id (jobs) */
+	init->session = 1; /* session leader id */
 	init->name    = strdup("init");  /* Um, duh. */
 	init->cmdline = NULL;
 	init->user    = 0;       /* UID 0 */
 	init->mask    = 022;     /* umask */
-	init->group   = 0;       /* Task group 0 */
 	init->status  = 0;       /* Run status */
 	init->fds = malloc(sizeof(fd_table_t));
 	init->fds->refs = 1;
@@ -293,6 +294,7 @@ process_t * spawn_init(void) {
 
 	/* Process is not finished */
 	init->finished = 0;
+	init->suspended = 0;
 	init->started = 1;
 	init->running = 1;
 	init->wait_queue = list_create();
@@ -385,8 +387,9 @@ process_t * spawn_process(volatile process_t * parent, int reuse_fds) {
 	proc->user  = parent->user;
 	proc->mask = parent->mask;
 
-	/* XXX this is wrong? */
-	proc->group = parent->group;
+	/* Until specified otherwise */
+	proc->job = parent->job;
+	proc->session = parent->session;
 
 	/* Zero out the ESP/EBP/EIP */
 	proc->thread.esp = 0;
@@ -436,6 +439,7 @@ process_t * spawn_process(volatile process_t * parent, int reuse_fds) {
 	/* Zero out the process status */
 	proc->status = 0;
 	proc->finished = 0;
+	proc->suspended = 0;
 	proc->started = 0;
 	proc->running = 0;
 	memset(proc->signals.functions, 0x00, sizeof(uintptr_t) * NUMSIGNALS);
@@ -783,10 +787,10 @@ static int wait_candidate(process_t * parent, int pid, int options, process_t *
 	}
 
 	if (pid < -1) {
-		if (proc->group == -pid || proc->id == -pid) return 1;
+		if (proc->job == -pid || proc->id == -pid) return 1;
 	} else if (pid == 0) {
 		/* Matches our group ID */
-		if (proc->group == parent->id) return 1;
+		if (proc->job == parent->id) return 1;
 	} else if (pid > 0) {
 		/* Specific pid */
 		if (proc->id == pid) return 1;
@@ -817,7 +821,7 @@ int waitpid(int pid, int * status, int options) {
 
 			if (wait_candidate(proc, pid, options, child)) {
 				has_children = 1;
-				if (child->finished) {
+				if (child->finished || child->suspended) {
 					candidate = child;
 					break;
 				}
@@ -836,7 +840,9 @@ int waitpid(int pid, int * status, int options) {
 				*status = candidate->status;
 			}
 			int pid = candidate->id;
-			reap_process(candidate);
+			if (candidate->finished) {
+				reap_process(candidate);
+			}
 			return pid;
 		} else {
 			if (options & 1) {

+ 26 - 1
kernel/sys/signal.c

@@ -69,7 +69,7 @@ char isdeadly[] = {
 	0, /* SIGPOLL    */
 	3, /* SIGSTOP    */
 	3, /* SIGTSTP    */
-	0, /* SIGCONT    */
+	4, /* SIGCONT    */
 	3, /* SIGTTIN    */
 	3, /* SIGTTOUT   */
 	1, /* SIGVTALRM  */
@@ -103,6 +103,21 @@ void handle_signal(process_t * proc, signal_t * sig) {
 			debug_print(WARNING, "Process %d killed by unhandled signal (%d)", proc->id, signum);
 			kexit(((128 + signum) << 8) | signum);
 			__builtin_unreachable();
+		} else if (dowhat == 3) {
+			debug_print(WARNING, "suspending pid %d", proc->id);
+			current_process->suspended = 1;
+			current_process->status = 0x7F;
+
+			process_t * parent = process_get_parent((process_t *)current_process);
+
+			if (parent && !parent->finished) {
+				wakeup_queue(parent->wait_queue);
+			}
+
+			switch_task(0);
+		} else if (dowhat == 4) {
+			switch_task(1);
+			return;
 		} else {
 			debug_print(WARNING, "Ignoring signal %d by default in pid %d", signum, proc->id);
 		}
@@ -215,6 +230,16 @@ int send_signal(pid_t process, uint32_t signal, int force_root) {
 		return 0;
 	}
 
+	if (isdeadly[signal] == 4) {
+		if (!receiver->suspended) {
+			return -EINVAL;
+		} else {
+			debug_print(WARNING, "Resuming pid %d from suspend", receiver->id);
+			receiver->suspended = 0;
+			receiver->status = 0;
+		}
+	}
+
 	/* Append signal to list */
 	signal_t * sig = malloc(sizeof(signal_t));
 	sig->handler = (uintptr_t)receiver->signals.functions[signal];

+ 53 - 0
kernel/sys/syscall.c

@@ -908,6 +908,57 @@ static int sys_fswait_timeout(int c, int fds[], int timeout) {
 	return result;
 }
 
+static int sys_setsid(void) {
+	if (current_process->job == current_process->group) {
+		return -EPERM;
+	}
+	current_process->session = current_process->group;
+	return current_process->group;
+}
+
+static int sys_setpgid(pid_t pid, pid_t pgid) {
+	if (pgid < 0) {
+		return -EINVAL;
+	}
+	process_t * proc;
+	if (pid == 0) {
+		proc = (process_t*)current_process;
+	} else {
+		proc = process_from_pid(pid);
+	}
+	if (!proc) {
+		debug_print(WARNING, "not found");
+		return -ESRCH;
+	}
+	if (proc->session != current_process->session) {
+		debug_print(WARNING, "child is in different sesion");
+		return -EPERM;
+	}
+	if (proc->session == proc->group) {
+		debug_print(WARNING, "process is session leader");
+		return -EPERM;
+	}
+
+	process_t * pgroup = process_from_pid(pgid);
+
+	if (!pgroup) {
+		debug_print(WARNING, "bad session id");
+		return -EPERM;
+	}
+
+	if (pgroup->session != proc->session) {
+		debug_print(WARNING, "tried to move to different session");
+		return -EPERM;
+	}
+
+	if (pgid == 0) {
+		proc->job = proc->group;
+	} else {
+		proc->job = pgid;
+	}
+	return 0;
+}
+
 /*
  * System Call Internals
  */
@@ -963,6 +1014,8 @@ static int (*syscalls[])() = {
 	[SYS_FSWAIT]       = sys_fswait,
 	[SYS_FSWAIT2]      = sys_fswait_timeout,
 	[SYS_CHOWN]        = sys_chown,
+	[SYS_SETSID]       = sys_setsid,
+	[SYS_SETPGID]      = sys_setpgid,
 };
 
 uint32_t num_syscalls = sizeof(syscalls) / sizeof(*syscalls);

+ 1 - 2
lib/rline_exp.c

@@ -1309,8 +1309,7 @@ static void get_initial_termios(void) {
 
 static void set_unbuffered(void) {
 	struct termios new = old;
-	new.c_lflag &= (~ICANON & ~ECHO);
-	new.c_cc[VINTR] = 0;
+	new.c_lflag &= (~ICANON & ~ECHO & ~ISIG);
 	tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new);
 }
 

+ 11 - 0
libc/unistd/setpgid.c

@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include <syscall.h>
+#include <syscall_nums.h>
+#include <errno.h>
+
+DEFN_SYSCALL2(setpgid, SYS_SETPGID, int, int);
+
+int setpgid(pid_t pid, pid_t pgid) {
+	__sets_errno(syscall_setpgid((int)pid,(int)pgid));
+}
+

+ 11 - 0
libc/unistd/setsid.c

@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include <syscall.h>
+#include <syscall_nums.h>
+#include <errno.h>
+
+DEFN_SYSCALL0(setsid, SYS_SETSID);
+
+pid_t setsid(void) {
+	__sets_errno(syscall_setsid());
+}
+

+ 4 - 0
modules/procfs.c

@@ -161,6 +161,8 @@ static uint32_t proc_status_func(fs_node_t *node, uint32_t offset, uint32_t size
 			"Tgid:\t%d\n" /* group ? group : pid */
 			"Pid:\t%d\n" /* pid */
 			"PPid:\t%d\n" /* parent pid */
+			"Pgid:\t%d\n" /* progress group id */
+			"Sid:\t%d\n" /* session id */
 			"Uid:\t%d\n"
 			"Ueip:\t0x%x\n"
 			"SCid:\t%d\n"
@@ -180,6 +182,8 @@ static uint32_t proc_status_func(fs_node_t *node, uint32_t offset, uint32_t size
 			proc->group ? proc->group : proc->id,
 			proc->id,
 			parent ? parent->id : 0,
+			proc->job,
+			proc->session,
 			proc->user,
 			proc->syscall_registers ? proc->syscall_registers->eip : 0,
 			proc->syscall_registers ? proc->syscall_registers->eax : 0,