job control

This commit is contained in:
K. Lange 2018-10-26 20:34:15 +09:00
parent a40fe23a50
commit 6c88956fa4
16 changed files with 237 additions and 35 deletions

View File

@ -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);

View File

@ -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");
}

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

@ -122,31 +122,27 @@ 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;
}
if (c == pty->tios.c_cc[VQUIT]) {
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, SIGQUIT, 1);
}
return;
sig = SIGINT;
} else if (c == pty->tios.c_cc[VQUIT]) {
sig = SIGQUIT;
} else if (c == pty->tios.c_cc[VSUSP]) {
sig = SIGTSTP;
}
/* VSUSP */
if (sig != -1) {
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, sig, 1);
}
return;
}
}
#if 0
if (pty->tios.c_lflag & IXON ) {

View File

@ -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) {

View File

@ -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];

View File

@ -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);

View File

@ -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
libc/unistd/setpgid.c Normal file
View File

@ -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
libc/unistd/setsid.c Normal file
View File

@ -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());
}

View File

@ -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,