diff --git a/apps/getty.c b/apps/getty.c index 65adbe1d..e591fe00 100644 --- a/apps/getty.c +++ b/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); diff --git a/apps/sh.c b/apps/sh.c index 2280de25..bd36b068 100644 --- a/apps/sh.c +++ b/apps/sh.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -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"); } diff --git a/apps/terminal-vga.c b/apps/terminal-vga.c index 762d6b12..8df30531 100644 --- a/apps/terminal-vga.c +++ b/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); diff --git a/apps/terminal.c b/apps/terminal.c index 583821af..0b00c523 100644 --- a/apps/terminal.c +++ b/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); diff --git a/base/usr/include/kernel/process.h b/base/usr/include/kernel/process.h index 5da4f4bc..90a22fd0 100644 --- a/base/usr/include/kernel/process.h +++ b/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 { diff --git a/base/usr/include/syscall.h b/base/usr/include/syscall.h index 9c53d9af..4db3ef1c 100644 --- a/base/usr/include/syscall.h +++ b/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 diff --git a/base/usr/include/syscall_nums.h b/base/usr/include/syscall_nums.h index f3379897..11d4d182 100644 --- a/base/usr/include/syscall_nums.h +++ b/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 diff --git a/base/usr/include/unistd.h b/base/usr/include/unistd.h index 0daff853..c1342ad9 100644 --- a/base/usr/include/unistd.h +++ b/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 diff --git a/kernel/fs/tty.c b/kernel/fs/tty.c index ead97d89..40802ff2 100644 --- a/kernel/fs/tty.c +++ b/kernel/fs/tty.c @@ -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 ) { diff --git a/kernel/sys/process.c b/kernel/sys/process.c index c6ac9f38..c58f7a37 100644 --- a/kernel/sys/process.c +++ b/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) { diff --git a/kernel/sys/signal.c b/kernel/sys/signal.c index 24d05899..a347393e 100644 --- a/kernel/sys/signal.c +++ b/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]; diff --git a/kernel/sys/syscall.c b/kernel/sys/syscall.c index 95017b03..e7b7a494 100644 --- a/kernel/sys/syscall.c +++ b/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); diff --git a/lib/rline_exp.c b/lib/rline_exp.c index 97e29c19..d76049b9 100644 --- a/lib/rline_exp.c +++ b/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); } diff --git a/libc/unistd/setpgid.c b/libc/unistd/setpgid.c new file mode 100644 index 00000000..62c73b1e --- /dev/null +++ b/libc/unistd/setpgid.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +DEFN_SYSCALL2(setpgid, SYS_SETPGID, int, int); + +int setpgid(pid_t pid, pid_t pgid) { + __sets_errno(syscall_setpgid((int)pid,(int)pgid)); +} + diff --git a/libc/unistd/setsid.c b/libc/unistd/setsid.c new file mode 100644 index 00000000..6f8fac39 --- /dev/null +++ b/libc/unistd/setsid.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +DEFN_SYSCALL0(setsid, SYS_SETSID); + +pid_t setsid(void) { + __sets_errno(syscall_setsid()); +} + diff --git a/modules/procfs.c b/modules/procfs.c index 7e070117..77a9503c 100644 --- a/modules/procfs.c +++ b/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,