job control
This commit is contained in:
parent
a40fe23a50
commit
6c88956fa4
@ -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);
|
||||
|
90
apps/sh.c
90
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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -106,6 +106,7 @@ typedef struct process {
|
||||
int awoken_index;
|
||||
node_t * timeout_node;
|
||||
struct timeval start;
|
||||
uint8_t suspended;
|
||||
} process_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -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
|
||||
|
||||
|
@ -50,3 +50,5 @@
|
||||
#define SYS_FSWAIT 59
|
||||
#define SYS_FSWAIT2 60
|
||||
#define SYS_CHOWN 61
|
||||
#define SYS_SETSID 62
|
||||
#define SYS_SETPGID 63
|
||||
|
@ -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
|
||||
|
@ -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 ) {
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
|
@ -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
11
libc/unistd/setpgid.c
Normal 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
11
libc/unistd/setsid.c
Normal 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());
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user