From b423d7b4775e7ce63719fa9595d69d6e9e299e90 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Wed, 30 Sep 2009 09:57:22 +0000 Subject: [PATCH] Merge of David's ptrace branch. Summary: o Support for ptrace T_ATTACH/T_DETACH and T_SYSCALL o PM signal handling logic should now work properly, even with debuggers being present o Asynchronous PM/VFS protocol, full IPC support for senda(), and AMF_NOREPLY senda() flag DETAILS Process stop and delay call handling of PM: o Added sys_runctl() kernel call with sys_stop() and sys_resume() aliases, for PM to stop and resume a process o Added exception for sending/syscall-traced processes to sys_runctl(), and matching SIGKREADY pseudo-signal to PM o Fixed PM signal logic to deal with requests from a process after stopping it (so-called "delay calls"), using the SIGKREADY facility o Fixed various PM panics due to race conditions with delay calls versus VFS calls o Removed special PRIO_STOP priority value o Added SYS_LOCK RTS kernel flag, to stop an individual process from running while modifying its process structure Signal and debugger handling in PM: o Fixed debugger signals being dropped if a second signal arrives when the debugger has not retrieved the first one o Fixed debugger signals being sent to the debugger more than once o Fixed debugger signals unpausing process in VFS; removed PM_UNPAUSE_TR protocol message o Detached debugger signals from general signal logic and from being blocked on VFS calls, meaning that even VFS can now be traced o Fixed debugger being unable to receive more than one pending signal in one process stop o Fixed signal delivery being delayed needlessly when multiple signals are pending o Fixed wait test for tracer, which was returning for children that were not waited for o Removed second parallel pending call from PM to VFS for any process o Fixed process becoming runnable between exec() and debugger trap o Added support for notifying the debugger before the parent when a debugged child exits o Fixed debugger death causing child to remain stopped forever o Fixed consistently incorrect use of _NSIG Extensions to ptrace(): o Added T_ATTACH and T_DETACH ptrace request, to attach and detach a debugger to and from a process o Added T_SYSCALL ptrace request, to trace system calls o Added T_SETOPT ptrace request, to set trace options o Added TO_TRACEFORK trace option, to attach automatically to children of a traced process o Added TO_ALTEXEC trace option, to send SIGSTOP instead of SIGTRAP upon a successful exec() of the tracee o Extended T_GETUSER ptrace support to allow retrieving a process's priv structure o Removed T_STOP ptrace request again, as it does not help implementing debuggers properly o Added MINIX3-specific ptrace test (test42) o Added proper manual page for ptrace(2) Asynchronous PM/VFS interface: o Fixed asynchronous messages not being checked when receive() is called with an endpoint other than ANY o Added AMF_NOREPLY senda() flag, preventing such messages from satisfying the receive part of a sendrec() o Added asynsend3() that takes optional flags; asynsend() is now a #define passing in 0 as third parameter o Made PM/VFS protocol asynchronous; reintroduced tell_fs() o Made PM_BASE request/reply number range unique o Hacked in a horrible temporary workaround into RS to deal with newly revealed RS-PM-VFS race condition triangle until VFS is asynchronous System signal handling: o Fixed shutdown logic of device drivers; removed old SIGKSTOP signal o Removed is-superuser check from PM's do_procstat() (aka getsigset()) o Added sigset macros to allow system processes to deal with the full signal set, rather than just the POSIX subset Miscellaneous PM fixes: o Split do_getset into do_get and do_set, merging common code and making structure clearer o Fixed setpriority() being able to put to sleep processes using an invalid parameter, or revive zombie processes o Made find_proc() global; removed obsolete proc_from_pid() o Cleanup here and there Also included: o Fixed false-positive boot order kernel warning o Removed last traces of old NOTIFY_FROM code THINGS OF POSSIBLE INTEREST o It should now be possible to run PM at any priority, even lower than user processes o No assumptions are made about communication speed between PM and VFS, although communication must be FIFO o A debugger will now receive incoming debuggee signals at kill time only; the process may not yet be fully stopped o A first step has been made towards making the SYSTEM task preemptible --- drivers/audio/framework/audio_fw.c | 34 +- drivers/dp8390/dp8390.c | 15 +- drivers/dpeth/dp.c | 10 +- drivers/floppy/floppy.c | 7 +- drivers/fxp/fxp.c | 15 +- drivers/lance/lance.c | 15 +- drivers/libdriver/driver.c | 11 +- drivers/libdriver/driver.h | 4 +- drivers/libdriver_asyn/driver.c | 13 +- drivers/libdriver_asyn/driver.h | 4 +- drivers/log/diag.c | 6 +- drivers/log/log.c | 29 +- drivers/log/log.h | 2 +- drivers/orinoco/orinoco.c | 16 +- drivers/printer/printer.c | 16 +- drivers/rtl8139/rtl8139.c | 14 +- include/minix/com.h | 114 +-- include/minix/ipc.h | 1 + include/minix/syslib.h | 5 + include/minix/sysutil.h | 3 +- include/minix/vm.h | 2 +- include/signal.h | 24 +- include/sys/ptrace.h | 9 + include/sys/resource.h | 3 - kernel/arch/i386/exception.c | 2 + kernel/arch/i386/mpx386.s | 6 +- kernel/arch/i386/system.c | 20 + kernel/clock.c | 20 +- kernel/config.h | 1 + kernel/debug.c | 2 +- kernel/main.c | 4 +- kernel/proc.c | 178 +++- kernel/proc.h | 19 +- kernel/proto.h | 2 + kernel/system.c | 18 + kernel/system.h | 5 + kernel/system/Makefile | 4 + kernel/system/do_fork.c | 3 +- kernel/system/do_nice.c | 39 +- kernel/system/do_runctl.c | 68 ++ kernel/system/do_sigsend.c | 5 +- kernel/system/do_trace.c | 32 +- lib/ansi/signal.c | 2 +- lib/posix/_sigset.c | 11 +- lib/syslib/Makefile.in | 1 + lib/syslib/sys_runctl.c | 14 + lib/syslib/vm_fork.c | 2 +- lib/sysutil/asynsend.c | 6 +- man/man2/ptrace.2 | 377 ++++---- servers/inet/mnx_eth.c | 55 -- servers/is/dmp_kernel.c | 2 +- servers/is/dmp_pm.c | 21 +- servers/pm/const.h | 2 + servers/pm/exec.c | 39 +- servers/pm/forkexit.c | 334 +++++-- servers/pm/getset.c | 142 ++- servers/pm/glo.h | 3 - servers/pm/main.c | 390 ++------ servers/pm/misc.c | 45 +- servers/pm/mproc.h | 50 +- servers/pm/proto.h | 51 +- servers/pm/signal.c | 441 +++++---- servers/pm/table.c | 18 +- servers/pm/trace.c | 232 +++-- servers/pm/utility.c | 42 +- servers/rs/manager.c | 13 + servers/vfs/main.c | 173 ++-- test/Makefile | 4 +- test/run | 4 +- test/test42.c | 1349 ++++++++++++++++++++++++++++ 70 files changed, 3177 insertions(+), 1446 deletions(-) create mode 100644 kernel/system/do_runctl.c create mode 100644 lib/syslib/sys_runctl.c create mode 100644 test/test42.c diff --git a/drivers/audio/framework/audio_fw.c b/drivers/audio/framework/audio_fw.c index cebc5f9b3..70c7c1bf8 100755 --- a/drivers/audio/framework/audio_fw.c +++ b/drivers/audio/framework/audio_fw.c @@ -44,6 +44,7 @@ #include "audio_fw.h" #include +#include #include #include @@ -92,14 +93,29 @@ PUBLIC void main(void) caller = mess.m_source; proc_nr = mess.IO_ENDPT; - if (caller == RS_PROC_NR && mess.m_type == DEV_PING) - { - /* Got ping from RS. Just notify RS */ - notify(RS_PROC_NR); + /* Now carry out the work. First check for notifications. */ + if (is_notify(mess.m_type)) { + switch (_ENDPOINT_P(mess.m_source)) { + case HARDWARE: + msg_hardware(); + break; + case PM_PROC_NR: + msg_sig_stop(); + break; + case RS_PROC_NR: + /* Got ping from RS. Just notify RS */ + notify(RS_PROC_NR); + break; + default: + dprint("%s: %d uncaught notify!\n", + drv.DriverName, mess.m_type); + } + + /* get next message */ continue; } - /* Now carry out the work. */ + /* Normal messages. */ switch(mess.m_type) { case DEV_OPEN: /* open the special file ( = parameter) */ @@ -149,10 +165,6 @@ PUBLIC void main(void) repl_mess.REP_STATUS = r; send(caller, &repl_mess); continue; - case HARD_INT: - msg_hardware();continue; /* don't reply */ - case SYS_SIG: - msg_sig_stop(); continue; /* don't reply */ default: dprint("%s: %d uncaught msg!\n", drv.DriverName, mess.m_type); @@ -883,14 +895,14 @@ PRIVATE int init_buffers(sub_dev_t *sub_dev_ptr) { #if (CHIP == INTEL) char *base; - size_t size, off; + size_t size; unsigned left; u32_t i; phys_bytes ph; /* allocate dma buffer space */ size= sub_dev_ptr->DmaSize + 64 * 1024; - off = base= alloc_contig(size, AC_ALIGN4K, &ph); + base= alloc_contig(size, AC_ALIGN4K, &ph); if (!base) { error("%s: failed to allocate dma buffer for channel %d\n", drv.DriverName,i); diff --git a/drivers/dp8390/dp8390.c b/drivers/dp8390/dp8390.c index 5caa73282..cce2c0ab8 100644 --- a/drivers/dp8390/dp8390.c +++ b/drivers/dp8390/dp8390.c @@ -281,17 +281,20 @@ int main(int argc, char *argv[]) case HARDWARE: r = handle_hw_intr(); break; - case SYSTEM: - if (sigismember((sigset_t*) - &m.NOTIFY_ARG, - SIGKSTOP)) + case PM_PROC_NR: + { + sigset_t set; + + if (getsigset(&set) != 0) break; + + if (sigismember(&set, SIGTERM)) dp8390_stop(); + break; + } case CLOCK: printf("dp8390: notify from CLOCK\n"); break; - case PM_PROC_NR: - break; default: panic("", "dp8390: illegal notify from", m.m_source); diff --git a/drivers/dpeth/dp.c b/drivers/dpeth/dp.c index efde4943f..afbb73861 100644 --- a/drivers/dpeth/dp.c +++ b/drivers/dpeth/dp.c @@ -544,10 +544,12 @@ static void do_watchdog(void *message) PRIVATE void handle_system_signal(message *m) { - sigset_t sigset = m->NOTIFY_ARG; + sigset_t set; int port; - if (sigismember(&sigset, SIGKSTOP)) { /* Shut down */ + if (getsigset(&set) != 0) return; + + if (sigismember(&set, SIGTERM)) { /* Shut down */ for (port = 0; port < DE_PORT_NR; port += 1) { if (de_table[port].de_mode == DEM_ENABLED) { m->m_type = DL_STOP; @@ -624,9 +626,6 @@ PUBLIC int main(int argc, char **argv) /* Status request from RS */ notify(m.m_source); break; - case SYSTEM: - handle_system_signal(&m); - break; case HARDWARE: /* Interrupt from device */ handle_hw_intr(); @@ -636,6 +635,7 @@ PUBLIC int main(int argc, char **argv) do_dump(&m); break; case PM_PROC_NR: + handle_system_signal(&m); break; default: /* Invalid message type */ diff --git a/drivers/floppy/floppy.c b/drivers/floppy/floppy.c index 1354cfd4c..e9cd36198 100644 --- a/drivers/floppy/floppy.c +++ b/drivers/floppy/floppy.c @@ -263,7 +263,7 @@ FORWARD _PROTOTYPE( void f_reset, (void) ); FORWARD _PROTOTYPE( int f_intr_wait, (void) ); FORWARD _PROTOTYPE( int read_id, (void) ); FORWARD _PROTOTYPE( int f_do_open, (struct driver *dp, message *m_ptr) ); -FORWARD _PROTOTYPE( void floppy_stop, (struct driver *dp, message *m_ptr)); +FORWARD _PROTOTYPE( void floppy_stop, (struct driver *dp, sigset_t *set)); FORWARD _PROTOTYPE( int test_read, (int density) ); FORWARD _PROTOTYPE( void f_geometry, (struct partition *entry) ); @@ -805,12 +805,11 @@ timer_t *tp; /*===========================================================================* * floppy_stop * *===========================================================================*/ -PRIVATE void floppy_stop(struct driver *dp, message *m_ptr) +PRIVATE void floppy_stop(struct driver *dp, sigset_t *set) { /* Stop all activity and cleanly exit with the system. */ int s; - sigset_t sigset = m_ptr->NOTIFY_ARG; - if (sigismember(&sigset, SIGTERM) || sigismember(&sigset, SIGKSTOP)) { + if (sigismember(set, SIGTERM)) { if ((s=sys_outb(DOR, ENABLE_INT)) != OK) panic("FLOPPY","Sys_outb in floppy_stop() failed", s); exit(0); diff --git a/drivers/fxp/fxp.c b/drivers/fxp/fxp.c index f733aef5c..78ff9fbbc 100644 --- a/drivers/fxp/fxp.c +++ b/drivers/fxp/fxp.c @@ -353,12 +353,17 @@ int main(int argc, char *argv[]) case HARDWARE: handle_hw_intr(); break; - case SYSTEM: - if (sigismember((sigset_t *)&m.NOTIFY_ARG, SIGKSTOP)) - fxp_stop(); - break; case PM_PROC_NR: + { + sigset_t set; + + if (getsigset(&set) != 0) break; + + if (sigismember(&set, SIGTERM)) + fxp_stop(); + break; + } case CLOCK: fxp_expire_timers(); break; @@ -2639,7 +2644,7 @@ static void fxp_stop() printf("%s: resetting device\n", fp->fxp_name); fxp_outl(port, CSR_PORT, CP_CMD_SOFT_RESET); } - sys_exit(0); + exit(0); } /*===========================================================================* diff --git a/drivers/lance/lance.c b/drivers/lance/lance.c index 3c8adea6f..ec94bbf5b 100644 --- a/drivers/lance/lance.c +++ b/drivers/lance/lance.c @@ -328,10 +328,6 @@ void main( int argc, char **argv ) case TTY_PROC_NR: lance_dump(); break; - case SYSTEM: - if (sigismember((sigset_t*)&m.NOTIFY_ARG, SIGKSTOP)) - lance_stop(); - break; case HARDWARE: for (i=0;idr_signal)(dp, &set); + break; case SYSTEM: - (*dp->dr_signal)(dp, &mess); + set = mess.NOTIFY_ARG; + (*dp->dr_signal)(dp, &set); break; case CLOCK: (*dp->dr_alarm)(dp, &mess); @@ -349,9 +354,9 @@ int safe; /*============================================================================* * nop_signal * *============================================================================*/ -PUBLIC void nop_signal(dp, mp) +PUBLIC void nop_signal(dp, set) struct driver *dp; -message *mp; +sigset_t *set; { /* Default action for signal is to ignore. */ } diff --git a/drivers/libdriver/driver.h b/drivers/libdriver/driver.h index 655f00cd6..7ee9fa4a8 100644 --- a/drivers/libdriver/driver.h +++ b/drivers/libdriver/driver.h @@ -37,7 +37,7 @@ struct driver { iovec_t *iov, unsigned nr_req, int safe) ); _PROTOTYPE( void (*dr_cleanup), (void) ); _PROTOTYPE( void (*dr_geometry), (struct partition *entry) ); - _PROTOTYPE( void (*dr_signal), (struct driver *dp, message *m_ptr) ); + _PROTOTYPE( void (*dr_signal), (struct driver *dp, sigset_t *set) ); _PROTOTYPE( void (*dr_alarm), (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int (*dr_cancel), (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int (*dr_select), (struct driver *dp, message *m_ptr) ); @@ -60,7 +60,7 @@ _PROTOTYPE( int do_nop, (struct driver *dp, message *m_ptr) ); _PROTOTYPE( struct device *nop_prepare, (int device) ); _PROTOTYPE( void nop_cleanup, (void) ); _PROTOTYPE( void nop_task, (void) ); -_PROTOTYPE( void nop_signal, (struct driver *dp, message *m_ptr) ); +_PROTOTYPE( void nop_signal, (struct driver *dp, sigset_t *set) ); _PROTOTYPE( void nop_alarm, (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int nop_cancel, (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int nop_select, (struct driver *dp, message *m_ptr) ); diff --git a/drivers/libdriver_asyn/driver.c b/drivers/libdriver_asyn/driver.c index 8adf5f318..ad11c55fb 100644 --- a/drivers/libdriver_asyn/driver.c +++ b/drivers/libdriver_asyn/driver.c @@ -53,8 +53,6 @@ FORWARD _PROTOTYPE( void init_buffer, (void) ); FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp, int safe) ); FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp, int safe) ); -_PROTOTYPE( int asynsend, (endpoint_t dst, message *mp)); - int device_caller; PRIVATE mq_t *queue_head = NULL; @@ -68,6 +66,7 @@ struct driver *dp; /* Device dependent entry points. */ int r, proc_nr; message mess, reply_mess; + sigset_t set; /* Init MQ library. */ mq_init(); @@ -117,8 +116,12 @@ struct driver *dp; /* Device dependent entry points. */ } break; case PM_PROC_NR: + if (getsigset(&set) != 0) break; + (*dp->dr_signal)(dp, &set); + break; case SYSTEM: - (*dp->dr_signal)(dp, &mess); + set = mess.NOTIFY_ARG; + (*dp->dr_signal)(dp, &set); break; case CLOCK: (*dp->dr_alarm)(dp, &mess); @@ -409,9 +412,9 @@ int safe; /*============================================================================* * nop_signal * *============================================================================*/ -PUBLIC void nop_signal(dp, mp) +PUBLIC void nop_signal(dp, set) struct driver *dp; -message *mp; +sigset_t *set; { /* Default action for signal is to ignore. */ } diff --git a/drivers/libdriver_asyn/driver.h b/drivers/libdriver_asyn/driver.h index 0737f1748..7e9423180 100644 --- a/drivers/libdriver_asyn/driver.h +++ b/drivers/libdriver_asyn/driver.h @@ -37,7 +37,7 @@ struct driver { iovec_t *iov, unsigned nr_req, int safe) ); _PROTOTYPE( void (*dr_cleanup), (void) ); _PROTOTYPE( void (*dr_geometry), (struct partition *entry) ); - _PROTOTYPE( void (*dr_signal), (struct driver *dp, message *m_ptr) ); + _PROTOTYPE( void (*dr_signal), (struct driver *dp, sigset_t *set) ); _PROTOTYPE( void (*dr_alarm), (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int (*dr_cancel), (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int (*dr_select), (struct driver *dp, message *m_ptr) ); @@ -60,7 +60,7 @@ _PROTOTYPE( int do_nop, (struct driver *dp, message *m_ptr) ); _PROTOTYPE( struct device *nop_prepare, (int device) ); _PROTOTYPE( void nop_cleanup, (void) ); _PROTOTYPE( void nop_task, (void) ); -_PROTOTYPE( void nop_signal, (struct driver *dp, message *m_ptr) ); +_PROTOTYPE( void nop_signal, (struct driver *dp, sigset_t *set) ); _PROTOTYPE( void nop_alarm, (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int nop_cancel, (struct driver *dp, message *m_ptr) ); _PROTOTYPE( int nop_select, (struct driver *dp, message *m_ptr) ); diff --git a/drivers/log/diag.c b/drivers/log/diag.c index 658950c04..5994d0e1d 100644 --- a/drivers/log/diag.c +++ b/drivers/log/diag.c @@ -18,8 +18,8 @@ /*==========================================================================* * do_new_kmess * *==========================================================================*/ -PUBLIC int do_new_kmess(m) -message *m; /* notification message */ +PUBLIC int do_new_kmess(from) +endpoint_t from; /* who sent this message? */ { /* Notification for a new kernel message. */ static struct kmessages kmess; /* entire kmess structure */ @@ -31,7 +31,7 @@ message *m; /* notification message */ static int kernel_prev_next = 0; static int tty_prev_next = 0; - if (m->m_source == TTY_PROC_NR) + if (from == TTY_PROC_NR) { cp_grant_id_t gid; message mess; diff --git a/drivers/log/log.c b/drivers/log/log.c index c23b94843..13ef2d63a 100644 --- a/drivers/log/log.c +++ b/drivers/log/log.c @@ -9,6 +9,7 @@ #include "log.h" #include #include +#include #define LOG_DEBUG 0 /* enable/ disable debugging */ @@ -28,7 +29,7 @@ FORWARD _PROTOTYPE( int log_transfer, (int proc_nr, int opcode, u64_t position, FORWARD _PROTOTYPE( int log_do_open, (struct driver *dp, message *m_ptr) ); FORWARD _PROTOTYPE( int log_cancel, (struct driver *dp, message *m_ptr) ); FORWARD _PROTOTYPE( int log_select, (struct driver *dp, message *m_ptr) ); -FORWARD _PROTOTYPE( void log_signal, (struct driver *dp, message *m_ptr) ); +FORWARD _PROTOTYPE( void log_signal, (struct driver *dp, sigset_t *set) ); FORWARD _PROTOTYPE( int log_other, (struct driver *dp, message *m_ptr, int) ); FORWARD _PROTOTYPE( void log_geometry, (struct partition *entry) ); FORWARD _PROTOTYPE( int subread, (struct logdevice *log, int count, int proc_nr, vir_bytes user_vir, size_t, int safe) ); @@ -368,13 +369,12 @@ message *m_ptr; /*============================================================================* * log_signal * *============================================================================*/ -PRIVATE void log_signal(dp, m_ptr) +PRIVATE void log_signal(dp, set) struct driver *dp; -message *m_ptr; +sigset_t *set; { - sigset_t sigset = m_ptr->NOTIFY_ARG; - if (sigismember(&sigset, SIGKMESS)) { - do_new_kmess(m_ptr); + if (sigismember(set, SIGKMESS)) { + do_new_kmess(SYSTEM); } } @@ -392,6 +392,19 @@ int safe; /* This function gets messages that the generic driver doesn't * understand. */ + if (is_notify(m_ptr->m_type)) { + switch (_ENDPOINT_P(m_ptr->m_source)) { + case TTY_PROC_NR: + do_new_kmess(m_ptr->m_source); + r = EDONTREPLY; + break; + default: + r = EINVAL; + break; + } + return r; + } + switch(m_ptr->m_type) { case DIAGNOSTICS_OLD: { r = do_diagnostics(m_ptr, 0); @@ -406,10 +419,6 @@ int safe; r = EDONTREPLY; break; } - case NOTIFY_FROM(TTY_PROC_NR): - do_new_kmess(m_ptr); - r = EDONTREPLY; - break; default: r = EINVAL; break; diff --git a/drivers/log/log.h b/drivers/log/log.h index 7b5001eee..e064cf7e1 100644 --- a/drivers/log/log.h +++ b/drivers/log/log.h @@ -31,7 +31,7 @@ struct logdevice { }; /* Function prototypes. */ -_PROTOTYPE( int do_new_kmess, (message *m) ); +_PROTOTYPE( int do_new_kmess, (endpoint_t from) ); _PROTOTYPE( int do_diagnostics, (message *m, int safe) ); _PROTOTYPE( void log_append, (char *buf, int len) ); diff --git a/drivers/orinoco/orinoco.c b/drivers/orinoco/orinoco.c index 282287552..8d8268a11 100755 --- a/drivers/orinoco/orinoco.c +++ b/drivers/orinoco/orinoco.c @@ -280,11 +280,6 @@ int main(int argc, char *argv[]) { case CLOCK: or_watchdog_f(NULL); break; - case SYSTEM: - if (sigismember((sigset_t*)&m.NOTIFY_ARG, - SIGKSTOP)) - orinoco_stop(); - break; case HARDWARE: do_hard_int(); if (int_event_check) @@ -294,7 +289,16 @@ int main(int argc, char *argv[]) { or_dump(&m); break; case PM_PROC_NR: + { + sigset_t set; + + if (getsigset(&set) != 0) break; + + if (sigismember(&set, SIGTERM)) + orinoco_stop(); + break; + } default: panic(__FILE__, "orinoco: illegal notify from:", @@ -424,7 +428,7 @@ static void orinoco_stop () { continue; /* TODO: send a signal to the card to shut it down */ } - sys_exit(0); + exit(0); } /***************************************************************************** diff --git a/drivers/printer/printer.c b/drivers/printer/printer.c index 4dc9815f4..00ec2e469 100644 --- a/drivers/printer/printer.c +++ b/drivers/printer/printer.c @@ -90,7 +90,7 @@ PRIVATE int revive_pending; /* set to true if revive is pending */ PRIVATE int revive_status; /* revive status */ PRIVATE int done_status; /* status of last output completion */ PRIVATE int oleft; /* bytes of output left in obuf */ -PRIVATE char obuf[128]; /* output buffer */ +PRIVATE unsigned char obuf[128]; /* output buffer */ PRIVATE unsigned char *optr; /* ptr to next char in obuf to print */ PRIVATE int orig_count; /* original byte count */ PRIVATE int port_base; /* I/O port for printer */ @@ -113,7 +113,7 @@ FORWARD _PROTOTYPE( void prepare_output, (void) ); FORWARD _PROTOTYPE( void do_initialize, (void) ); FORWARD _PROTOTYPE( void reply, (int code,int replyee,int proc,int status)); FORWARD _PROTOTYPE( void do_printer_output, (void) ); -FORWARD _PROTOTYPE( void do_signal, (message *m_ptr) ); +FORWARD _PROTOTYPE( void do_signal, (void) ); /*===========================================================================* @@ -140,13 +140,11 @@ PUBLIC void main(void) case HARDWARE: do_printer_output(); break; - case SYSTEM: - do_signal(&pr_mess); - break; case RS_PROC_NR: notify(pr_mess.m_source); break; case PM_PROC_NR: + do_signal(); break; default: reply(TASK_REPLY, pr_mess.m_source, @@ -175,11 +173,11 @@ PUBLIC void main(void) /*===========================================================================* * do_signal * *===========================================================================*/ -PRIVATE void do_signal(m_ptr) -message *m_ptr; /* signal message */ +PRIVATE void do_signal() { - int sig; - sigset_t sigset = m_ptr->NOTIFY_ARG; + sigset_t sigset; + + if (getsigset(&sigset) != 0) return; /* Expect a SIGTERM signal when this server must shutdown. */ if (sigismember(&sigset, SIGTERM)) { diff --git a/drivers/rtl8139/rtl8139.c b/drivers/rtl8139/rtl8139.c index 15e974e07..e736972ea 100755 --- a/drivers/rtl8139/rtl8139.c +++ b/drivers/rtl8139/rtl8139.c @@ -369,11 +369,6 @@ int main(int argc, char *argv[]) */ rl_watchdog_f(NULL); break; - case SYSTEM: - if (sigismember((sigset_t*)&m.NOTIFY_ARG, - SIGKSTOP)) - rtl8139_stop(); - break; case HARDWARE: do_hard_int(); if (int_event_check) @@ -383,7 +378,16 @@ int main(int argc, char *argv[]) rtl8139_dump(&m); break; case PM_PROC_NR: + { + sigset_t set; + + if (getsigset(&set) != 0) break; + + if (sigismember(&set, SIGTERM)) + rtl8139_stop(); + break; + } default: panic("rtl8139","illegal notify from", m.m_source); diff --git a/include/minix/com.h b/include/minix/com.h index b4d24d8f2..f507d2f99 100755 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -64,20 +64,10 @@ /* FIXME will be is_notify(a) ((a) == NOTIFY_MESSAGE) */ #define is_notify(a) ((a) & NOTIFY_MESSAGE) #define NOTIFY_FROM(p_nr) (NOTIFY_MESSAGE | ((p_nr) + NR_TASKS)) -# define PROC_EVENT NOTIFY_FROM(PM_PROC_NR) /* process status change */ -# define SYN_ALARM NOTIFY_FROM(CLOCK) /* synchronous alarm */ -# define SYS_SIG NOTIFY_FROM(SYSTEM) /* system signal */ -# define HARD_INT NOTIFY_FROM(HARDWARE) /* hardware interrupt */ -# define FKEY_PRESSED NOTIFY_FROM(TTY_PROC_NR)/* function key press */ -# define DEV_PING NOTIFY_FROM(RS_PROC_NR) /* driver liveness ping */ -# define DS_UPDATE NOTIFY_FROM(DS_PROC_NR) /* subscription update */ /* Shorthands for message parameters passed with notifications. */ -#define NOTIFY_SOURCE m_source -#define NOTIFY_TYPE m_type #define NOTIFY_ARG m2_l1 #define NOTIFY_TIMESTAMP m2_l2 -#define NOTIFY_FLAGS m2_i1 /*===========================================================================* * Messages for BUS controller drivers * @@ -330,8 +320,9 @@ # define SYS_SYSCTL (KERNEL_CALL + 44) /* sys_sysctl() */ # define SYS_VTIMER (KERNEL_CALL + 45) /* sys_vtimer() */ +# define SYS_RUNCTL (KERNEL_CALL + 46) /* sys_runctl() */ -#define NR_SYS_CALLS 46 /* number of system calls */ +#define NR_SYS_CALLS 47 /* number of system calls */ /* Pseudo call for use in kernel/table.c. */ #define SYS_ALL_CALLS (NR_SYS_CALLS) @@ -621,6 +612,12 @@ #define VT_VALUE m2_l1 /* new/previous value of the timer */ #define VT_ENDPT m2_l2 /* process to set/retrieve the timer for */ +/* Field names for SYS_RUNCTL. */ +#define RC_ENDPT m1_i1 /* which process to stop or resume */ +#define RC_ACTION m1_i2 /* set or clear stop flag */ +# define RC_STOP 0 /* stop the process, unless delaying */ +# define RC_RESUME 1 /* clear the stop flag */ + /*===========================================================================* * Messages for the Reincarnation Server * *===========================================================================*/ @@ -696,52 +693,57 @@ #define DIAG_REPL_OLD (DIAG_BASE+0x80+0) /* reply to DIAGNOSTICS(_S) */ -#define PM_BASE 0x900 -#define PM_GET_WORK (PM_BASE + 1) /* Get work from PM */ -#define PM_IDLE (PM_BASE + 2) /* PM doesn't have any more work */ -#define PM_BUSY (PM_BASE + 3) /* A reply from FS is needed */ -#define PM_SETSID (PM_BASE + 5) /* Tell FS about the session leader */ -#define PM_SETSID_PROC m1_i1 /* process */ -#define PM_SETGID (PM_BASE + 6) /* Tell FS about the new group IDs */ -#define PM_SETGID_PROC m1_i1 /* process */ -#define PM_SETGID_EGID m1_i2 /* effective group id */ -#define PM_SETGID_RGID m1_i3 /* real group id */ -#define PM_SETUID (PM_BASE + 7) /* Tell FS about the new user IDs */ -#define PM_SETUID_PROC m1_i1 /* process */ -#define PM_SETUID_EGID m1_i2 /* effective user id */ -#define PM_SETUID_RGID m1_i3 /* real user id */ -#define PM_FORK (PM_BASE + 8) /* Tell FS about the new process */ -#define PM_FORK_PPROC m1_i1 /* parent process */ -#define PM_FORK_CPROC m1_i2 /* child process */ -#define PM_FORK_CPID m1_i3 /* child pid */ -#define PM_EXIT (PM_BASE + 9) /* Tell FS about the exiting process */ -#define PM_EXIT_PROC m1_i1 /* process */ -#define PM_UNPAUSE (PM_BASE + 10) /* interrupted process */ -#define PM_UNPAUSE_PROC m1_i1 /* process */ -#define PM_REBOOT (PM_BASE + 11) /* Tell FS that we about to reboot */ -#define PM_EXEC (PM_BASE + 12) /* Forward exec call to FS */ -#define PM_EXEC_PROC m1_i1 /* process */ -#define PM_EXEC_PATH m1_p1 /* executable */ -#define PM_EXEC_PATH_LEN m1_i2 /* length of path including - * terminating nul - */ -#define PM_EXEC_FRAME m1_p2 /* arguments and environment */ -#define PM_EXEC_FRAME_LEN m1_i3 /* size of frame */ -#define PM_FORK_NB (PM_BASE + 13) /* Tell FS about the fork_nb call */ -#define PM_DUMPCORE (PM_BASE + 14) /* Ask FS to generate a core dump */ -#define PM_CORE_PROC m1_i1 -#define PM_CORE_SEGPTR m1_p1 -#define PM_UNPAUSE_TR (PM_BASE + 15) /* interrupted process (for tracing) */ +/*===========================================================================* + * Messages used between PM and VFS * + *===========================================================================*/ -/* Replies */ -#define PM_EXIT_REPLY (PM_BASE + 20) /* Reply from FS */ -#define PM_REBOOT_REPLY (PM_BASE + 21) /* Reply from FS */ -#define PM_EXEC_REPLY (PM_BASE + 22) /* Reply from FS */ - /* PM_EXEC_PROC m1_i1 */ -#define PM_EXEC_STATUS m1_i2 /* OK or failure */ -#define PM_CORE_REPLY (PM_BASE + 23) /* Reply from FS */ - /* PM_CORE_PROC m1_i1 */ -#define PM_CORE_STATUS m1_i2 /* OK or failure */ +#define PM_BASE 0xE00 + +/* Requests from PM to VFS */ +#define PM_SETUID (PM_BASE + 1) /* Tell FS about the new user IDs */ +#define PM_SETGID (PM_BASE + 2) /* Tell FS about the new group IDs */ +#define PM_SETSID (PM_BASE + 3) /* Tell FS about the session leader */ +#define PM_EXIT (PM_BASE + 4) /* Tell FS about the exiting process */ +#define PM_DUMPCORE (PM_BASE + 5) /* Ask FS to generate a core dump */ +#define PM_EXEC (PM_BASE + 6) /* Forward exec call to FS */ +#define PM_FORK (PM_BASE + 7) /* Tell FS about the new process */ +#define PM_FORK_NB (PM_BASE + 8) /* Tell FS about the fork_nb call */ +#define PM_UNPAUSE (PM_BASE + 9) /* interrupted process */ +#define PM_REBOOT (PM_BASE + 10) /* Tell FS that we about to reboot */ + +/* Replies from VFS to PM */ +#define PM_SETUID_REPLY (PM_BASE + 21) +#define PM_SETGID_REPLY (PM_BASE + 22) +#define PM_SETSID_REPLY (PM_BASE + 23) +#define PM_EXIT_REPLY (PM_BASE + 24) +#define PM_CORE_REPLY (PM_BASE + 25) +#define PM_EXEC_REPLY (PM_BASE + 26) +#define PM_FORK_REPLY (PM_BASE + 27) +#define PM_FORK_NB_REPLY (PM_BASE + 28) +#define PM_UNPAUSE_REPLY (PM_BASE + 29) +#define PM_REBOOT_REPLY (PM_BASE + 30) + +/* Standard parameters for all requests and replies, except PM_REBOOT */ +# define PM_PROC m1_i1 /* process */ + +/* Additional parameters for PM_SETUID and PM_SETGID */ +# define PM_EID m1_i2 /* effective user/group id */ +# define PM_RID m1_i3 /* real user/group id */ + +/* Additional parameters for PM_EXEC */ +# define PM_PATH m1_p1 /* executable */ +# define PM_PATH_LEN m1_i2 /* length of path including + * terminating nul + */ +# define PM_FRAME m1_p2 /* arguments and environment */ +# define PM_FRAME_LEN m1_i3 /* size of frame */ + +/* Additional parameters for PM_EXEC_REPLY and PM_CORE_REPLY */ +# define PM_STATUS m1_i2 /* OK or failure */ + +/* Additional parameters for PM_FORK and PM_FORK_NB */ +# define PM_PPROC m1_i2 /* parent process */ +# define PM_CPID m1_i3 /* child pid */ /* Parameters for the EXEC_NEWMEM call */ #define EXC_NM_PROC m1_i1 /* process that needs new map */ diff --git a/include/minix/ipc.h b/include/minix/ipc.h index 37711da2d..811dad192 100644 --- a/include/minix/ipc.h +++ b/include/minix/ipc.h @@ -133,6 +133,7 @@ typedef struct asynmsg * result is stored in 'result' */ #define AMF_NOTIFY 4 /* Send a notification when AMF_DONE is set */ +#define AMF_NOREPLY 8 /* Not a reply message for a SENDREC */ /* Hide names to avoid name space pollution. */ #define echo _echo diff --git a/include/minix/syslib.h b/include/minix/syslib.h index d4772e59a..b3f6d0cad 100755 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -40,6 +40,11 @@ _PROTOTYPE( int sys_newmap, (endpoint_t proc_ep, struct mem_map *ptr)); _PROTOTYPE( int sys_exit, (endpoint_t proc_ep)); _PROTOTYPE( int sys_trace, (int req, endpoint_t proc_ep, long addr, long *data_p)); +/* Shorthands for sys_runctl() system call. */ +#define sys_stop(proc_ep) sys_runctl(proc_ep, RC_STOP) +#define sys_resume(proc_ep) sys_runctl(proc_ep, RC_RESUME) +_PROTOTYPE( int sys_runctl, (endpoint_t proc_ep, int action)); + _PROTOTYPE( int sys_privctl, (endpoint_t proc_ep, int req, void *p)); _PROTOTYPE( int sys_setgrant, (cp_grant_t *grants, int ngrants)); _PROTOTYPE( int sys_nice, (endpoint_t proc_ep, int priority)); diff --git a/include/minix/sysutil.h b/include/minix/sysutil.h index e0b2987b8..7318bd94d 100644 --- a/include/minix/sysutil.h +++ b/include/minix/sysutil.h @@ -56,10 +56,11 @@ _PROTOTYPE( void util_nstrcat, (char *str, unsigned long n) ); _PROTOTYPE( void util_stacktrace_strcat, (char *)); _PROTOTYPE( int micro_delay, (u32_t micros)); _PROTOTYPE( u32_t micros_to_ticks, (u32_t micros)); -_PROTOTYPE( int asynsend, (endpoint_t ep, message *msg)); _PROTOTYPE( void ser_putc, (char c)); _PROTOTYPE( void get_randomness, (struct k_randomness *, int)); +#define asynsend(ep, msg) asynsend3(ep, msg, 0) +_PROTOTYPE( int asynsend3, (endpoint_t ep, message *msg, int flags)); #define ASSERT(c) if(!(c)) { panic(__FILE__, "assert " #c " failed at line", __LINE__); } diff --git a/include/minix/vm.h b/include/minix/vm.h index 7ac40f9c5..ef7692fa3 100755 --- a/include/minix/vm.h +++ b/include/minix/vm.h @@ -7,7 +7,7 @@ #include _PROTOTYPE( int vm_exit, (endpoint_t ep)); -_PROTOTYPE( int vm_fork, (endpoint_t ep, int slotno, int *child_ep)); +_PROTOTYPE( int vm_fork, (endpoint_t ep, int slotno, endpoint_t *child_ep)); _PROTOTYPE( int vm_brk, (endpoint_t ep, char *newaddr)); _PROTOTYPE( int vm_exec_newmem, (endpoint_t ep, struct exec_newmem *args, int args_bytes, char **ret_stack_top, int *ret_flags)); diff --git a/include/signal.h b/include/signal.h index a52c77420..2b47fc269 100755 --- a/include/signal.h +++ b/include/signal.h @@ -66,7 +66,7 @@ typedef unsigned long sigset_t; */ #define SIGKMESS 29 /* new kernel message */ #define SIGKSIG 30 /* kernel signal pending */ -#define SIGKSTOP 31 /* kernel shutting down */ +#define SIGKREADY 31 /* ready for signal delivery */ #endif @@ -113,15 +113,29 @@ _PROTOTYPE( int kill, (pid_t _pid, int _sig) ); _PROTOTYPE( int killpg, (pid_t _pgrp, int _sig) ); _PROTOTYPE( int sigaction, (int _sig, const struct sigaction *_act, struct sigaction *_oact) ); +_PROTOTYPE( int sigpending, (sigset_t *_set) ); +_PROTOTYPE( int sigprocmask, + (int _how, const sigset_t *_set, sigset_t *_oset) ); +_PROTOTYPE( int sigsuspend, (const sigset_t *_sigmask) ); + +/* For the sigset functions, only use the library version with error + * checking from user programs. System programs need to be able to use + * nonstanard signals. + */ +#ifndef _SYSTEM _PROTOTYPE( int sigaddset, (sigset_t *_set, int _sig) ); _PROTOTYPE( int sigdelset, (sigset_t *_set, int _sig) ); _PROTOTYPE( int sigemptyset, (sigset_t *_set) ); _PROTOTYPE( int sigfillset, (sigset_t *_set) ); _PROTOTYPE( int sigismember, (const sigset_t *_set, int _sig) ); -_PROTOTYPE( int sigpending, (sigset_t *_set) ); -_PROTOTYPE( int sigprocmask, - (int _how, const sigset_t *_set, sigset_t *_oset) ); -_PROTOTYPE( int sigsuspend, (const sigset_t *_sigmask) ); +#else +#define sigaddset(set, sig) ((int) ((*(set) |= (1 << (sig))) && 0)) +#define sigdelset(set, sig) ((int) ((*(set) &= ~(1 << (sig))) && 0)) +#define sigemptyset(set) ((int) (*(set) = 0)) +#define sigfillset(set) ((int) ((*(set) = ~0) && 0)) +#define sigismember(set, sig) ((*(set) & (1 << (sig))) ? 1 : 0) +#endif + #endif #endif /* _SIGNAL_H */ diff --git a/include/sys/ptrace.h b/include/sys/ptrace.h index ccb450312..e9694dd9e 100755 --- a/include/sys/ptrace.h +++ b/include/sys/ptrace.h @@ -5,6 +5,7 @@ #ifndef _PTRACE_H #define _PTRACE_H +/* Trace requests. */ #define T_STOP -1 /* stop the process */ #define T_OK 0 /* enable tracing by parent for this process */ #define T_GETINS 1 /* return value from instruction space */ @@ -16,6 +17,10 @@ #define T_RESUME 7 /* resume execution */ #define T_EXIT 8 /* exit */ #define T_STEP 9 /* set trace bit */ +#define T_SYSCALL 10 /* trace system call */ +#define T_ATTACH 11 /* attach to a running process */ +#define T_DETACH 12 /* detach from a traced process */ +#define T_SETOPT 13 /* set trace options */ #define T_READB_INS 100 /* Read a byte from the text segment of an * untraced process (only for root) @@ -24,6 +29,10 @@ * untraced process (only for root) */ +/* Trace options. */ +#define TO_TRACEFORK 0x1 /* automatically attach to forked children */ +#define TO_ALTEXEC 0x2 /* send SIGSTOP on successful exec() */ + /* Function Prototypes. */ #ifndef _ANSI_H #include diff --git a/include/sys/resource.h b/include/sys/resource.h index 1b14af4e2..6dc5bd869 100755 --- a/include/sys/resource.h +++ b/include/sys/resource.h @@ -8,9 +8,6 @@ #define PRIO_MIN -20 #define PRIO_MAX 20 -/* Magic, invalid priority to stop the process. */ -#define PRIO_STOP 76 - #define PRIO_PROCESS 0 #define PRIO_PGRP 1 #define PRIO_USER 2 diff --git a/kernel/arch/i386/exception.c b/kernel/arch/i386/exception.c index 7e54f2745..995e9601b 100755 --- a/kernel/arch/i386/exception.c +++ b/kernel/arch/i386/exception.c @@ -166,6 +166,7 @@ struct proc *t; * k_reenter larger than zero. */ if (k_reenter == 0 && ! iskernelp(saved_proc)) { +#if 0 { kprintf( @@ -181,6 +182,7 @@ struct proc *t; proc_stacktrace(saved_proc); } +#endif cause_sig(proc_nr(saved_proc), ep->signum); return; } diff --git a/kernel/arch/i386/mpx386.s b/kernel/arch/i386/mpx386.s index 3c2248c48..742891545 100755 --- a/kernel/arch/i386/mpx386.s +++ b/kernel/arch/i386/mpx386.s @@ -367,10 +367,8 @@ set_restart1: _s_call: _p_s_call: cld ! set direction flag to a known value - sub esp, 6*4 ! skip RETADR, eax, ecx, edx, ebx, est - push ebp ! stack already points into proc table - push esi - push edi + sub esp, 4 ! skip RETADR + pushad ! save "general" registers o16 push ds o16 push es o16 push fs diff --git a/kernel/arch/i386/system.c b/kernel/arch/i386/system.c index 80a7fb9a3..b8bb73f22 100644 --- a/kernel/arch/i386/system.c +++ b/kernel/arch/i386/system.c @@ -353,3 +353,23 @@ PUBLIC int arch_set_params(char *params, int size) return OK; } +PUBLIC void arch_do_syscall(struct proc *proc) +{ +/* Perform a previously postponed system call. + */ + int call_nr, src_dst_e; + message *m_ptr; + long bit_map; + + /* Get the system call parameters from their respective registers. */ + call_nr = proc->p_reg.cx; + src_dst_e = proc->p_reg.retreg; + m_ptr = (message *) proc->p_reg.bx; + bit_map = proc->p_reg.dx; + + /* sys_call() expects the given process's memory to be accessible. */ + vm_set_cr3(proc); + + /* Make the system call, for real this time. */ + proc->p_reg.retreg = sys_call(call_nr, src_dst_e, m_ptr, bit_map); +} diff --git a/kernel/clock.c b/kernel/clock.c index 0be366e4b..d9aa3ba2a 100755 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -34,6 +34,7 @@ #include "proc.h" #include #include +#include #include /* Function prototype for PRIVATE functions. @@ -79,14 +80,21 @@ PUBLIC void clock_task() minix_panic("receive() failed", result); /* Handle the request. Only clock ticks are expected. */ - switch (m.m_type) { - case HARD_INT: - do_clocktick(&m); /* handle clock tick */ - break; - default: /* illegal request type */ + if (is_notify(m.m_type)) { + switch (_ENDPOINT_P(m.m_source)) { + case HARDWARE: + do_clocktick(&m); /* handle clock tick */ + break; + default: /* illegal request type */ + kprintf("CLOCK: illegal notify %d from %d.\n", + m.m_type, m.m_source); + } + } + else { + /* illegal request type */ kprintf("CLOCK: illegal request %d from %d.\n", m.m_type, m.m_source); - } + } } } diff --git a/kernel/config.h b/kernel/config.h index 07b9c8770..f1a0fcbf4 100644 --- a/kernel/config.h +++ b/kernel/config.h @@ -43,6 +43,7 @@ #define USE_PHYSCOPY 1 /* copy using physical addressing */ #define USE_PHYSVCOPY 1 /* vector with physical copy requests */ #define USE_MEMSET 1 /* write char to a given memory area */ +#define USE_RUNCTL 1 /* control stop flags of a process */ /* Length of program names stored in the process table. This is only used * for the debugging dumps that can be generated with the IS server. The PM diff --git a/kernel/debug.c b/kernel/debug.c index 7324d4857..e710f6a21 100644 --- a/kernel/debug.c +++ b/kernel/debug.c @@ -117,7 +117,7 @@ rtsflagstr(int flags) #define FLAG(n) if(flags & n) { strcat(str, #n " "); } FLAG(SLOT_FREE); - FLAG(NO_PRIORITY); + FLAG(PROC_STOP); FLAG(SENDING); FLAG(RECEIVING); FLAG(SIGNALED); diff --git a/kernel/main.c b/kernel/main.c index f9c710ab7..885dddb94 100755 --- a/kernel/main.c +++ b/kernel/main.c @@ -87,7 +87,7 @@ PUBLIC void main() priv(rp)->s_trap_mask = ip->trap_mask; /* allowed traps */ /* Warn about violations of the boot image table order consistency. */ - if (priv_id(rp) != s_nr_to_id(ip->proc_nr)) + if (priv_id(rp) != s_nr_to_id(ip->proc_nr) && (ip->flags & SYS_PROC)) kprintf("Warning: boot image table has wrong process order\n"); /* Initialize call mask bitmap from unordered set. @@ -173,7 +173,7 @@ PUBLIC void main() RTS_SET(rp, VMINHIBIT); /* Set ready. The HARDWARE task is never ready. */ - if (rp->p_nr == HARDWARE) RTS_SET(rp, NO_PRIORITY); + if (rp->p_nr == HARDWARE) RTS_SET(rp, PROC_STOP); RTS_UNSET(rp, SLOT_FREE); /* remove SLOT_FREE and schedule */ alloc_segments(rp); } diff --git a/kernel/proc.c b/kernel/proc.c index 24289c5e7..5ef4555e9 100755 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -59,7 +59,8 @@ FORWARD _PROTOTYPE( int mini_senda, (struct proc *caller_ptr, FORWARD _PROTOTYPE( int deadlock, (int function, register struct proc *caller, int src_dst)); FORWARD _PROTOTYPE( int try_async, (struct proc *caller_ptr)); -FORWARD _PROTOTYPE( int try_one, (struct proc *src_ptr, struct proc *dst_ptr)); +FORWARD _PROTOTYPE( int try_one, (struct proc *src_ptr, struct proc *dst_ptr, + int *postponed)); FORWARD _PROTOTYPE( void sched, (struct proc *rp, int *queue, int *front)); FORWARD _PROTOTYPE( void pick_proc, (void)); @@ -137,21 +138,76 @@ PUBLIC void schedcheck(void) } vmassert(proc_ptr); vmassert(!proc_ptr->p_rts_flags); - while(proc_ptr->p_misc_flags & MF_DELIVERMSG) { + while (proc_ptr->p_misc_flags & + (MF_DELIVERMSG | MF_SC_DEFER | MF_SC_TRACE | MF_SC_ACTIVE)) { + vmassert(!next_ptr); vmassert(!proc_ptr->p_rts_flags); - TRACE(VF_SCHEDULING, printf("delivering to %s / %d\n", - proc_ptr->p_name, proc_ptr->p_endpoint);); - if(delivermsg(proc_ptr) == VMSUSPEND) { - vmassert(next_ptr); - TRACE(VF_SCHEDULING, printf("suspending %s / %d\n", + if (proc_ptr->p_misc_flags & MF_DELIVERMSG) { + TRACE(VF_SCHEDULING, printf("delivering to %s / %d\n", proc_ptr->p_name, proc_ptr->p_endpoint);); - vmassert(proc_ptr->p_rts_flags); - vmassert(next_ptr != proc_ptr); + if(delivermsg(proc_ptr) == VMSUSPEND) { + vmassert(next_ptr); + TRACE(VF_SCHEDULING, + printf("suspending %s / %d\n", + proc_ptr->p_name, + proc_ptr->p_endpoint);); + vmassert(proc_ptr->p_rts_flags); + vmassert(next_ptr != proc_ptr); + } + } + else if (proc_ptr->p_misc_flags & MF_SC_DEFER) { + /* Perform the system call that we deferred earlier. */ + +#if DEBUG_SCHED_CHECK + if (proc_ptr->p_misc_flags & MF_SC_ACTIVE) + minix_panic("MF_SC_ACTIVE and MF_SC_DEFER set", + NO_NUM); +#endif + + arch_do_syscall(proc_ptr); + + /* If the process is stopped for signal delivery, and + * not blocked sending a message after the system call, + * inform PM. + */ + if ((proc_ptr->p_misc_flags & MF_SIG_DELAY) && + !RTS_ISSET(proc_ptr, SENDING)) + sig_delay_done(proc_ptr); + } + else if (proc_ptr->p_misc_flags & MF_SC_TRACE) { + /* Trigger a system call leave event if this was a + * system call. We must do this after processing the + * other flags above, both for tracing correctness and + * to be able to use 'break'. + */ + if (!(proc_ptr->p_misc_flags & MF_SC_ACTIVE)) + break; + + proc_ptr->p_misc_flags &= + ~(MF_SC_TRACE | MF_SC_ACTIVE); + + /* Signal the "leave system call" event. + * Block the process. + */ + cause_sig(proc_nr(proc_ptr), SIGTRAP); + } + else if (proc_ptr->p_misc_flags & MF_SC_ACTIVE) { + /* If MF_SC_ACTIVE was set, remove it now: + * we're leaving the system call. + */ + proc_ptr->p_misc_flags &= ~MF_SC_ACTIVE; + + break; + } + + /* If proc_ptr is now descheduled, + * continue with another process. + */ + if (next_ptr) { proc_ptr = next_ptr; - vmassert(!proc_ptr->p_rts_flags); next_ptr = NULL; - } + } } TRACE(VF_SCHEDULING, printf("starting %s / %d\n", proc_ptr->p_name, proc_ptr->p_endpoint);); @@ -181,6 +237,37 @@ long bit_map; /* notification event set or flags */ int src_dst_p; /* Process slot number */ size_t msg_size; + /* If this process is subject to system call tracing, handle that first. */ + if (caller_ptr->p_misc_flags & (MF_SC_TRACE | MF_SC_DEFER)) { + /* Are we tracing this process, and is it the first sys_call entry? */ + if ((caller_ptr->p_misc_flags & (MF_SC_TRACE | MF_SC_DEFER)) == + MF_SC_TRACE) { + /* We must notify the tracer before processing the actual + * system call. If we don't, the tracer could not obtain the + * input message. Postpone the entire system call. + */ + caller_ptr->p_misc_flags &= ~MF_SC_TRACE; + caller_ptr->p_misc_flags |= MF_SC_DEFER; + + /* Signal the "enter system call" event. Block the process. */ + cause_sig(proc_nr(caller_ptr), SIGTRAP); + + /* Preserve the return register's value. */ + return caller_ptr->p_reg.retreg; + } + + /* If the MF_SC_DEFER flag is set, the syscall is now being resumed. */ + caller_ptr->p_misc_flags &= ~MF_SC_DEFER; + +#if DEBUG_SCHED_CHECK + if (caller_ptr->p_misc_flags & MF_SC_ACTIVE) + minix_panic("MF_SC_ACTIVE already set", NO_NUM); +#endif + + /* Set a flag to allow reliable tracing of leaving the system call. */ + caller_ptr->p_misc_flags |= MF_SC_ACTIVE; + } + #if DEBUG_SCHED_CHECK if(caller_ptr->p_misc_flags & MF_DELIVERMSG) { kprintf("sys_call: MF_DELIVERMSG on for %s / %d\n", @@ -593,6 +680,8 @@ int flags; vmassert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); QueueMess((*xpp)->p_endpoint, vir2phys(&(*xpp)->p_sendmsg), caller_ptr); + if ((*xpp)->p_misc_flags & MF_SIG_DELAY) + sig_delay_done(*xpp); RTS_UNSET(*xpp, SENDING); *xpp = (*xpp)->p_q_link; /* remove from queue */ return(OK); /* report success */ @@ -603,16 +692,10 @@ int flags; if (caller_ptr->p_misc_flags & MF_ASYNMSG) { if (src_e != ANY) - { -#if 0 - kprintf("mini_receive: should try async from %d\n", src_e); -#endif - r= EAGAIN; - } + r= try_one(proc_addr(src_p), caller_ptr, NULL); else - { r= try_async(caller_ptr); - } + if (r == OK) return OK; /* Got a message */ } @@ -772,7 +855,7 @@ size_t size; continue; /* Check for reserved bits in the flags field */ - if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY) || + if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY) || !(flags & AMF_VALID)) { return EINVAL; @@ -831,14 +914,13 @@ size_t size; continue; } - /* Check if 'dst' is blocked waiting for this message. The - * destination's SENDING flag may be set when its SENDREC call - * blocked while sending. + /* Check if 'dst' is blocked waiting for this message. + * If AMF_NOREPLY is set, do not satisfy the receiving part of + * a SENDREC. */ - if ( (dst_ptr->p_rts_flags & (RECEIVING | SENDING)) == - RECEIVING && - (dst_ptr->p_getfrom_e == ANY || - dst_ptr->p_getfrom_e == caller_ptr->p_endpoint)) + if (WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) && + (!(flags & AMF_NOREPLY) || + !(dst_ptr->p_misc_flags & MF_REPLY_PEND))) { /* Destination is indeed waiting for this message. */ m_ptr= &table[i].msg; /* Note: pointer in the @@ -887,25 +969,25 @@ struct proc *caller_ptr; int r; struct priv *privp; struct proc *src_ptr; - + int postponed = FALSE; + /* Try all privilege structures */ for (privp = BEG_PRIV_ADDR; privp < END_PRIV_ADDR; ++privp) { - if (privp->s_proc_nr == NONE || privp->s_id == USER_PRIV_ID) - continue; - if (privp->s_asynsize == 0) + if (privp->s_proc_nr == NONE) continue; + src_ptr= proc_addr(privp->s_proc_nr); - if (!may_send_to(src_ptr, proc_nr(caller_ptr))) - continue; + vmassert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); - r= try_one(src_ptr, caller_ptr); + r= try_one(src_ptr, caller_ptr, &postponed); if (r == OK) return r; } - /* Nothing found, clear MF_ASYNMSG */ - caller_ptr->p_misc_flags &= ~MF_ASYNMSG; + /* Nothing found, clear MF_ASYNMSG unless messages were postponed */ + if (postponed == FALSE) + caller_ptr->p_misc_flags &= ~MF_ASYNMSG; return ESRCH; } @@ -914,9 +996,10 @@ struct proc *caller_ptr; /*===========================================================================* * try_one * *===========================================================================*/ -PRIVATE int try_one(src_ptr, dst_ptr) +PRIVATE int try_one(src_ptr, dst_ptr, postponed) struct proc *src_ptr; struct proc *dst_ptr; +int *postponed; { int i, do_notify, done; unsigned flags; @@ -931,6 +1014,12 @@ struct proc *dst_ptr; int r; privp= priv(src_ptr); + + /* Basic validity checks */ + if (privp->s_id == USER_PRIV_ID) return EAGAIN; + if (privp->s_asynsize == 0) return EAGAIN; + if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return EAGAIN; + size= privp->s_asynsize; table_v = privp->s_asyntab; caller_ptr = src_ptr; @@ -953,7 +1042,7 @@ struct proc *dst_ptr; } /* Check for reserved bits in the flags field */ - if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY) || + if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY) || !(flags & AMF_VALID)) { kprintf("try_one: bad bits in table\n"); @@ -980,6 +1069,19 @@ struct proc *dst_ptr; continue; } + /* If AMF_NOREPLY is set, do not satisfy the receiving part of + * a SENDREC. Do not unset MF_ASYNMSG later because of this, + * though: this message is still to be delivered later. + */ + if ((flags & AMF_NOREPLY) && + (dst_ptr->p_misc_flags & MF_REPLY_PEND)) + { + if (postponed != NULL) + *postponed = TRUE; + + continue; + } + /* Deliver message */ table_ptr= (asynmsg_t *)privp->s_asyntab; m_ptr= &table_ptr[i].msg; /* Note: pointer in the diff --git a/kernel/proc.h b/kernel/proc.h index ac07514b0..8debbb88f 100755 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -106,7 +106,7 @@ struct proc { /* Bits for the runtime flags. A process is runnable iff p_rts_flags == 0. */ #define SLOT_FREE 0x01 /* process slot is free */ -#define NO_PRIORITY 0x02 /* process has been stopped */ +#define PROC_STOP 0x02 /* process has been stopped */ #define SENDING 0x04 /* process blocked trying to send */ #define RECEIVING 0x08 /* process blocked trying to receive */ #define SIGNALED 0x10 /* set when new kernel signal arrives */ @@ -118,6 +118,7 @@ struct proc { #define PAGEFAULT 0x400 /* process has unhandled pagefault */ #define VMREQUEST 0x800 /* originator of vm memory request */ #define VMREQTARGET 0x1000 /* target of vm memory request */ +#define SYS_LOCK 0x2000 /* temporary process lock flag for systask */ /* These runtime flags can be tested and manipulated by these macros. */ @@ -177,12 +178,16 @@ struct proc { } while(0) /* Misc flags */ -#define MF_REPLY_PEND 0x01 /* reply to IPC_REQUEST is pending */ -#define MF_VIRT_TIMER 0x02 /* process-virtual timer is running */ -#define MF_PROF_TIMER 0x04 /* process-virtual profile timer is running */ -#define MF_ASYNMSG 0x10 /* Asynchrous message pending */ -#define MF_FULLVM 0x20 -#define MF_DELIVERMSG 0x40 /* Copy message for him before running */ +#define MF_REPLY_PEND 0x001 /* reply to IPC_REQUEST is pending */ +#define MF_VIRT_TIMER 0x002 /* process-virtual timer is running */ +#define MF_PROF_TIMER 0x004 /* process-virtual profile timer is running */ +#define MF_ASYNMSG 0x010 /* Asynchrous message pending */ +#define MF_FULLVM 0x020 +#define MF_DELIVERMSG 0x040 /* Copy message for him before running */ +#define MF_SIG_DELAY 0x080 /* Send signal when no longer sending */ +#define MF_SC_ACTIVE 0x100 /* Syscall tracing: in a system call now */ +#define MF_SC_DEFER 0x200 /* Syscall tracing: deferred system call */ +#define MF_SC_TRACE 0x400 /* Syscall tracing: trigger syscall events */ /* Scheduling priorities for p_priority. Values must start at zero (highest * priority) and increment. Priorities of the processes in the boot image diff --git a/kernel/proto.h b/kernel/proto.h index 929950585..769040f7b 100755 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -58,6 +58,7 @@ _PROTOTYPE( void set_sendto_bit, (struct proc *rc, int id) ); _PROTOTYPE( void unset_sendto_bit, (struct proc *rc, int id) ); _PROTOTYPE( void send_sig, (int proc_nr, int sig_nr) ); _PROTOTYPE( void cause_sig, (int proc_nr, int sig_nr) ); +_PROTOTYPE( void sig_delay_done, (struct proc *rp) ); _PROTOTYPE( void sys_task, (void) ); #define numap_local(proc_nr, vir_addr, bytes) \ umap_local(proc_addr(proc_nr), D, (vir_addr), (bytes)) @@ -173,5 +174,6 @@ _PROTOTYPE( int vm_suspend, (struct proc *caller, struct proc *target, _PROTOTYPE( int delivermsg, (struct proc *target)); _PROTOTYPE( phys_bytes arch_switch_copymsg, (struct proc *rp, message *m, phys_bytes lin)); +_PROTOTYPE( void arch_do_syscall, (struct proc *proc) ); #endif /* PROTO_H */ diff --git a/kernel/system.c b/kernel/system.c index c0b8afdae..3e7fff8e9 100755 --- a/kernel/system.c +++ b/kernel/system.c @@ -16,6 +16,7 @@ * unset_sendto_bit: disallow a process from sending messages to a target * send_sig: send a signal directly to a system process * cause_sig: take action to cause a signal to occur via PM + * sig_delay_done: tell PM that a process is not sending * umap_bios: map virtual address in BIOS_SEG to physical * get_randomness: accumulate randomness in a buffer * clear_endpoint: remove a process' ability to send and receive messages @@ -178,6 +179,7 @@ PRIVATE void initialize(void) map(SYS_PRIVCTL, do_privctl); /* system privileges control */ map(SYS_TRACE, do_trace); /* request a trace operation */ map(SYS_SETGRANT, do_setgrant); /* get/set own parameters */ + map(SYS_RUNCTL, do_runctl); /* set/clear stop flag of a process */ /* Signal handling. */ map(SYS_KILL, do_kill); /* cause a process to be signaled */ @@ -365,6 +367,22 @@ int sig_nr; /* signal to be sent, 1 to _NSIG */ } } +/*===========================================================================* + * sig_delay_done * + *===========================================================================*/ +PUBLIC void sig_delay_done(rp) +struct proc *rp; +{ +/* A process is now known not to send any direct messages. + * Tell PM by sending a signal to the process. + * Used for actual signal delivery. + */ + + rp->p_misc_flags &= ~MF_SIG_DELAY; + + cause_sig(proc_nr(rp), SIGKREADY); +} + #if _MINIX_CHIP == _CHIP_INTEL /*===========================================================================* diff --git a/kernel/system.h b/kernel/system.h index d35c7a474..06aafb4fe 100644 --- a/kernel/system.h +++ b/kernel/system.h @@ -69,6 +69,11 @@ _PROTOTYPE( int do_nice, (message *m_ptr) ); #define do_nice do_unused #endif +_PROTOTYPE( int do_runctl, (message *m_ptr) ); +#if ! USE_RUNCTL +#define do_runctl do_unused +#endif + _PROTOTYPE( int do_copy, (message *m_ptr) ); #define do_vircopy do_copy #if ! (USE_VIRCOPY || USE_PHYSCOPY) diff --git a/kernel/system/Makefile b/kernel/system/Makefile index a93e64f53..f0e4e3789 100644 --- a/kernel/system/Makefile +++ b/kernel/system/Makefile @@ -29,6 +29,7 @@ OBJECTS = \ $(SYSTEM)(do_exit.o) \ $(SYSTEM)(do_trace.o) \ $(SYSTEM)(do_nice.o) \ + $(SYSTEM)(do_runctl.o) \ $(SYSTEM)(do_times.o) \ $(SYSTEM)(do_setalarm.o) \ $(SYSTEM)(do_stime.o) \ @@ -92,6 +93,9 @@ $(SYSTEM)(do_trace.o): do_trace.c $(SYSTEM)(do_nice.o): do_nice.c $(CC) do_nice.c +$(SYSTEM)(do_runctl.o): do_runctl.c + $(CC) do_runctl.c + $(SYSTEM)(do_times.o): do_times.c $(CC) do_times.c diff --git a/kernel/system/do_fork.c b/kernel/system/do_fork.c index aa94a3418..3e8fc83c3 100644 --- a/kernel/system/do_fork.c +++ b/kernel/system/do_fork.c @@ -74,8 +74,7 @@ register message *m_ptr; /* pointer to request message */ rpc->p_sys_time = 0; rpc->p_reg.psw &= ~TRACEBIT; /* clear trace bit */ - - rpc->p_misc_flags &= ~(MF_VIRT_TIMER | MF_PROF_TIMER); + rpc->p_misc_flags &= ~(MF_VIRT_TIMER | MF_PROF_TIMER | MF_SC_TRACE); rpc->p_virt_left = 0; /* disable, clear the process-virtual timers */ rpc->p_prof_left = 0; diff --git a/kernel/system/do_nice.c b/kernel/system/do_nice.c index e31403fc3..dd9d4447d 100644 --- a/kernel/system/do_nice.c +++ b/kernel/system/do_nice.c @@ -27,32 +27,25 @@ PUBLIC int do_nice(message *m_ptr) pri = m_ptr->PR_PRIORITY; rp = proc_addr(proc_nr); - if (pri == PRIO_STOP) { - /* Take process off the scheduling queues. */ - RTS_LOCK_SET(rp, NO_PRIORITY); - return(OK); - } - else if (pri >= PRIO_MIN && pri <= PRIO_MAX) { + /* The value passed in is currently between PRIO_MIN and PRIO_MAX. + * We have to scale this between MIN_USER_Q and MAX_USER_Q to match + * the kernel's scheduling queues. + */ + if (pri < PRIO_MIN || pri > PRIO_MAX) return(EINVAL); - /* The value passed in is currently between PRIO_MIN and PRIO_MAX. - * We have to scale this between MIN_USER_Q and MAX_USER_Q to match - * the kernel's scheduling queues. - */ - new_q = MAX_USER_Q + (pri-PRIO_MIN) * (MIN_USER_Q-MAX_USER_Q+1) / - (PRIO_MAX-PRIO_MIN+1); - if (new_q < MAX_USER_Q) new_q = MAX_USER_Q; /* shouldn't happen */ - if (new_q > MIN_USER_Q) new_q = MIN_USER_Q; /* shouldn't happen */ + new_q = MAX_USER_Q + (pri-PRIO_MIN) * (MIN_USER_Q-MAX_USER_Q+1) / + (PRIO_MAX-PRIO_MIN+1); + if (new_q < MAX_USER_Q) new_q = MAX_USER_Q; /* shouldn't happen */ + if (new_q > MIN_USER_Q) new_q = MIN_USER_Q; /* shouldn't happen */ - /* Make sure the process is not running while changing its priority. - * Put the process back in its new queue if it is runnable. - */ - RTS_LOCK_SET(rp, NO_PRIORITY); - rp->p_max_priority = rp->p_priority = new_q; - RTS_LOCK_UNSET(rp, NO_PRIORITY); + /* Make sure the process is not running while changing its priority. + * Put the process back in its new queue if it is runnable. + */ + RTS_LOCK_SET(rp, SYS_LOCK); + rp->p_max_priority = rp->p_priority = new_q; + RTS_LOCK_UNSET(rp, SYS_LOCK); - return(OK); - } - return(EINVAL); + return(OK); } #endif /* USE_NICE */ diff --git a/kernel/system/do_runctl.c b/kernel/system/do_runctl.c new file mode 100644 index 000000000..5b24e259e --- /dev/null +++ b/kernel/system/do_runctl.c @@ -0,0 +1,68 @@ +/* The kernel call implemented in this file: + * m_type: SYS_RUNCTL + * + * The parameters for this kernel call are: + * m1_i1: RC_ENDPT process number to control + * m1_i2: RC_ACTION stop or resume the process + */ + +#include "../system.h" +#include + +#if USE_RUNCTL + +/*===========================================================================* + * do_runctl * + *===========================================================================*/ +PUBLIC int do_runctl(message *m_ptr) +{ +/* Control a process's PROC_STOP flag. Used for process management. + * If the process is queued sending a message or stopped for system call + * tracing, set MF_SIG_DELAY instead of PROC_STOP, and send a SIGKREADY signal + * later when the process is done sending. Used by PM for safe signal delivery. + */ + int proc_nr, action, delayed; + register struct proc *rp; + + /* Extract the message parameters and do sanity checking. */ + if (!isokendpt(m_ptr->RC_ENDPT, &proc_nr)) return(EINVAL); + if (iskerneln(proc_nr)) return(EPERM); + rp = proc_addr(proc_nr); + + action = m_ptr->RC_ACTION; + + /* Is the target sending or syscall-traced? Then set MF_SIG_DELAY instead. + * The process will not become runnable before PM has called SYS_ENDKSIG. + * Note that asynchronous messages are not covered: a process using SENDA + * should not also install signal handlers *and* expect POSIX compliance. + */ + if (action == RC_STOP) { + RTS_LOCK_SET(rp, SYS_LOCK); + + if (RTS_ISSET(rp, SENDING) || (rp->p_misc_flags & MF_SC_DEFER)) + rp->p_misc_flags |= MF_SIG_DELAY; + + delayed = (rp->p_misc_flags & MF_SIG_DELAY); + + RTS_LOCK_UNSET(rp, SYS_LOCK); + + if (delayed) return(EBUSY); + } + + /* Either set or clear the stop flag. */ + switch (action) { + case RC_STOP: + RTS_LOCK_SET(rp, PROC_STOP); + break; + case RC_RESUME: + RTS_LOCK_UNSET(rp, PROC_STOP); + break; + default: + return(EINVAL); + } + + return(OK); +} + +#endif /* USE_RUNCTL */ + diff --git a/kernel/system/do_sigsend.c b/kernel/system/do_sigsend.c index 879a05ad0..e710b1c67 100644 --- a/kernel/system/do_sigsend.c +++ b/kernel/system/do_sigsend.c @@ -75,10 +75,7 @@ message *m_ptr; /* pointer to request message */ rp->p_reg.sp = (reg_t) frp; rp->p_reg.pc = (reg_t) smsg.sm_sighandler; - /* Reschedule if necessary. */ - if(RTS_ISSET(rp, NO_PRIORITY)) - RTS_LOCK_UNSET(rp, NO_PRIORITY); - else { + if(!RTS_ISSET(rp, PROC_STOP)) { struct proc *caller; caller = proc_addr(who_p); kprintf("system: warning: sigsend a running process\n"); diff --git a/kernel/system/do_trace.c b/kernel/system/do_trace.c index b13b66b3c..9511c0f99 100644 --- a/kernel/system/do_trace.c +++ b/kernel/system/do_trace.c @@ -34,6 +34,7 @@ register message *m_ptr; * T_RESUME resume execution * T_EXIT exit * T_STEP set trace bit + * T_SYSCALL trace system call * * The T_OK and T_EXIT commands are handled completely by the process manager, * all others come here. @@ -81,11 +82,12 @@ register message *m_ptr; if (iskerneln(tr_proc_nr)) return(EPERM); rp = proc_addr(tr_proc_nr); - if (isemptyp(rp)) return(EIO); + if (isemptyp(rp)) return(EINVAL); switch (tr_request) { case T_STOP: /* stop process */ RTS_LOCK_SET(rp, P_STOP); rp->p_reg.psw &= ~TRACEBIT; /* clear trace bit */ + rp->p_misc_flags &= ~MF_SC_TRACE; /* clear syscall trace flag */ return(OK); case T_GETINS: /* return value from instruction space */ @@ -102,10 +104,22 @@ register message *m_ptr; break; case T_GETUSER: /* return value from process table */ - if ((tr_addr & (sizeof(long) - 1)) != 0 || - tr_addr > sizeof(struct proc) - sizeof(long)) - return(EIO); - m_ptr->CTL_DATA = *(long *) ((char *) rp + (int) tr_addr); + if ((tr_addr & (sizeof(long) - 1)) != 0) return(EIO); + + if (tr_addr <= sizeof(struct proc) - sizeof(long)) { + m_ptr->CTL_DATA = *(long *) ((char *) rp + (int) tr_addr); + break; + } + + /* The process's proc struct is followed by its priv struct. + * The alignment here should be unnecessary, but better safe.. + */ + i = sizeof(long) - 1; + tr_addr -= (sizeof(struct proc) + i) & ~i; + + if (tr_addr > sizeof(struct priv) - sizeof(long)) return(EIO); + + m_ptr->CTL_DATA = *(long *) ((char *) rp->p_priv + (int) tr_addr); break; case T_SETINS: /* set value in instruction space */ @@ -160,6 +174,12 @@ register message *m_ptr; m_ptr->CTL_DATA = 0; break; + case T_SYSCALL: /* trace system call */ + rp->p_misc_flags |= MF_SC_TRACE; + RTS_LOCK_UNSET(rp, P_STOP); + m_ptr->CTL_DATA = 0; + break; + case T_READB_INS: /* get value from instruction space */ COPYFROMPROC(rp->p_memmap[T].mem_len > 0 ? T : D, tr_addr, (vir_bytes) &ub, 1); m_ptr->CTL_DATA = ub; @@ -171,7 +191,7 @@ register message *m_ptr; break; default: - return(EIO); + return(EINVAL); } return(OK); } diff --git a/lib/ansi/signal.c b/lib/ansi/signal.c index a8cdca1fe..bae9b5290 100755 --- a/lib/ansi/signal.c +++ b/lib/ansi/signal.c @@ -2,7 +2,7 @@ #include #define sigaction _sigaction -#define sigemptyset _sigemptyset +#define _SYSTEM 1 #include PUBLIC sighandler_t signal(sig, disp) diff --git a/lib/posix/_sigset.c b/lib/posix/_sigset.c index d199546f1..91226de5f 100755 --- a/lib/posix/_sigset.c +++ b/lib/posix/_sigset.c @@ -1,12 +1,7 @@ #include -/* XXX - these have to be hidden because signal() uses them and signal() is - * ANSI and not POSIX. It would be surely be better to use macros for the - * library and system uses, and perhaps macros as well as functions for the - * POSIX user interface. The macros would not need underlines. It may be - * inconvenient to match the exact semantics of the current functions - * because the interface is bloated by reporting errors. For library and - * system uses, the signal number is mostly already known to be valid - * before the sigset-changing routines are called. +/* System processes use simpler macros with no range error checking (defined in + * signal.h). The ANSI signal() implementation now also uses the macro + * versions, which makes hiding of the functions here a historical remains. */ #define sigaddset _sigaddset #define sigdelset _sigdelset diff --git a/lib/syslib/Makefile.in b/lib/syslib/Makefile.in index 3fc7305e3..3c2793856 100644 --- a/lib/syslib/Makefile.in +++ b/lib/syslib/Makefile.in @@ -46,6 +46,7 @@ libsys_FILES=" \ sys_out.c \ sys_physcopy.c \ sys_readbios.c \ + sys_runctl.c \ sys_safecopy.c \ sys_sysctl.c \ sys_vsafecopy.c \ diff --git a/lib/syslib/sys_runctl.c b/lib/syslib/sys_runctl.c new file mode 100644 index 000000000..7c31eef4b --- /dev/null +++ b/lib/syslib/sys_runctl.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_runctl * + *===========================================================================*/ +PUBLIC int sys_runctl(endpoint_t proc_ep, int action) +{ + message m; + + m.RC_ENDPT = proc_ep; + m.RC_ACTION = action; + + return(_taskcall(SYSTASK, SYS_RUNCTL, &m)); +} diff --git a/lib/syslib/vm_fork.c b/lib/syslib/vm_fork.c index c67e2cb70..7a433b697 100644 --- a/lib/syslib/vm_fork.c +++ b/lib/syslib/vm_fork.c @@ -6,7 +6,7 @@ /*===========================================================================* * vm_fork * *===========================================================================*/ -PUBLIC int vm_fork(endpoint_t ep, int slot, int *childep) +PUBLIC int vm_fork(endpoint_t ep, int slot, endpoint_t *childep) { message m; int result; diff --git a/lib/sysutil/asynsend.c b/lib/sysutil/asynsend.c index f7607618d..19bab8160 100644 --- a/lib/sysutil/asynsend.c +++ b/lib/sysutil/asynsend.c @@ -22,9 +22,10 @@ PRIVATE asynmsg_t msgtable[ASYN_NR]; PRIVATE int first_slot= 0, next_slot= 0; -PUBLIC int asynsend(dst, mp) +PUBLIC int asynsend3(dst, mp, fl) endpoint_t dst; message *mp; +int fl; { int r, src_ind, dst_ind; unsigned flags; @@ -115,9 +116,10 @@ message *mp; panic(__FILE__, "asynsend: msgtable full", NO_NUM); } + fl |= AMF_VALID; msgtable[next_slot].dst= dst; msgtable[next_slot].msg= *mp; - msgtable[next_slot].flags= AMF_VALID; /* Has to be last. The kernel + msgtable[next_slot].flags= fl; /* Has to be last. The kernel * scans this table while we * are sleeping. */ diff --git a/man/man2/ptrace.2 b/man/man2/ptrace.2 index 35c5752b0..b4b3766c3 100644 --- a/man/man2/ptrace.2 +++ b/man/man2/ptrace.2 @@ -1,10 +1,4 @@ -.\" Copyright (c) 1980 Regents of the University of California. -.\" All rights reserved. The Berkeley software License Agreement -.\" specifies the terms and conditions for redistribution. -.\" -.\" @(#)ptrace.2 6.4 (Berkeley) 5/23/86 -.\" -.TH PTRACE 2 "May 23, 1986" +.TH PTRACE 2 "September 27, 2009" .UC 4 .SH NAME ptrace \- process trace @@ -12,220 +6,179 @@ ptrace \- process trace .nf .ft B #include -#include #include -int ptrace(int \fIrequest\fP, pid_t \fIpid\fP, long \fIaddr\fP, long \fIdata\fP) +long ptrace(int \fIreq\fP, pid_t \fIpid\fP, long \fIaddr\fP, long \fIdata\fP) .ft R .fi .SH DESCRIPTION -.ft B -Note: This manual page has no relation to MINIX 3. Someone who knows ptrace() -has to check, or rewrite, this page. (kjb) -.ft R +The \fBptrace\fP call provides a primitive means to trace (debug) another +process. A process can submit itself to tracing using a \fBT_OK\fP ptrace +request, or can be attached to by a tracer using a \fBT_ATTACH\fP request. +From that point on, whenever a signal is sent to the traced process, +the process will be stopped. Its tracer will be told about the signal +causing the stop, via +.BR wait (2). +The tracer can then inspect the traced process, and choose how to continue the +process's execution and whether to pass on the signal to it. .PP -.B Ptrace -provides a means by which a parent process -may control the execution of a child process, -and examine and change its core image. -Its primary use is for the implementation of breakpoint debugging. -There are four arguments whose interpretation -depends on a -.I request -argument. -Generally, -.I pid -is the process ID of the traced process, -which must be a child (no more distant descendant) -of the tracing process. -A process being traced -behaves normally until it encounters some signal -whether internally generated -like \*(lqillegal instruction\*(rq or externally -generated like \*(lqinterrupt\*(rq. -See -.BR sigaction (2) -for the list. -Then the traced process enters a stopped state -and its parent is notified via -.BR wait (2). -When the child is in the stopped state, -its core image can be examined and modified -using -.BR ptrace . -If desired, another -.B ptrace -request can then cause the child either to terminate -or to continue, possibly ignoring the signal. +In the current model, the tracer will be notified of the signal before any +checks on ignore or block masks are made. A \fBSIGKILL\fP signal cannot be +intercepted by the tracer, and will always kill the traced process. .PP -The value of the -.I request -argument determines the precise -action of the call: -.TP 4 -PT_TRACE_ME -This request is the only one used by the child process; -it declares that the process is to be traced by its parent. -All the other arguments are ignored. -Peculiar results will ensue -if the parent does not expect to trace the child. -.TP 4 -PT_READ_I, PT_READ_D -The -word in the child process's address space -at -.I addr -is returned. -If I and D space are separated (e.g. historically -on a pdp-11), request PT_READ_I indicates I space, -PT_READ_D D space. -.I Addr -must be even on some machines. -The child must be stopped. -The input -.I data -is ignored. -.TP 4 -PT_READ_U -The word -of the system's per-process data area corresponding to -.I addr -is returned. -.I Addr -must be even on some machines and less than 512. -This space contains the registers and other information about -the process; -its layout corresponds to the -.I user -structure in the system. -.TP 4 -PT_WRITE_I, PT_WRITE_D -The -given -.I data -is written at the word in the process's address space corresponding to -.I addr, -which must be even on some machines. -No useful value is returned. -If I and D space are separated, request PT_WRITE_I indicates I space, -PT_WRITE_D D space. -Attempts to write in pure procedure -fail if another process is executing the same file. -.TP 4 -PT_WRITE_U -The process's system data is written, -as it is read with request PT_READ_U. -Only a few locations can be written in this way: -the general registers, -the floating point status and registers, -and certain bits of the processor status word. -.TP 4 -PT_CONTINUE -The -.I data -argument is taken as a signal number -and the child's execution continues -at location -.I addr -as if it had incurred that signal. -Normally the signal number will be -either 0 to indicate that the signal that caused the stop -should be ignored, -or that value fetched out of the -process's image indicating which signal caused -the stop. -If -.I addr -is (int *)1 then execution continues from where it stopped. -.TP 4 -PT_KILL -The traced process terminates. -.TP 4 -PT_STEP -Execution continues as in request PT_CONTINUE; -however, as soon as possible after execution of at least one instruction, -execution stops again. -The signal number from the stop is -SIGTRAP. -(On the VAX-11 the T-bit is used and just one instruction -is executed.) -This is part of the mechanism for implementing breakpoints. +When the traced process performs a successful +.BR execve (2) +call, it will be stopped and a \fBSIGTRAP\fP will be generated for it. +Set-uid and set-gid bits on the new executable are ignored. .PP -As indicated, -these calls -(except for request PT_TRACE_ME) -can be used only when the subject process has stopped. -The -.B wait -call is used to determine -when a process stops; -in such a case the \*(lqtermination\*(rq status -returned by -.B wait -has the value 0177 to indicate stoppage rather -than genuine termination. +The \fIreq\fP parameter specifies the process trace request. The interpretation +of the remaining parameters depends on the given request. For all requests +except \fBT_OK\fP, the \fIpid\fP parameter specifies process ID of the target +process. For all requests except \fBT_OK\fP and \fBT_ATTACH\fP, the process +must be stopped. The following requests are supported: +.TP 2 +.B T_OK +Set the caller's parent to be its tracer. All other arguments are ignored. +This request is typically made by the child fork of a debugger, +before performing an +.BR execve (2) +call. +.TP +.B T_GETINS, T_GETDATA +Retrieve a value from the given process's instruction and data area, +respectively, at the address given in \fIaddr\fP. +.TP +.B T_SETINS, T_SETDATA +Set the value from the given process's instruction and data area, respectively, +at the address given in \fIaddr\fP, to the value given in \fIdata\fP. +.TP +.B T_GETUSER +Retrieve the value at the zero-based offset given in \fIaddr\fP from the +process's \fBstruct proc\fP kernel structure, followed by, aligned on +\fBlong\fP size boundary, its \fBstruct priv\fP kernel structure. +.TP +.B T_SETUSER +Set some of the given process's registers at the beginning of its +\fBstruct proc\fP kernel structure. The value in \fIdata\fP will be written to +the zero-based offset given in \fIaddr\fP from the process's \fBstruct proc\fP +kernel structure. +.TP +.B T_RESUME +Resume execution of the process. A nonzero \fIdata\fP argument will be +interpreted as a signal to pass to the process. +.TP +.B T_STEP +Single-step an instruction. A nonzero \fIdata\fP argument will be interpreted +as a signal to pass to the process. +.TP +.B T_SYSCALL +Resume execution with system call tracing. When the traced process makes a +system call, a \fBSIGTRAP\fP signal will be generated. A subsequent +\fBT_SYSCALL\fP request will then cause a \fBSIGTRAP\fP signal to be generated +when the process leaves the system call. A nonzero \fIdata\fP argument will be +interpreted as a signal to pass to the process. +.TP +.B T_EXIT +Terminate the traced process, with the exit code given in the \fIdata\fP +argument. This call will return once the process has exited. +.TP +.B T_ATTACH +Attach to the given process. The process will be stopped with a \fBSIGSTOP\fP +signal. +.TP +.B T_DETACH +Detach from the given process. Any signals still pending for the tracer are +passed on directly to the process. A nonzero \fIdata\fP argument will be +interpreted as an additional signal to pass to the process. +.TP +.B T_SETOPT +Set the given process's trace options to the bit combination of flags given +in the \fIdata\fP argument. .PP -To forestall possible fraud, -.B ptrace -inhibits the set-user-id and set-group-id facilities -on subsequent -.BR execve (2) -calls. -If a traced process calls -.BR execve , -it will stop before executing the first instruction of the new image -showing signal SIGTRAP. +The following option flags are currently supported for \fBT_SETOPT\fP: +.TP 2 +.B TO_TRACEFORK +When the traced process performs a +.BR fork (2), +automatically attach to the new child as well. +The child will be stopped with a \fBSIGSTOP\fP signal right after forking. +.TP +.B TO_ALTEXEC +Send \fBSIGSTOP\fP instead of \fBSIGTRAP\fP upon a successful +.BR execve (2). +This allows the tracer to disambiguate between this case and other traps. .PP -On a VAX-11, \*(lqword\*(rq also means a 32-bit integer, -but the \*(lqeven\*(rq -restriction does not apply. -.SH "RETURN VALUE -A 0 value is returned if the call succeeds. If the call fails -then a \-1 is returned and the global variable \fIerrno\fP is -set to indicate the error. -.SH "ERRORS -.TP 15 -[EIO] -The request code is invalid. -.TP 15 -[ESRCH] -The specified process does not exist. -.TP 15 -[EIO] -The given signal number is invalid. -.TP 15 -[EIO] -The specified address is out of bounds. -.TP 15 -[EPERM] -The specified process cannot be traced. +All addresses specified for the \fBT_GET\fP* and \fBT_SET\fP* requests must be +aligned on \fBlong\fP boundary. Similarly, only \fBlong\fP sized values can be +retrieved and set at a time. +.SH "RETURN VALUE" +All but the \fBT_GET\fP* requests return 0 upon successful completion. +Otherwise, a value of -1 is returned and \fIerrno\fP is set to indicate the +error. +.PP +The \fBT_GET\fP* requests return the resulting data. Here, -1 is a legitimate +return value. To distinguish between this and an error, clear \fIerrno\fP +before the \fBptrace\fP call, and check whether it is zero afterwards. +.SH ERRORS +The functions will fail if any of the following errors occur: +.TP 10 +.B EINVAL +Invalid request or signal given. +.TP 10 +.B ESRCH +The given process is not found, exiting, or not traced by the caller. +.TP 10 +.B EBUSY +The given process is not stopped, or already being traced. +.TP 10 +.B EIO +The given address is out of range or not properly aligned. +.TP 10 +.B EPERM +Attaching is denied, because the caller equals the given process, +or the caller is not root and does not match the given process's +user or group ID, or the caller is not root and the given process +is a system process, or the caller is a system process, +or the given process may not be traced at all. +.TP +.SH LIMITATIONS +Signals are not ordered. Attaching to a process guarantees that a \fBSIGSTOP\fP +will arrive at the tracer, but it is not guaranteed that this will be the first +signal to arrive. The same goes for automatically attached children of the +traced process. Similarly, if the tracer wants to detach from a running +process, it will typically send a \fBSIGSTOP\fP using +.BR kill (2) +to the process to stop it, but there is no guarantee that this will be the +first signal to arrive. +.PP +Signals not caused by the process itself (e.g. those caused by +.BR kill (2)) +will arrive at the tracer while the process is in stopped state, but this does +not imply that the process is in a stable state at that point. The process may +still have a system call pending, and this means that registers and memory of +the process may change almost arbitrarily after the tracer has been told about +the arrival of the current signal. Implementers of debuggers are advised to +make minimal assumptions about the conditions of the process when an unexpected +signal arrives. +.PP +It is not possible to use \fBT_SYSCALL\fP to get a trap upon leaving of a +system call, if \fBT_SYSCALL\fP was not used to get a trap upon entering that +system call. This is in fact helpful: after attaching to a process, the first +\fBT_SYSCALL\fP call will always cause a trap after entering the next system +call. As the only exception, \fBT_SYSCALL\fP on a +.BR fork (2) +call of a process with \fBTO_TRACEFORK\fP set, will result in two traps upon +leaving: one for the parent, and one for the child. The child's \fBSIGSTOP\fP +signal will always come before the \fBSIGTRAP\fP from its leaving the system +call. +.PP +There is no way to reliably distinguish between real signals and signals +generated for the tracer. +.PP +For system stability reasons, the PM and VM servers cannot be traced. .SH "SEE ALSO" .BR wait (2), -.BR sigaction (2), -.BR mdb (1). -.SH BUGS -.B Ptrace -is unique and arcane; it should be replaced with a special file that -can be opened and read and written. The control functions could then -be implemented with -.BR ioctl (2) -calls on this file. This would be simpler to understand and have much -higher performance. -.PP -The request PT_TRACE_ME call should be able to specify -signals that are to be treated normally and not cause a stop. -In this way, for example, -programs with simulated floating point (which -use \*(lqillegal instruction\*(rq signals at a very high rate) -could be efficiently debugged. -.PP -The error indication, \-1, is a legitimate function value; -.BR errno , -(see -.BR intro (2)), -can be used to disambiguate. -.PP -It should be possible to stop a process on occurrence of a system -call; -in this way a completely controlled environment could -be provided. +.BR kill (2), +.BR mdb (1) +.SH AUTHOR +Manual page written by David van Moolenbroek diff --git a/servers/inet/mnx_eth.c b/servers/inet/mnx_eth.c index 88d030681..210e59dc8 100644 --- a/servers/inet/mnx_eth.c +++ b/servers/inet/mnx_eth.c @@ -34,10 +34,6 @@ FORWARD _PROTOTYPE( eth_port_t *find_port, (message *m) ); FORWARD _PROTOTYPE( void eth_restart, (eth_port_t *eth_port, int tasknr) ); FORWARD _PROTOTYPE( void send_getstat, (eth_port_t *eth_port) ); -#if 0 -FORWARD _PROTOTYPE( int asynsend, (endpoint_t dst, message *mp) ); -#endif - PUBLIC void osdep_eth_init() { int i, j, r, rport; @@ -1013,57 +1009,6 @@ eth_port_t *eth_port; ip_panic(( "eth_get_stat: asynsend failed: %d", r)); } -#if 0 -PRIVATE asynmsg_t *msgtable= NULL; -PRIVATE size_t msgtable_n= 0; - -PRIVATE int asynsend(dst, mp) -endpoint_t dst; -message *mp; -{ - int i; - unsigned flags; - - if (msgtable == NULL) - { - printf("asynsend: allocating msg table\n"); - msgtable_n= 5; - msgtable= malloc(msgtable_n * sizeof(msgtable[0])); - for (i= 0; i= msgtable_n) - ip_panic(( "asynsend: should resize table" )); - msgtable[i].dst= dst; - msgtable[i].msg= *mp; - msgtable[i].flags= AMF_VALID; /* Has to be last. The kernel - * scans this table while we are - * sleeping. - */ - - /* Tell the kernel to rescan the table */ - return senda(msgtable, msgtable_n); -} -#endif - /* * $PchId: mnx_eth.c,v 1.16 2005/06/28 14:24:37 philip Exp $ */ diff --git a/servers/is/dmp_kernel.c b/servers/is/dmp_kernel.c index 3f01f5112..e5c2cc3dd 100644 --- a/servers/is/dmp_kernel.c +++ b/servers/is/dmp_kernel.c @@ -341,7 +341,7 @@ PUBLIC void privileges_dmp() PRIVATE char *p_rts_flags_str(int flags) { static char str[10]; - str[0] = (flags & NO_PRIORITY) ? 's' : '-'; + str[0] = (flags & PROC_STOP) ? 's' : '-'; str[1] = (flags & SENDING) ? 'S' : '-'; str[2] = (flags & RECEIVING) ? 'R' : '-'; str[3] = (flags & SIGNALED) ? 'I' : '-'; diff --git a/servers/is/dmp_pm.c b/servers/is/dmp_pm.c index a3e57c824..2c8071901 100644 --- a/servers/is/dmp_pm.c +++ b/servers/is/dmp_pm.c @@ -20,20 +20,21 @@ PUBLIC struct mproc mproc[NR_PROCS]; *===========================================================================*/ PRIVATE char *flags_str(int flags) { - static char str[13]; + static char str[14]; str[0] = (flags & WAITING) ? 'W' : '-'; str[1] = (flags & ZOMBIE) ? 'Z' : '-'; str[2] = (flags & PAUSED) ? 'P' : '-'; str[3] = (flags & ALARM_ON) ? 'A' : '-'; - str[4] = (flags & TRACED) ? 'T' : '-'; + str[4] = (flags & EXITING) ? 'E' : '-'; str[5] = (flags & STOPPED) ? 'S' : '-'; str[6] = (flags & SIGSUSPENDED) ? 'U' : '-'; str[7] = (flags & REPLY) ? 'R' : '-'; - str[8] = (flags & PRIV_PROC) ? 'p' : '-'; + str[8] = (flags & FS_CALL) ? 'F' : '-'; str[9] = (flags & PM_SIG_PENDING) ? 's' : '-'; - str[10] = (flags & PARTIAL_EXEC) ? 'x' : '-'; - str[11] = (flags & EXITING) ? 'E' : '-'; - str[12] = '\0'; + str[10] = (flags & PRIV_PROC) ? 'p' : '-'; + str[11] = (flags & PARTIAL_EXEC) ? 'x' : '-'; + str[12] = (flags & DELAY_CALL) ? 'd' : '-'; + str[13] = '\0'; return str; } @@ -48,14 +49,14 @@ PUBLIC void mproc_dmp() getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc); - printf("-process- -nr-prnt- -pid ppid grp- -uid--gid- -nice- -flags------\n"); + printf("-process- -nr-pnr-tnr- --pid--ppid--pgrp- -uid-- -gid-- -nice- -flags-------\n"); for (i=prev_i; imp_pid == 0 && i != PM_PROC_NR) continue; if (++n > 22) break; - printf("%8.8s %4d%4d %5d %5d %5d ", - mp->mp_name, i, mp->mp_parent, mp->mp_pid, mproc[mp->mp_parent].mp_pid, mp->mp_procgrp); - printf("%2d(%2d) %2d(%2d) ", + printf("%8.8s %4d%4d%4d %5d %5d %5d ", + mp->mp_name, i, mp->mp_parent, mp->mp_tracer, mp->mp_pid, mproc[mp->mp_parent].mp_pid, mp->mp_procgrp); + printf("%2d(%2d) %2d(%2d) ", mp->mp_realuid, mp->mp_effuid, mp->mp_realgid, mp->mp_effgid); printf(" %3d %s ", mp->mp_nice, flags_str(mp->mp_flags)); diff --git a/servers/pm/const.h b/servers/pm/const.h index cf75e72c5..2fa9bdad1 100644 --- a/servers/pm/const.h +++ b/servers/pm/const.h @@ -8,6 +8,8 @@ #define PM_PID 0 /* PM's process id number */ #define INIT_PID 1 /* INIT's process id number */ +#define NO_TRACER 0 /* process is not being traced */ + #define DUMPED 0200 /* bit set in status when core dumped */ #define MAX_SECS (((1<<(sizeof(clock_t)*8-1))-1)/system_hz) diff --git a/servers/pm/exec.c b/servers/pm/exec.c index 927d1cfc4..7113eefcb 100644 --- a/servers/pm/exec.c +++ b/servers/pm/exec.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "mproc.h" #include "param.h" @@ -38,23 +39,18 @@ *===========================================================================*/ PUBLIC int do_exec() { + message m; int r; - /* Save parameters */ - mp->mp_exec_path= m_in.exec_name; - mp->mp_exec_path_len= m_in.exec_len; - mp->mp_exec_frame= m_in.stack_ptr; - mp->mp_exec_frame_len= m_in.stack_bytes; - /* Forward call to FS */ - if (mp->mp_fs_call != PM_IDLE) - { - panic(__FILE__, "do_exec: not idle", mp->mp_fs_call); - } - mp->mp_fs_call= PM_EXEC; - r= notify(FS_PROC_NR); - if (r != OK) - panic(__FILE__, "do_exec: unable to notify FS", r); + m.m_type = PM_EXEC; + m.PM_PROC = mp->mp_endpoint; + m.PM_PATH = m_in.exec_name; + m.PM_PATH_LEN = m_in.exec_len; + m.PM_FRAME = m_in.stack_ptr; + m.PM_FRAME_LEN = m_in.stack_bytes; + + tell_fs(mp, &m); /* Do not reply */ return SUSPEND; @@ -92,7 +88,7 @@ PUBLIC int exec_newmem() if((r=vm_exec_newmem(proc_e, &args, sizeof(args), &stack_top, &flags)) == OK) { allow_setuid= 0; /* Do not allow setuid execution */ - if ((rmp->mp_flags & TRACED) == 0) { + if (rmp->mp_tracer == NO_TRACER) { /* Okay, setuid execution is allowed */ allow_setuid= 1; rmp->mp_effuid = args.new_uid; @@ -174,7 +170,7 @@ int result; /* Fix 'mproc' fields, tell kernel that exec is done, reset caught * sigs. */ - for (sn = 1; sn <= _NSIG; sn++) { + for (sn = 1; sn < _NSIG; sn++) { if (sigismember(&rmp->mp_catch, sn)) { sigdelset(&rmp->mp_catch, sn); rmp->mp_sigact[sn].sa_handler = SIG_DFL; @@ -182,13 +178,18 @@ int result; } } + /* Cause a signal if this process is traced. + * Do this before making the process runnable again! + */ + if (rmp->mp_tracer != NO_TRACER) { + sn = (rmp->mp_trace_flags & TO_ALTEXEC) ? SIGSTOP : SIGTRAP; + + check_sig(rmp->mp_pid, sn); + } new_sp= (char *)rmp->mp_procargs; pc= 0; /* for now */ r= sys_exec(rmp->mp_endpoint, new_sp, rmp->mp_name, pc); if (r != OK) panic(__FILE__, "sys_exec failed", r); - - /* Cause a signal if this process is traced. */ - if (rmp->mp_flags & TRACED) check_sig(rmp->mp_pid, SIGTRAP); } diff --git a/servers/pm/forkexit.c b/servers/pm/forkexit.c index 36fe0a36b..30b58fd31 100644 --- a/servers/pm/forkexit.c +++ b/servers/pm/forkexit.c @@ -13,6 +13,7 @@ * exit_proc: actually do the exiting, and tell FS about it * exit_restart: continue exiting a process after FS has replied * do_waitpid: perform the WAITPID or WAIT system call + * wait_test: check whether a parent is waiting for a child */ #include "pm.h" @@ -20,6 +21,7 @@ #include #include #include +#include #include #include #include "mproc.h" @@ -28,7 +30,11 @@ #define LAST_FEW 2 /* last few slots reserved for superuser */ FORWARD _PROTOTYPE (void zombify, (struct mproc *rmp) ); +FORWARD _PROTOTYPE (void check_parent, (struct mproc *child, + int try_cleanup) ); FORWARD _PROTOTYPE (void tell_parent, (struct mproc *child) ); +FORWARD _PROTOTYPE (void tell_tracer, (struct mproc *child) ); +FORWARD _PROTOTYPE (void tracer_died, (struct mproc *child) ); FORWARD _PROTOTYPE (void cleanup, (register struct mproc *rmp) ); /*===========================================================================* @@ -43,6 +49,7 @@ PUBLIC int do_fork() static int next_child; int i, n = 0, r, s; endpoint_t child_ep; + message m; /* If tables might fill up during FORK, don't even start since recovery half * way through is such a nuisance. @@ -79,8 +86,13 @@ PUBLIC int do_fork() procs_in_use++; *rmc = *rmp; /* copy parent's process slot to child's */ rmc->mp_parent = who_p; /* record child's parent */ + if (!(rmc->mp_trace_flags & TO_TRACEFORK)) { + rmc->mp_tracer = NO_TRACER; /* no tracer attached */ + rmc->mp_trace_flags = 0; + sigemptyset(&rmc->mp_sigtrace); + } /* inherit only these flags */ - rmc->mp_flags &= (IN_USE|PRIV_PROC); + rmc->mp_flags &= (IN_USE|PRIV_PROC|DELAY_CALL); rmc->mp_child_utime = 0; /* reset administration */ rmc->mp_child_stime = 0; /* reset administration */ rmc->mp_exitstatus = 0; @@ -93,11 +105,16 @@ PUBLIC int do_fork() new_pid = get_free_pid(); rmc->mp_pid = new_pid; /* assign pid to child */ - if (rmc->mp_fs_call != PM_IDLE) - panic("pm", "do_fork: not idle", rmc->mp_fs_call); - rmc->mp_fs_call= PM_FORK; - r= notify(FS_PROC_NR); - if (r != OK) panic("pm", "do_fork: unable to notify FS", r); + m.m_type = PM_FORK; + m.PM_PROC = rmc->mp_endpoint; + m.PM_PPROC = rmp->mp_endpoint; + m.PM_CPID = rmc->mp_pid; + + tell_fs(rmc, &m); + + /* Tell the tracer, if any, about the new child */ + if (rmc->mp_tracer != NO_TRACER) + sig_proc(rmc, SIGSTOP, TRUE /*trace*/); /* Do not reply until FS is ready to process the fork * request @@ -118,6 +135,7 @@ PUBLIC int do_fork_nb() static int next_child; int i, n = 0, r; endpoint_t child_ep; + message m; /* Only system processes are allowed to use fork_nb */ if (!(mp->mp_flags & PRIV_PROC)) @@ -155,8 +173,13 @@ PUBLIC int do_fork_nb() procs_in_use++; *rmc = *rmp; /* copy parent's process slot to child's */ rmc->mp_parent = who_p; /* record child's parent */ + if (!(rmc->mp_trace_flags & TO_TRACEFORK)) { + rmc->mp_tracer = NO_TRACER; /* no tracer attached */ + rmc->mp_trace_flags = 0; + sigemptyset(&rmc->mp_sigtrace); + } /* inherit only these flags */ - rmc->mp_flags &= (IN_USE|PRIV_PROC); + rmc->mp_flags &= (IN_USE|PRIV_PROC|DELAY_CALL); rmc->mp_child_utime = 0; /* reset administration */ rmc->mp_child_stime = 0; /* reset administration */ rmc->mp_exitstatus = 0; @@ -169,11 +192,16 @@ PUBLIC int do_fork_nb() new_pid = get_free_pid(); rmc->mp_pid = new_pid; /* assign pid to child */ - if (rmc->mp_fs_call != PM_IDLE) - panic("pm", "do_fork: not idle", rmc->mp_fs_call); - rmc->mp_fs_call= PM_FORK_NB; - r= notify(FS_PROC_NR); - if (r != OK) panic("pm", "do_fork: unable to notify FS", r); + m.m_type = PM_FORK_NB; + m.PM_PROC = rmc->mp_endpoint; + m.PM_PPROC = rmp->mp_endpoint; + m.PM_CPID = rmc->mp_pid; + + tell_fs(rmc, &m); + + /* Tell the tracer, if any, about the new child */ + if (rmc->mp_tracer != NO_TRACER) + sig_proc(rmc, SIGSTOP, TRUE /*trace*/); /* Wakeup the newly created process */ setreply(rmc-mproc, OK); @@ -210,6 +238,7 @@ int dump_core; /* flag indicating whether to dump core */ pid_t procgrp; struct mproc *p_mp; clock_t user_time, sys_time; + message m; /* Do not create core files for set uid execution */ if (dump_core && rmp->mp_realuid != rmp->mp_effuid) @@ -244,7 +273,7 @@ int dump_core; /* flag indicating whether to dump core */ * This order is important so that FS can tell drivers to cancel requests * such as copying to/ from the exiting process, before it is gone. */ - sys_nice(proc_nr_e, PRIO_STOP); /* stop the process */ + sys_stop(proc_nr_e); /* stop the process */ if((r=vm_willexit(proc_nr_e)) != OK) { panic(__FILE__, "exit_proc: vm_willexit failed", r); } @@ -255,39 +284,34 @@ int dump_core; /* flag indicating whether to dump core */ printf("PM: INIT died\n"); return; } - else - if(proc_nr_e != FS_PROC_NR) /* if it is not FS that is exiting.. */ + if (proc_nr_e == FS_PROC_NR) { - /* Tell FS about the exiting process. */ - if (rmp->mp_fs_call != PM_IDLE) - panic(__FILE__, "exit_proc: not idle", rmp->mp_fs_call); - rmp->mp_fs_call= dump_core ? PM_DUMPCORE : PM_EXIT; - r= notify(FS_PROC_NR); - if (r != OK) panic(__FILE__, "exit_proc: unable to notify FS", r); - - if (rmp->mp_flags & PRIV_PROC) - { - /* Destroy system processes without waiting for FS. This is - * needed because the system process might be a block device - * driver that FS is blocked waiting on. - */ - if((r= sys_exit(rmp->mp_endpoint)) != OK) - panic(__FILE__, "exit_proc: sys_exit failed", r); - } - } - else - { - panic(__FILE__, "pm_exit: FS died", r); + panic(__FILE__, "exit_proc: FS died", r); } - /* The process is now officially exiting. The ZOMBIE flag is not enough, as - * it is not set here for core dumps - introducing potential race conditions. + + /* Tell FS about the exiting process. */ + m.m_type = dump_core ? PM_DUMPCORE : PM_EXIT; + m.PM_PROC = rmp->mp_endpoint; + + tell_fs(rmp, &m); + + if (rmp->mp_flags & PRIV_PROC) + { + /* Destroy system processes without waiting for FS. This is + * needed because the system process might be a block device + * driver that FS is blocked waiting on. + */ + if((r= sys_exit(rmp->mp_endpoint)) != OK) + panic(__FILE__, "exit_proc: sys_exit failed", r); + } + + /* Clean up most of the flags describing the process's state before the exit, + * and mark it as exiting. */ + rmp->mp_flags &= (IN_USE|FS_CALL|PRIV_PROC|TRACE_EXIT); rmp->mp_flags |= EXITING; - /* Pending reply messages for the dead process cannot be delivered. */ - rmp->mp_flags &= ~REPLY; - /* Keep the process around until FS is finished with it. */ rmp->mp_exitstatus = (char) exit_status; @@ -300,16 +324,18 @@ int dump_core; /* flag indicating whether to dump core */ /* If the process has children, disinherit them. INIT is the new parent. */ for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { - if (rmp->mp_flags & IN_USE && rmp->mp_parent == proc_nr) { + if (!(rmp->mp_flags & IN_USE)) continue; + if (rmp->mp_tracer == proc_nr) { + /* This child's tracer died. Do something sensible. */ + tracer_died(rmp); + } + if (rmp->mp_parent == proc_nr) { /* 'rmp' now points to a child to be disinherited. */ rmp->mp_parent = INIT_PROC_NR; - parent_waiting = mproc[INIT_PROC_NR].mp_flags & WAITING; - if (parent_waiting && (rmp->mp_flags & ZOMBIE)) { - tell_parent(rmp); - if (rmp->mp_fs_call == PM_IDLE) - cleanup(rmp); - } + /* Notify new parent. */ + if (rmp->mp_flags & ZOMBIE) + check_parent(rmp, TRUE /*try_cleanup*/); } } @@ -345,11 +371,11 @@ int dump_core; /* flag indicating whether to dump core */ panic(__FILE__, "exit_restart: vm_exit failed", r); } - if ((rmp->mp_flags & TRACE_EXIT) && rmp->mp_parent != INIT_PROC_NR) + if (rmp->mp_flags & TRACE_EXIT) { - /* Wake up the parent, completing the ptrace(T_EXIT) call */ - mproc[rmp->mp_parent].mp_reply.reply_trace = 0; - setreply(rmp->mp_parent, OK); + /* Wake up the tracer, completing the ptrace(T_EXIT) call */ + mproc[rmp->mp_tracer].mp_reply.reply_trace = 0; + setreply(rmp->mp_tracer, OK); } /* Clean up if the parent has collected the exit status */ @@ -372,7 +398,7 @@ PUBLIC int do_waitpid() * Both WAIT and WAITPID are handled by this code. */ register struct mproc *rp; - int pidarg, options, children; + int i, pidarg, options, children; /* Set internal variables, depending on whether this is WAIT or WAITPID. */ pidarg = (call_nr == WAIT ? -1 : m_in.pid); /* 1st param of waitpid */ @@ -386,26 +412,47 @@ PUBLIC int do_waitpid() */ children = 0; for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) { - if ( (rp->mp_flags & IN_USE) && rp->mp_parent == who_p) { - /* The value of pidarg determines which children qualify. */ - if (pidarg > 0 && pidarg != rp->mp_pid) continue; - if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue; - if (rp->mp_flags & TOLD_PARENT) continue; /* post-ZOMBIE */ + if ((rp->mp_flags & (IN_USE | TOLD_PARENT)) != IN_USE) continue; + if (rp->mp_parent != who_p && rp->mp_tracer != who_p) continue; + if (rp->mp_parent != who_p && (rp->mp_flags & ZOMBIE)) continue; - children++; /* this child is acceptable */ + /* The value of pidarg determines which children qualify. */ + if (pidarg > 0 && pidarg != rp->mp_pid) continue; + if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue; + + children++; /* this child is acceptable */ + + if (rp->mp_tracer == who_p) { + if (rp->mp_flags & TRACE_ZOMBIE) { + /* Traced child meets the pid test and has exited. */ + tell_tracer(rp); + check_parent(rp, TRUE /*try_cleanup*/); + return(SUSPEND); + } + if (rp->mp_flags & STOPPED) { + /* This child meets the pid test and is being traced. + * Deliver a signal to the tracer, if any. + */ + for (i = 1; i < _NSIG; i++) { + if (sigismember(&rp->mp_sigtrace, i)) { + sigdelset(&rp->mp_sigtrace, i); + + mp->mp_reply.reply_res2 = + 0177 | (i << 8); + return(rp->mp_pid); + } + } + } + } + + if (rp->mp_parent == who_p) { if (rp->mp_flags & ZOMBIE) { /* This child meets the pid test and has exited. */ tell_parent(rp); /* this child has already exited */ - if (rp->mp_fs_call == PM_IDLE) + if (!(rp->mp_flags & FS_CALL)) cleanup(rp); return(SUSPEND); } - if ((rp->mp_flags & STOPPED) && rp->mp_sigstatus) { - /* This child meets the pid test and is being traced.*/ - mp->mp_reply.reply_res2 = 0177|(rp->mp_sigstatus << 8); - rp->mp_sigstatus = 0; - return(rp->mp_pid); - } } } @@ -424,35 +471,100 @@ PUBLIC int do_waitpid() } } +/*===========================================================================* + * wait_test * + *===========================================================================*/ +PUBLIC int wait_test(rmp, child) +struct mproc *rmp; /* process that may be waiting */ +struct mproc *child; /* process that may be waited for */ +{ +/* See if a parent or tracer process is waiting for a child process. + * A tracer is considered to be a pseudo-parent. + */ + int parent_waiting, right_child; + pid_t pidarg; + + pidarg = rmp->mp_wpid; /* who's being waited for? */ + parent_waiting = rmp->mp_flags & WAITING; + right_child = /* child meets one of the 3 tests? */ + (pidarg == -1 || pidarg == child->mp_pid || + -pidarg == child->mp_procgrp); + + return (parent_waiting && right_child); +} + /*===========================================================================* * zombify * *===========================================================================*/ PRIVATE void zombify(rmp) struct mproc *rmp; { -/* Zombify a process. If the parent is waiting, notify it immediately. - * Otherwise, send a SIGCHLD signal to the parent. +/* Zombify a process. First check if the exiting process is traced by a process + * other than its parent; if so, the tracer must be notified about the exit + * first. Once that is done, the real parent may be notified about the exit of + * its child. */ - struct mproc *p_mp; - int parent_waiting, right_child; - pid_t pidarg; + struct mproc *t_mp; - if (rmp->mp_flags & ZOMBIE) + if (rmp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)) panic(__FILE__, "zombify: process was already a zombie", NO_NUM); - rmp->mp_flags &= (IN_USE|PRIV_PROC|EXITING|TRACE_EXIT); - rmp->mp_flags |= ZOMBIE; + /* See if we have to notify a tracer process first. */ + if (rmp->mp_tracer != NO_TRACER && rmp->mp_tracer != rmp->mp_parent) { + rmp->mp_flags |= TRACE_ZOMBIE; - p_mp = &mproc[rmp->mp_parent]; - pidarg = p_mp->mp_wpid; /* who's being waited for? */ - parent_waiting = p_mp->mp_flags & WAITING; - right_child = /* child meets one of the 3 tests? */ - (pidarg == -1 || pidarg == rmp->mp_pid || -pidarg == rmp->mp_procgrp); + t_mp = &mproc[rmp->mp_tracer]; - if (parent_waiting && right_child) - tell_parent(rmp); /* tell parent */ - else - sig_proc(p_mp, SIGCHLD); /* send parent a "child died" signal */ + /* Do not bother sending SIGCHLD signals to tracers. */ + if (!wait_test(t_mp, rmp)) + return; + + tell_tracer(rmp); + } + else { + rmp->mp_flags |= ZOMBIE; + } + + /* No tracer, or tracer is parent, or tracer has now been notified. */ + check_parent(rmp, FALSE /*try_cleanup*/); +} + +/*===========================================================================* + * check_parent * + *===========================================================================*/ +PRIVATE void check_parent(child, try_cleanup) +struct mproc *child; /* tells which process is exiting */ +int try_cleanup; /* clean up the child when done? */ +{ +/* We would like to inform the parent of an exiting child about the child's + * death. If the parent is waiting for the child, tell it immediately; + * otherwise, send it a SIGCHLD signal. + * + * Note that we may call this function twice on a single child; first with + * its original parent, later (if the parent died) with INIT as its parent. + */ + struct mproc *p_mp; + + p_mp = &mproc[child->mp_parent]; + + if (p_mp->mp_flags & EXITING) { + /* This may trigger if the child of a dead parent dies. The child will + * be assigned to INIT and rechecked shortly after. Do nothing. + */ + } + else if (wait_test(p_mp, child)) { + tell_parent(child); + + /* The 'try_cleanup' flag merely saves us from having to be really + * careful with statement ordering in exit_proc() and exit_restart(). + */ + if (try_cleanup && !(child->mp_flags & FS_CALL)) + cleanup(child); + } + else { + /* Parent is not waiting. */ + sig_proc(p_mp, SIGCHLD, TRUE /*trace*/); + } } /*===========================================================================* @@ -482,6 +594,64 @@ register struct mproc *child; /* tells which process is exiting */ child->mp_flags |= TOLD_PARENT; /* avoid informing parent twice */ } +/*===========================================================================* + * tell_tracer * + *===========================================================================*/ +PRIVATE void tell_tracer(child) +struct mproc *child; /* tells which process is exiting */ +{ + int exitstatus, mp_tracer; + struct mproc *tracer; + + mp_tracer = child->mp_tracer; + if (mp_tracer <= 0) + panic(__FILE__, "tell_tracer: bad value in mp_tracer", mp_tracer); + if(!(child->mp_flags & TRACE_ZOMBIE)) + panic(__FILE__, "tell_tracer: child not a zombie", NO_NUM); + tracer = &mproc[mp_tracer]; + + exitstatus = (child->mp_exitstatus << 8) | (child->mp_sigstatus & 0377); + tracer->mp_reply.reply_res2 = exitstatus; + setreply(child->mp_tracer, child->mp_pid); + tracer->mp_flags &= ~WAITING; /* tracer no longer waiting */ + child->mp_flags &= ~TRACE_ZOMBIE; /* child no longer zombie to tracer */ + child->mp_flags |= ZOMBIE; /* child is now zombie to parent */ +} + +/*===========================================================================* + * tracer_died * + *===========================================================================*/ +PRIVATE void tracer_died(child) +struct mproc *child; /* process being traced */ +{ +/* The process that was tracing the given child, has died for some reason. + * This is really the tracer's fault, but we can't let INIT deal with this. + */ + + child->mp_tracer = NO_TRACER; + child->mp_flags &= ~TRACE_EXIT; + + /* If the tracer died while the child was running or stopped, we have no + * idea what state the child is in. Avoid a trainwreck, by killing the child. + * Note that this may cause cascading exits. + */ + if (!(child->mp_flags & EXITING)) { + sig_proc(child, SIGKILL, TRUE /*trace*/); + + return; + } + + /* If the tracer died while the child was telling it about its own death, + * forget about the tracer and notify the real parent instead. + */ + if (child->mp_flags & TRACE_ZOMBIE) { + child->mp_flags &= ~TRACE_ZOMBIE; + child->mp_flags |= ZOMBIE; + + check_parent(child, TRUE /*try_cleanup*/); + } +} + /*===========================================================================* * cleanup * *===========================================================================*/ diff --git a/servers/pm/getset.c b/servers/pm/getset.c index 7b26a2482..dce214dea 100644 --- a/servers/pm/getset.c +++ b/servers/pm/getset.c @@ -1,4 +1,4 @@ -/* This file handles the 4 system calls that get and set uids and gids. +/* This file handles the 6 system calls that get and set uids and gids. * It also handles getpid(), setsid(), and getpgrp(). The code for each * one is so tiny that it hardly seemed worthwhile to make each a separate * function. @@ -13,14 +13,11 @@ #include "param.h" /*===========================================================================* - * do_getset * + * do_get * *===========================================================================*/ -PUBLIC int do_getset() +PUBLIC int do_get() { -/* Handle GETUID, GETGID, GETPID, GETPGRP, SETUID, SETGID, SETSID. The four - * GETs and SETSID return their primary results in 'r'. GETUID, GETGID, and - * GETPID also return secondary results (the effective IDs, or the parent - * process ID) in 'reply_res2', which is returned to the user. +/* Handle GETUID, GETGID, GETPID, GETPGRP. */ register struct mproc *rmp = mp; @@ -48,74 +45,6 @@ PUBLIC int do_getset() rmp->mp_reply.reply_res3 = mproc[proc].mp_pid; break; - case SETEUID: - case SETUID: - if (rmp->mp_realuid != (uid_t) m_in.usr_id && - rmp->mp_effuid != SUPER_USER) - return(EPERM); - if(call_nr == SETUID) rmp->mp_realuid = (uid_t) m_in.usr_id; - rmp->mp_effuid = (uid_t) m_in.usr_id; - - if (rmp->mp_fs_call != PM_IDLE) - { - panic(__FILE__, "do_getset: not idle", - rmp->mp_fs_call); - } - rmp->mp_fs_call= PM_SETUID; - r= notify(FS_PROC_NR); - if (r != OK) - panic(__FILE__, "do_getset: unable to notify FS", r); - - /* Do not reply until FS is ready to process the setuid - * request - */ - r= SUSPEND; - break; - - case SETEGID: - case SETGID: - if (rmp->mp_realgid != (gid_t) m_in.grp_id && - rmp->mp_effuid != SUPER_USER) - return(EPERM); - if(call_nr == SETGID) rmp->mp_realgid = (gid_t) m_in.grp_id; - rmp->mp_effgid = (gid_t) m_in.grp_id; - - if (rmp->mp_fs_call != PM_IDLE) - { - panic(__FILE__, "do_getset: not idle", - rmp->mp_fs_call); - } - rmp->mp_fs_call= PM_SETGID; - r= notify(FS_PROC_NR); - if (r != OK) - panic(__FILE__, "do_getset: unable to notify FS", r); - - /* Do not reply until FS is ready to process the setgid - * request - */ - r= SUSPEND; - break; - - case SETSID: - if (rmp->mp_procgrp == rmp->mp_pid) return(EPERM); - rmp->mp_procgrp = rmp->mp_pid; - - if (rmp->mp_fs_call != PM_IDLE) - { - panic(__FILE__, "do_getset: not idle", - rmp->mp_fs_call); - } - rmp->mp_fs_call= PM_SETSID; - r= notify(FS_PROC_NR); - if (r != OK) - panic(__FILE__, "do_getset: unable to notify FS", r); - - /* Do not reply until FS is ready to process the setsid - * request - */ - r= SUSPEND; - break; - case GETPGRP: r = rmp->mp_procgrp; break; @@ -126,3 +55,66 @@ PUBLIC int do_getset() } return(r); } + +/*===========================================================================* + * do_set * + *===========================================================================*/ +PUBLIC int do_set() +{ +/* Handle SETUID, SETEUID, SETGID, SETEGID, SETSID. These calls have in common + * that, if successful, they will be forwarded to VFS as well. + */ + register struct mproc *rmp = mp; + message m; + int r; + + switch(call_nr) { + case SETUID: + case SETEUID: + if (rmp->mp_realuid != (uid_t) m_in.usr_id && + rmp->mp_effuid != SUPER_USER) + return(EPERM); + if(call_nr == SETUID) rmp->mp_realuid = (uid_t) m_in.usr_id; + rmp->mp_effuid = (uid_t) m_in.usr_id; + + m.m_type = PM_SETUID; + m.PM_PROC = rmp->mp_endpoint; + m.PM_EID = rmp->mp_effuid; + m.PM_RID = rmp->mp_realuid; + + break; + + case SETGID: + case SETEGID: + if (rmp->mp_realgid != (gid_t) m_in.grp_id && + rmp->mp_effuid != SUPER_USER) + return(EPERM); + if(call_nr == SETGID) rmp->mp_realgid = (gid_t) m_in.grp_id; + rmp->mp_effgid = (gid_t) m_in.grp_id; + + m.m_type = PM_SETGID; + m.PM_PROC = rmp->mp_endpoint; + m.PM_EID = rmp->mp_effgid; + m.PM_RID = rmp->mp_realgid; + + break; + + case SETSID: + if (rmp->mp_procgrp == rmp->mp_pid) return(EPERM); + rmp->mp_procgrp = rmp->mp_pid; + + m.m_type = PM_SETSID; + m.PM_PROC = rmp->mp_endpoint; + + break; + + default: + return(EINVAL); + } + + /* Send the request to FS */ + tell_fs(rmp, &m); + + /* Do not reply until FS has processed the request */ + return(SUSPEND); +} diff --git a/servers/pm/glo.h b/servers/pm/glo.h index b9adae444..ed4f3d326 100644 --- a/servers/pm/glo.h +++ b/servers/pm/glo.h @@ -27,8 +27,5 @@ EXTERN time_t boottime; /* time when the system was booted (for * reporting to FS) */ EXTERN u32_t system_hz; /* System clock frequency. */ -EXTERN int report_reboot; /* During reboot to report to FS that we are - * rebooting. - */ EXTERN int abort_flag; EXTERN char monitor_code[256]; diff --git a/servers/pm/main.c b/servers/pm/main.c index 80de21707..ce6fd9058 100644 --- a/servers/pm/main.c +++ b/servers/pm/main.c @@ -43,9 +43,7 @@ EXTERN unsigned long calls_stats[NCALLS]; FORWARD _PROTOTYPE( void get_work, (void) ); FORWARD _PROTOTYPE( void pm_init, (void) ); FORWARD _PROTOTYPE( int get_nice_value, (int queue) ); -FORWARD _PROTOTYPE( void send_work, (void) ); -FORWARD _PROTOTYPE( void handle_fs_reply, (message *m_ptr) ); -FORWARD _PROTOTYPE( void restart_sigs, (struct mproc *rmp) ); +FORWARD _PROTOTYPE( void handle_fs_reply, (void) ); #define click_to_round_k(n) \ ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024)) @@ -66,6 +64,10 @@ PUBLIC int main() while (TRUE) { get_work(); /* wait for an PM system call */ + /* Drop delayed calls from exiting processes. */ + if (mp->mp_flags & EXITING) + continue; + /* Check for system notifications first. Special cases. */ if (is_notify(call_nr)) { switch(who_p) { @@ -90,22 +92,19 @@ PUBLIC int main() switch(call_nr) { - case PM_GET_WORK: - if (who_e == FS_PROC_NR) - { - send_work(); - result= SUSPEND; /* don't reply */ - } - else - result= ENOSYS; - break; - case PM_EXIT_REPLY: - case PM_REBOOT_REPLY: + case PM_SETUID_REPLY: + case PM_SETGID_REPLY: + case PM_SETSID_REPLY: case PM_EXEC_REPLY: + case PM_EXIT_REPLY: case PM_CORE_REPLY: + case PM_FORK_REPLY: + case PM_FORK_NB_REPLY: + case PM_UNPAUSE_REPLY: + case PM_REBOOT_REPLY: if (who_e == FS_PROC_NR) { - handle_fs_reply(&m_in); + handle_fs_reply(); result= SUSPEND; /* don't reply */ } else @@ -255,9 +254,6 @@ PRIVATE void pm_init() /* Initialize process table, including timers. */ for (rmp=&mproc[0]; rmp<&mproc[NR_PROCS]; rmp++) { tmr_inittimer(&rmp->mp_timer); - - rmp->mp_fs_call= PM_IDLE; - rmp->mp_fs_call2= PM_IDLE; } /* Build the set of signals which cause core dumps, and the set of signals @@ -404,299 +400,109 @@ void checkme(char *str, int line) } } -/*===========================================================================* - * send_work * - *===========================================================================*/ -PRIVATE void send_work() -{ - int r, call; - struct mproc *rmp; - message m; - - m.m_type= PM_IDLE; - for (rmp= mproc; rmp < &mproc[NR_PROCS]; rmp++) - { - call= rmp->mp_fs_call; - if (call == PM_IDLE) - call= rmp->mp_fs_call2; - if (call == PM_IDLE) - continue; - switch(call) - { - case PM_SETSID: - m.m_type= call; - m.PM_SETSID_PROC= rmp->mp_endpoint; - - /* FS does not reply */ - rmp->mp_fs_call= PM_IDLE; - - /* Wakeup the original caller */ - setreply(rmp-mproc, rmp->mp_procgrp); - break; - - case PM_SETGID: - m.m_type= call; - m.PM_SETGID_PROC= rmp->mp_endpoint; - m.PM_SETGID_EGID= rmp->mp_effgid; - m.PM_SETGID_RGID= rmp->mp_realgid; - - /* FS does not reply */ - rmp->mp_fs_call= PM_IDLE; - - /* Wakeup the original caller */ - setreply(rmp-mproc, OK); - break; - - case PM_SETUID: - m.m_type= call; - m.PM_SETUID_PROC= rmp->mp_endpoint; - m.PM_SETUID_EGID= rmp->mp_effuid; - m.PM_SETUID_RGID= rmp->mp_realuid; - - /* FS does not reply */ - rmp->mp_fs_call= PM_IDLE; - - /* Wakeup the original caller */ - setreply(rmp-mproc, OK); - break; - - case PM_FORK: - { - int parent_p; - struct mproc *parent_mp; - - parent_p = rmp->mp_parent; - parent_mp = &mproc[parent_p]; - - m.m_type= call; - m.PM_FORK_PPROC= parent_mp->mp_endpoint; - m.PM_FORK_CPROC= rmp->mp_endpoint; - m.PM_FORK_CPID= rmp->mp_pid; - - /* FS does not reply */ - rmp->mp_fs_call= PM_IDLE; - - /* Wakeup the newly created process */ - setreply(rmp-mproc, OK); - - /* Wakeup the parent */ - setreply(parent_mp-mproc, rmp->mp_pid); - break; - } - - case PM_EXIT: - m.m_type= call; - m.PM_EXIT_PROC= rmp->mp_endpoint; - - /* Mark the process as busy */ - rmp->mp_fs_call= PM_BUSY; - - break; - - case PM_UNPAUSE: - m.m_type= call; - m.PM_UNPAUSE_PROC= rmp->mp_endpoint; - - /* FS does not reply */ - rmp->mp_fs_call2= PM_IDLE; - - /* Ask the kernel to deliver the signal */ - r= sys_sigsend(rmp->mp_endpoint, - &rmp->mp_sigmsg); - if (r != OK) { -#if 0 - panic(__FILE__,"sys_sigsend failed",r); -#else - printf("PM: PM_UNPAUSE: sys_sigsend failed to %d: %d\n", - rmp->mp_endpoint, r); -#endif - } - - break; - - case PM_UNPAUSE_TR: - m.m_type= call; - m.PM_UNPAUSE_PROC= rmp->mp_endpoint; - - /* FS does not reply */ - rmp->mp_fs_call= PM_IDLE; - - break; - - case PM_EXEC: - m.m_type= call; - m.PM_EXEC_PROC= rmp->mp_endpoint; - m.PM_EXEC_PATH= rmp->mp_exec_path; - m.PM_EXEC_PATH_LEN= rmp->mp_exec_path_len; - m.PM_EXEC_FRAME= rmp->mp_exec_frame; - m.PM_EXEC_FRAME_LEN= rmp->mp_exec_frame_len; - - /* Mark the process as busy */ - rmp->mp_fs_call= PM_BUSY; - - break; - - case PM_FORK_NB: - { - int parent_p; - struct mproc *parent_mp; - - parent_p = rmp->mp_parent; - parent_mp = &mproc[parent_p]; - - m.m_type= PM_FORK; - m.PM_FORK_PPROC= parent_mp->mp_endpoint; - m.PM_FORK_CPROC= rmp->mp_endpoint; - m.PM_FORK_CPID= rmp->mp_pid; - - /* FS does not reply */ - rmp->mp_fs_call= PM_IDLE; - - break; - } - - case PM_DUMPCORE: - m.m_type= call; - m.PM_CORE_PROC= rmp->mp_endpoint; - /* XXX - m.PM_CORE_SEGPTR= (char *)rmp->mp_seg; - */ - - /* Mark the process as busy */ - rmp->mp_fs_call= PM_BUSY; - - break; - - default: - printf("send_work: should report call 0x%x to FS\n", - call); - break; - } - break; - } - if (m.m_type != PM_IDLE) - { - restart_sigs(rmp); - } - else if (report_reboot) - { - m.m_type= PM_REBOOT; - report_reboot= FALSE; - } - r= send(FS_PROC_NR, &m); - if (r != OK) panic("pm", "send_work: send failed", r); -} - /*===========================================================================* * handle_fs_reply * *===========================================================================*/ -PRIVATE void handle_fs_reply(m_ptr) -message *m_ptr; +PRIVATE void handle_fs_reply() { - int r, proc_e, proc_n, s; - struct mproc *rmp; + struct mproc *rmp; + endpoint_t proc_e; + int r, proc_n; - switch(m_ptr->m_type) - { - case PM_EXIT_REPLY: - proc_e= m_ptr->PM_EXIT_PROC; - if (pm_isokendpt(proc_e, &proc_n) != OK) - { - panic(__FILE__, - "PM_EXIT_REPLY: got bad endpoint from FS", - proc_e); - } - rmp= &mproc[proc_n]; + /* PM_REBOOT is the only request not associated with a process. + * Handle its reply first. + */ + if (call_nr == PM_REBOOT_REPLY) { + vir_bytes code_addr; + size_t code_size; - /* Call is finished */ - rmp->mp_fs_call= PM_IDLE; + /* Ask the kernel to abort. All system services, including + * the PM, will get a HARD_STOP notification. Await the + * notification in the main loop. + */ + code_addr = (vir_bytes) monitor_code; + code_size = strlen(monitor_code) + 1; + sys_abort(abort_flag, PM_PROC_NR, code_addr, code_size); - exit_restart(rmp, FALSE /*dump_core*/); + return; + } - break; + /* Get the process associated with this call */ + proc_e = m_in.PM_PROC; - case PM_REBOOT_REPLY: - { - vir_bytes code_addr; - size_t code_size; + if (pm_isokendpt(proc_e, &proc_n) != OK) { + panic(__FILE__, "handle_fs_reply: got bad endpoint from FS", proc_e); + } - /* Ask the kernel to abort. All system services, including - * the PM, will get a HARD_STOP notification. Await the - * notification in the main loop. - */ - code_addr = (vir_bytes) monitor_code; - code_size = strlen(monitor_code) + 1; - sys_abort(abort_flag, PM_PROC_NR, code_addr, code_size); - break; - } + rmp = &mproc[proc_n]; - case PM_EXEC_REPLY: - proc_e= m_ptr->PM_EXEC_PROC; - if (pm_isokendpt(proc_e, &proc_n) != OK) - { - panic(__FILE__, - "PM_EXIT_REPLY: got bad endpoint from FS", - proc_e); - } - rmp= &mproc[proc_n]; + /* Now that FS replied, mark the process as FS-idle again */ + if (!(rmp->mp_flags & FS_CALL)) + panic(__FILE__, "handle_fs_reply: reply without request", call_nr); - /* Call is finished */ - rmp->mp_fs_call= PM_IDLE; + rmp->mp_flags &= ~FS_CALL; - exec_restart(rmp, m_ptr->PM_EXEC_STATUS); + if (rmp->mp_flags & UNPAUSED) + panic(__FILE__, "handle_fs_reply: UNPAUSED set on entry", call_nr); - restart_sigs(rmp); + /* Call-specific handler code */ + switch (call_nr) { + case PM_SETUID_REPLY: + case PM_SETGID_REPLY: + /* Wake up the original caller */ + setreply(rmp-mproc, OK); - break; + break; - case PM_CORE_REPLY: - { - proc_e= m_ptr->PM_CORE_PROC; - if (pm_isokendpt(proc_e, &proc_n) != OK) - { - panic(__FILE__, - "PM_EXIT_REPLY: got bad endpoint from FS", - proc_e); - } - rmp= &mproc[proc_n]; + case PM_SETSID_REPLY: + /* Wake up the original caller */ + setreply(rmp-mproc, rmp->mp_procgrp); - if (m_ptr->PM_CORE_STATUS == OK) - rmp->mp_sigstatus |= DUMPED; + break; - /* Call is finished */ - rmp->mp_fs_call= PM_IDLE; + case PM_EXEC_REPLY: + exec_restart(rmp, m_in.PM_STATUS); - exit_restart(rmp, TRUE /*dump_core*/); + break; - break; - } - default: - panic(__FILE__, "handle_fs_reply: unknown reply type", - m_ptr->m_type); - break; - } + case PM_EXIT_REPLY: + exit_restart(rmp, FALSE /*dump_core*/); -} - -/*===========================================================================* - * restart_sigs * - *===========================================================================*/ -PRIVATE void restart_sigs(rmp) -struct mproc *rmp; -{ - - if (rmp->mp_fs_call != PM_IDLE || rmp->mp_fs_call2 != PM_IDLE) - return; - - if (rmp->mp_flags & TRACE_EXIT) { - exit_proc(rmp, rmp->mp_exitstatus, FALSE /*dump_core*/); - } - else if (rmp->mp_flags & PM_SIG_PENDING) { - rmp->mp_flags &= ~PM_SIG_PENDING; - check_pending(rmp); - if (!(rmp->mp_flags & PM_SIG_PENDING)) { - /* Allow the process to be scheduled */ - sys_nice(rmp->mp_endpoint, rmp->mp_nice); - } - } + break; + + case PM_CORE_REPLY: + if (m_in.PM_STATUS == OK) + rmp->mp_sigstatus |= DUMPED; + + exit_restart(rmp, TRUE /*dump_core*/); + + break; + + case PM_FORK_REPLY: + /* Wake up the newly created process */ + setreply(proc_n, OK); + + /* Wake up the parent */ + setreply(rmp->mp_parent, rmp->mp_pid); + + break; + + case PM_FORK_NB_REPLY: + /* Nothing to do */ + + break; + + case PM_UNPAUSE_REPLY: + /* Process is now unpaused */ + rmp->mp_flags |= UNPAUSED; + + break; + + default: + panic(__FILE__, "handle_fs_reply: unknown reply code", call_nr); + } + + /* Now that the process is idle again, look at pending signals */ + if ((rmp->mp_flags & (IN_USE | EXITING)) == IN_USE) + restart_sigs(rmp); } diff --git a/servers/pm/misc.c b/servers/pm/misc.c index fd752f242..98f9cc42e 100644 --- a/servers/pm/misc.c +++ b/servers/pm/misc.c @@ -117,13 +117,6 @@ PUBLIC int do_procstat() */ /* This call should be removed, or made more general. */ - if (mp->mp_effuid != 0) - { - printf("PM: unauthorized call of do_procstat by proc %d\n", - mp->mp_endpoint); - sys_sysctl_stacktrace(mp->mp_endpoint); - return EPERM; - } if (m_in.stat_nr == SELF) { mp->mp_reply.sig_set = mp->mp_sigpending; @@ -323,14 +316,12 @@ PUBLIC int do_getprocnr() #endif if (m_in.pid >= 0) { /* lookup process by pid */ - for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { - if ((rmp->mp_flags & IN_USE) && (rmp->mp_pid==m_in.pid)) { - mp->mp_reply.PM_ENDPT = rmp->mp_endpoint; + if ((rmp = find_proc(m_in.pid)) != NIL_MPROC) { + mp->mp_reply.PM_ENDPT = rmp->mp_endpoint; #if 0 - printf("PM: pid result: %d\n", rmp->mp_endpoint); + printf("PM: pid result: %d\n", rmp->mp_endpoint); #endif - return(OK); - } + return(OK); } return(ESRCH); } else if (m_in.namelen > 0) { /* lookup process by name */ @@ -393,6 +384,7 @@ PUBLIC int do_getpuid() *===========================================================================*/ PUBLIC int do_reboot() { + message m; int r; /* Check permission to abort the system. */ @@ -419,12 +411,13 @@ PUBLIC int do_reboot() */ check_sig(-1, SIGKILL); /* kill all users except init */ - sys_nice(INIT_PROC_NR, PRIO_STOP); /* stop init, but keep it around */ + sys_stop(INIT_PROC_NR); /* stop init, but keep it around */ + + /* Tell FS to reboot */ + m.m_type = PM_REBOOT; + + tell_fs(&mproc[FS_PROC_NR], &m); - report_reboot= 1; - r= notify(FS_PROC_NR); - if (r != OK) panic("pm", "do_reboot: unable to notify FS", r); - return(SUSPEND); /* don't reply to caller */ } @@ -433,8 +426,7 @@ PUBLIC int do_reboot() *===========================================================================*/ PUBLIC int do_getsetpriority() { - int arg_which, arg_who, arg_pri; - int rmp_nr; + int r, arg_which, arg_who, arg_pri; struct mproc *rmp; arg_which = m_in.m1_i1; @@ -448,13 +440,11 @@ PUBLIC int do_getsetpriority() return(EINVAL); if (arg_who == 0) - rmp_nr = who_p; + rmp = mp; else - if ((rmp_nr = proc_from_pid(arg_who)) < 0) + if ((rmp = find_proc(arg_who)) == NIL_MPROC) return(ESRCH); - rmp = &mproc[rmp_nr]; - if (mp->mp_effuid != SUPER_USER && mp->mp_effuid != rmp->mp_effuid && mp->mp_effuid != rmp->mp_realuid) return EPERM; @@ -468,9 +458,12 @@ PUBLIC int do_getsetpriority() if (rmp->mp_nice > arg_pri && mp->mp_effuid != SUPER_USER) return(EACCES); - /* We're SET, and it's allowed. Do it and tell kernel. */ + /* We're SET, and it's allowed. */ + if ((r = sys_nice(rmp->mp_endpoint, arg_pri)) != OK) + return(r); + rmp->mp_nice = arg_pri; - return sys_nice(rmp->mp_endpoint, arg_pri); + return(OK); } /*===========================================================================* diff --git a/servers/pm/mproc.h b/servers/pm/mproc.h index c50b3764d..9b6e9b48e 100644 --- a/servers/pm/mproc.h +++ b/servers/pm/mproc.h @@ -7,6 +7,7 @@ #include #include +/* Needs to be included here, for 'ps' etc */ #include "const.h" EXTERN struct mproc { @@ -17,6 +18,7 @@ EXTERN struct mproc { pid_t mp_procgrp; /* pid of process group (used for signals) */ pid_t mp_wpid; /* pid this process is waiting for */ int mp_parent; /* index of parent process */ + int mp_tracer; /* index of tracer process, or NO_TRACER */ /* Child user and system times. Accounting done on child exit. */ clock_t mp_child_utime; /* cumulative user time of children */ @@ -35,26 +37,17 @@ EXTERN struct mproc { sigset_t mp_sigmask; /* signals to be blocked */ sigset_t mp_sigmask2; /* saved copy of mp_sigmask */ sigset_t mp_sigpending; /* pending signals to be handled */ - struct sigaction mp_sigact[_NSIG + 1]; /* as in sigaction(2) */ + sigset_t mp_sigtrace; /* signals to hand to tracer first */ + struct sigaction mp_sigact[_NSIG]; /* as in sigaction(2) */ vir_bytes mp_sigreturn; /* address of C library __sigreturn function */ - struct sigmsg mp_sigmsg; /* Save the details of the signal until the - * PM_UNPAUSE request is delivered. - */ struct timer mp_timer; /* watchdog timer for alarm(2), setitimer(2) */ clock_t mp_interval[NR_ITIMERS]; /* setitimer(2) repetition intervals */ unsigned mp_flags; /* flag bits */ + unsigned mp_trace_flags; /* trace options */ vir_bytes mp_procargs; /* ptr to proc's initial stack arguments */ message mp_reply; /* reply message to be sent to one */ - /* Communication with FS */ - int mp_fs_call; /* Record the call for normal system calls */ - int mp_fs_call2; /* Record the call for signals */ - char *mp_exec_path; /* Path of executable */ - vir_bytes mp_exec_path_len; /* Length of path (including nul) */ - char *mp_exec_frame; /* Arguments */ - vir_bytes mp_exec_frame_len; /* Length of arguments */ - /* Scheduling priority. */ signed int mp_nice; /* nice is PRIO_MIN..PRIO_MAX, standard 0. */ @@ -62,21 +55,24 @@ EXTERN struct mproc { } mproc[NR_PROCS]; /* Flag values */ -#define IN_USE 0x001 /* set when 'mproc' slot in use */ -#define WAITING 0x002 /* set by WAIT system call */ -#define ZOMBIE 0x004 /* waiting for parent to issue WAIT call */ -#define PAUSED 0x008 /* set by PAUSE system call */ -#define ALARM_ON 0x010 /* set when SIGALRM timer started */ -#define TRACED 0x040 /* set if process is to be traced */ -#define STOPPED 0x080 /* set if process stopped for tracing */ -#define SIGSUSPENDED 0x100 /* set by SIGSUSPEND system call */ -#define REPLY 0x200 /* set if a reply message is pending */ -#define PRIV_PROC 0x2000 /* system process, special privileges */ -#define PM_SIG_PENDING 0x4000 /* process got a signal while waiting for FS */ -#define PARTIAL_EXEC 0x8000 /* Process got a new map but no content */ -#define TOLD_PARENT 0x10000 /* Parent wait() completed, ZOMBIE off */ -#define EXITING 0x20000 /* set by EXIT, process is now exiting */ -#define TRACE_EXIT 0x40000 /* tracer is forcing this process to exit */ +#define IN_USE 0x00001 /* set when 'mproc' slot in use */ +#define WAITING 0x00002 /* set by WAIT system call */ +#define ZOMBIE 0x00004 /* waiting for parent to issue WAIT call */ +#define PAUSED 0x00008 /* set by PAUSE system call */ +#define ALARM_ON 0x00010 /* set when SIGALRM timer started */ +#define EXITING 0x00020 /* set by EXIT, process is now exiting */ +#define TOLD_PARENT 0x00040 /* parent wait() completed, ZOMBIE off */ +#define STOPPED 0x00080 /* set if process stopped for tracing */ +#define SIGSUSPENDED 0x00100 /* set by SIGSUSPEND system call */ +#define REPLY 0x00200 /* set if a reply message is pending */ +#define FS_CALL 0x00400 /* set if waiting for FS (normal calls) */ +#define PM_SIG_PENDING 0x00800 /* process got a signal while waiting for FS */ +#define UNPAUSED 0x01000 /* process is not in a blocking call */ +#define PRIV_PROC 0x02000 /* system process, special privileges */ +#define PARTIAL_EXEC 0x04000 /* process got a new map but no content */ +#define TRACE_EXIT 0x08000 /* tracer is forcing this process to exit */ +#define TRACE_ZOMBIE 0x10000 /* waiting for tracer to issue WAIT call */ +#define DELAY_CALL 0x20000 /* waiting for call before sending signal */ #define NIL_MPROC ((struct mproc *) 0) diff --git a/servers/pm/proto.h b/servers/pm/proto.h index b70f4837d..836bdbde5 100644 --- a/servers/pm/proto.h +++ b/servers/pm/proto.h @@ -16,19 +16,10 @@ _PROTOTYPE( void check_vtimer, (int proc_nr, int sig) ); /* break.c */ _PROTOTYPE( int do_brk, (void) ); -/* devio.c */ -_PROTOTYPE( int do_dev_io, (void) ); -_PROTOTYPE( int do_dev_io, (void) ); - /* dma.c */ _PROTOTYPE( int do_adddma, (void) ); _PROTOTYPE( int do_deldma, (void) ); _PROTOTYPE( int do_getdma, (void) ); -_PROTOTYPE( void release_dma, (endpoint_t proc_e, phys_clicks base, - phys_clicks size) ); - -/* dmp.c */ -_PROTOTYPE( int do_fkey_pressed, (void) ); /* exec.c */ _PROTOTYPE( int do_exec, (void) ); @@ -40,19 +31,19 @@ _PROTOTYPE( void exec_restart, (struct mproc *rmp, int result) ); _PROTOTYPE( int do_fork, (void) ); _PROTOTYPE( int do_fork_nb, (void) ); _PROTOTYPE( int do_exit, (void) ); -_PROTOTYPE( int do_waitpid, (void) ); _PROTOTYPE( void exit_proc, (struct mproc *rmp, int exit_status, int dump_core) ); _PROTOTYPE( void exit_restart, (struct mproc *rmp, int dump_core) ); +_PROTOTYPE( int do_waitpid, (void) ); +_PROTOTYPE( int wait_test, (struct mproc *rmp, struct mproc *child) ); /* getset.c */ -_PROTOTYPE( int do_getset, (void) ); - -/* kputc.c */ -_PROTOTYPE( void diag_repl, (void) ); +_PROTOTYPE( int do_get, (void) ); +_PROTOTYPE( int do_set, (void) ); /* main.c */ _PROTOTYPE( int main, (void) ); +_PROTOTYPE( void setreply, (int proc_nr, int result) ); /* misc.c */ _PROTOTYPE( int do_reboot, (void) ); @@ -67,31 +58,24 @@ _PROTOTYPE( int do_allocmem, (void) ); _PROTOTYPE( int do_freemem, (void) ); _PROTOTYPE( int do_getsetpriority, (void) ); - -#if (MACHINE == MACINTOSH) -_PROTOTYPE( phys_clicks start_click, (void) ); -#endif - -_PROTOTYPE( void setreply, (int proc_nr, int result) ); - /* profile.c */ -_PROTOTYPE( int do_sprofile, (void) ); -_PROTOTYPE( int do_cprofile, (void) ); +_PROTOTYPE( int do_sprofile, (void) ); +_PROTOTYPE( int do_cprofile, (void) ); /* signal.c */ _PROTOTYPE( int do_kill, (void) ); _PROTOTYPE( int ksig_pending, (void) ); _PROTOTYPE( int do_pause, (void) ); _PROTOTYPE( int check_sig, (pid_t proc_id, int signo) ); -_PROTOTYPE( void sig_proc, (struct mproc *rmp, int sig_nr) ); +_PROTOTYPE( void sig_proc, (struct mproc *rmp, int signo, int trace) ); _PROTOTYPE( int do_sigaction, (void) ); _PROTOTYPE( int do_sigpending, (void) ); _PROTOTYPE( int do_sigprocmask, (void) ); _PROTOTYPE( int do_sigreturn, (void) ); _PROTOTYPE( int do_sigsuspend, (void) ); _PROTOTYPE( void check_pending, (struct mproc *rmp) ); -_PROTOTYPE( int, vm_notify_sig_wrapper(endpoint_t ep) ); - +_PROTOTYPE( void restart_sigs, (struct mproc *rmp) ); +_PROTOTYPE( void vm_notify_sig_wrapper, (endpoint_t ep) ); /* time.c */ _PROTOTYPE( int do_stime, (void) ); @@ -101,9 +85,9 @@ _PROTOTYPE( int do_gettimeofday, (void) ); /* timers.c */ _PROTOTYPE( void pm_set_timer, (timer_t *tp, int delta, - tmr_func_t watchdog, int arg)); -_PROTOTYPE( void pm_expire_timers, (clock_t now)); -_PROTOTYPE( void pm_cancel_timer, (timer_t *tp)); + tmr_func_t watchdog, int arg) ); +_PROTOTYPE( void pm_expire_timers, (clock_t now) ); +_PROTOTYPE( void pm_cancel_timer, (timer_t *tp) ); /* trace.c */ _PROTOTYPE( int do_trace, (void) ); @@ -112,8 +96,7 @@ _PROTOTYPE( void stop_proc, (struct mproc *rmp, int sig_nr) ); /* utility.c */ _PROTOTYPE( pid_t get_free_pid, (void) ); _PROTOTYPE( int no_sys, (void) ); -_PROTOTYPE( void panic, (char *who, char *mess, int num) ); -_PROTOTYPE( char *find_param, (const char *key)); -_PROTOTYPE( int proc_from_pid, (pid_t p)); -_PROTOTYPE( int pm_isokendpt, (int ep, int *proc)); - +_PROTOTYPE( char *find_param, (const char *key) ); +_PROTOTYPE( struct mproc *find_proc, (pid_t lpid) ); +_PROTOTYPE( int pm_isokendpt, (int ep, int *proc) ); +_PROTOTYPE( void tell_fs, (struct mproc *rmp, message *m_ptr) ); diff --git a/servers/pm/signal.c b/servers/pm/signal.c index 678945bbb..47bbedb5e 100644 --- a/servers/pm/signal.c +++ b/servers/pm/signal.c @@ -5,17 +5,18 @@ * can be signaled. The actual signaling is done by sig_proc(). * * The entry points into this file are: - * do_sigaction: perform the SIGACTION system call - * do_sigpending: perform the SIGPENDING system call - * do_sigprocmask: perform the SIGPROCMASK system call - * do_sigreturn: perform the SIGRETURN system call - * do_sigsuspend: perform the SIGSUSPEND system call - * do_kill: perform the KILL system call - * do_pause: perform the PAUSE system call - * ksig_pending: the kernel notified about pending signals - * sig_proc: interrupt or terminate a signaled process - * check_sig: check which processes to signal with sig_proc() - * check_pending: check if a pending signal can now be delivered + * do_sigaction: perform the SIGACTION system call + * do_sigpending: perform the SIGPENDING system call + * do_sigprocmask: perform the SIGPROCMASK system call + * do_sigreturn: perform the SIGRETURN system call + * do_sigsuspend: perform the SIGSUSPEND system call + * do_kill: perform the KILL system call + * do_pause: perform the PAUSE system call + * ksig_pending: the kernel notified about pending signals + * sig_proc: interrupt or terminate a signaled process + * check_sig: check which processes to signal with sig_proc() + * check_pending: check if a pending signal can now be delivered + * restart_sigs: restart signal work after finishing a FS call */ #include "pm.h" @@ -32,8 +33,9 @@ #include "mproc.h" #include "param.h" -FORWARD _PROTOTYPE( void unpause, (int pro, int for_trace) ); +FORWARD _PROTOTYPE( void unpause, (struct mproc *rmp) ); FORWARD _PROTOTYPE( void handle_ksig, (int proc_nr, sigset_t sig_map) ); +FORWARD _PROTOTYPE( int sig_send, (struct mproc *rmp, int signo) ); /*===========================================================================* * do_sigaction * @@ -45,7 +47,7 @@ PUBLIC int do_sigaction() struct sigaction *svp; if (m_in.sig_nr == SIGKILL) return(OK); - if (m_in.sig_nr < 1 || m_in.sig_nr > _NSIG) return (EINVAL); + if (m_in.sig_nr < 1 || m_in.sig_nr >= _NSIG) return(EINVAL); svp = &mp->mp_sigact[m_in.sig_nr]; if ((struct sigaction *) m_in.sig_osa != (struct sigaction *) NULL) { r = sys_datacopy(PM_PROC_NR,(vir_bytes) svp, @@ -122,14 +124,14 @@ PUBLIC int do_sigprocmask() case SIG_BLOCK: sigdelset((sigset_t *)&m_in.sig_set, SIGKILL); sigdelset((sigset_t *)&m_in.sig_set, SIGSTOP); - for (i = 1; i <= _NSIG; i++) { + for (i = 1; i < _NSIG; i++) { if (sigismember((sigset_t *)&m_in.sig_set, i)) sigaddset(&mp->mp_sigmask, i); } break; case SIG_UNBLOCK: - for (i = 1; i <= _NSIG; i++) { + for (i = 1; i < _NSIG; i++) { if (sigismember((sigset_t *)&m_in.sig_set, i)) sigdelset(&mp->mp_sigmask, i); } @@ -212,7 +214,7 @@ PUBLIC int ksig_pending() * signals until all signals are handled. If there are no more signals, * NONE is returned in the process number field. */ - int proc_nr_e; + endpoint_t proc_nr_e; sigset_t sig_map; while (TRUE) { @@ -257,7 +259,9 @@ sigset_t sig_map; } rmp = &mproc[proc_nr]; if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) { +#if 0 printf("PM: handle_ksig: %d?? exiting / not in use\n", proc_nr_e); +#endif return; } proc_id = rmp->mp_pid; @@ -272,7 +276,7 @@ sigset_t sig_map; * to indicate a broadcast to the recipient's process group. For * SIGKILL, use proc_id -1 to indicate a systemwide broadcast. */ - for (i = 1; i <= _NSIG; i++) { + for (i = 1; i < _NSIG; i++) { if (!sigismember(&sig_map, i)) continue; #if 0 printf("PM: sig %d for %d from kernel\n", @@ -293,6 +297,26 @@ sigset_t sig_map; } check_sig(id, i); } + + /* If SIGKREADY is set, an earlier sys_stop() failed because the process was + * still sending, and the kernel hereby tells us that the process is now done + * with that. We can now try to resume what we planned to do in the first + * place: set up a signal handler. However, the process's message may have + * been a call to PM, in which case the process may have changed any of its + * signal settings. The process may also have forked, exited etcetera. + */ + if (sigismember(&sig_map, SIGKREADY) && (rmp->mp_flags & DELAY_CALL)) { + rmp->mp_flags &= ~DELAY_CALL; + + if (rmp->mp_flags & (FS_CALL | PM_SIG_PENDING)) + panic(__FILE__, "handle_ksig: bad process state", NO_NUM); + + /* Process as many normal signals as possible. */ + check_pending(rmp); + + if (rmp->mp_flags & DELAY_CALL) + panic(__FILE__, "handle_ksig: multiple delay calls?", NO_NUM); + } } /*===========================================================================* @@ -306,34 +330,13 @@ PUBLIC int do_pause() return(SUSPEND); } -PUBLIC vm_notify_sig_wrapper(endpoint_t ep) -{ - /* get IPC's endpoint, - * the reason that we directly get the endpoint - * instead of from DS server is that otherwise - * it will cause deadlock between PM, VM and DS. - */ - struct mproc *rmp; - endpoint_t ipc_ep = 0; - - for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { - if (!(rmp->mp_flags & IN_USE)) - continue; - if (!strcmp(rmp->mp_name, "ipc")) { - ipc_ep = rmp->mp_endpoint; - vm_notify_sig(ep, ipc_ep); - - return; - } - } -} - /*===========================================================================* * sig_proc * *===========================================================================*/ -PUBLIC void sig_proc(rmp, signo) +PUBLIC void sig_proc(rmp, signo, trace) register struct mproc *rmp; /* pointer to the process to be signaled */ -int signo; /* signal to send to process (1 to _NSIG) */ +int signo; /* signal to send to process (1 to _NSIG-1) */ +int trace; /* pass signal to tracer first? */ { /* Send a signal to a process. Check to see if the signal is to be caught, * ignored, tranformed into a message (for system processes) or blocked. @@ -348,111 +351,99 @@ int signo; /* signal to send to process (1 to _NSIG) */ * context from the sigcontext structure. * If there is insufficient stack space, kill the process. */ - - vir_bytes cur_sp; - int s; - int slot; - int sigflags; + int r, slot; slot = (int) (rmp - mproc); if ((rmp->mp_flags & (IN_USE | EXITING)) != IN_USE) { printf("PM: signal %d sent to exiting process %d\n", signo, slot); panic(__FILE__,"", NO_NUM); } - if (rmp->mp_fs_call != PM_IDLE || rmp->mp_fs_call2 != PM_IDLE) - { + + if (trace == TRUE && rmp->mp_tracer != NO_TRACER && signo != SIGKILL) { + /* Signal should be passed to the debugger first. + * This happens before any checks on block/ignore masks; otherwise, + * the process itself could block/ignore debugger signals. + */ + + sigaddset(&rmp->mp_sigtrace, signo); + + if (!(rmp->mp_flags & STOPPED)) + stop_proc(rmp, signo); /* a signal causes it to stop */ + + return; + } + + if (rmp->mp_flags & FS_CALL) { sigaddset(&rmp->mp_sigpending, signo); - rmp->mp_flags |= PM_SIG_PENDING; - /* keep the process from running */ - sys_nice(rmp->mp_endpoint, PRIO_STOP); - return; - - } - if ((rmp->mp_flags & TRACED) && signo != SIGKILL) { - /* A traced process has special handling. */ - unpause(slot, TRUE /*for_trace*/); - stop_proc(rmp, signo); /* a signal causes it to stop */ + + if (!(rmp->mp_flags & PM_SIG_PENDING)) { + /* This stop request must never result in EBUSY here! */ + if ((r = sys_stop(rmp->mp_endpoint)) != OK) + panic(__FILE__, "sys_stop failed", r); + + rmp->mp_flags |= PM_SIG_PENDING; + } + return; } - /* Some signals are ignored by default. */ + if (sigismember(&rmp->mp_ignore, signo)) { - return; + /* Signal should be ignored. */ + return; } if (sigismember(&rmp->mp_sigmask, signo)) { /* Signal should be blocked. */ sigaddset(&rmp->mp_sigpending, signo); return; } - sigflags = rmp->mp_sigact[signo].sa_flags; - if (sigismember(&rmp->mp_catch, signo)) { - if (rmp->mp_flags & SIGSUSPENDED) - rmp->mp_sigmsg.sm_mask = rmp->mp_sigmask2; - else - rmp->mp_sigmsg.sm_mask = rmp->mp_sigmask; - rmp->mp_sigmsg.sm_signo = signo; - rmp->mp_sigmsg.sm_sighandler = - (vir_bytes) rmp->mp_sigact[signo].sa_handler; - rmp->mp_sigmsg.sm_sigreturn = rmp->mp_sigreturn; - rmp->mp_sigmask |= rmp->mp_sigact[signo].sa_mask; - - if (sigflags & SA_NODEFER) - sigdelset(&rmp->mp_sigmask, signo); - else - sigaddset(&rmp->mp_sigmask, signo); - - if (sigflags & SA_RESETHAND) { - sigdelset(&rmp->mp_catch, signo); - rmp->mp_sigact[signo].sa_handler = SIG_DFL; - } - sigdelset(&rmp->mp_sigpending, signo); - - /* Stop process from running before we fiddle with its stack. */ - sys_nice(rmp->mp_endpoint, PRIO_STOP); - if(vm_push_sig(rmp->mp_endpoint, &cur_sp) != OK) - goto doterminate; - - rmp->mp_sigmsg.sm_stkptr = cur_sp; - - /* Check to see if process is hanging on a PAUSE, WAIT or SIGSUSPEND - * call. - */ - if (rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED)) { - rmp->mp_flags &= ~(PAUSED | WAITING | SIGSUSPENDED); - setreply(slot, EINTR); - - /* Ask the kernel to deliver the signal */ - s= sys_sigsend(rmp->mp_endpoint, &rmp->mp_sigmsg); - if (s != OK) - panic(__FILE__, "sys_sigsend failed", s); - - /* Done */ - return; - } - - /* Ask FS to unpause the process. Deliver the signal when FS is - * ready. - */ - unpause(slot, FALSE /*!for_trace*/); - vm_notify_sig_wrapper(rmp->mp_endpoint); - return; - } - else if (sigismember(&rmp->mp_sig2mess, signo)) { - + if (sigismember(&rmp->mp_sig2mess, signo)) { /* Mark event pending in process slot and send notification. */ sigaddset(&rmp->mp_sigpending, signo); notify(rmp->mp_endpoint); - return; + return; } -doterminate: - /* Signal should not or cannot be caught. Take default action. */ - if (sigismember(&ign_sset, signo)) { + if ((rmp->mp_flags & STOPPED) && signo != SIGKILL) { + /* If the process is stopped for a debugger, do not deliver any signals + * (except SIGKILL) in order not to confuse the debugger. The signals + * will be delivered using the check_pending() calls in do_trace(). + */ + sigaddset(&rmp->mp_sigpending, signo); + return; + } + + if (sigismember(&rmp->mp_catch, signo)) { + /* Signal is caught. First interrupt the process's current call, if + * applicable. This may involve a roundtrip to FS, in which case we'll + * have to check back later. + */ + if (!(rmp->mp_flags & UNPAUSED)) { + unpause(rmp); + + if (!(rmp->mp_flags & UNPAUSED)) { + /* not yet unpaused; continue later */ + sigaddset(&rmp->mp_sigpending, signo); + + return; + } + } + + /* Then send the actual signal to the process, by setting up a signal + * handler. + */ + if (sig_send(rmp, signo)) + return; + + /* We were unable to spawn a signal handler. Kill the process. */ + } + else if (sigismember(&ign_sset, signo)) { + /* Signal defaults to being ignored. */ return; } /* Terminate process */ rmp->mp_sigstatus = (char) signo; - if (sigismember(&core_sset, signo) && slot != FS_PROC_NR) { + if (sigismember(&core_sset, signo)) { printf("PM: coredump signal %d for %d / %s\n", signo, rmp->mp_pid, rmp->mp_name); exit_proc(rmp, 0, TRUE /*dump_core*/); @@ -467,7 +458,7 @@ doterminate: *===========================================================================*/ PUBLIC int check_sig(proc_id, signo) pid_t proc_id; /* pid of proc to sig, or 0 or -1, or -pgrp */ -int signo; /* signal to send to process (0 to _NSIG) */ +int signo; /* signal to send to process (0 to _NSIG-1) */ { /* Check to see if it is possible to send a signal. The signal may have to be * sent to a group of processes. This routine is invoked by the KILL system @@ -478,7 +469,7 @@ int signo; /* signal to send to process (0 to _NSIG) */ int count; /* count # of signals sent */ int error_code; - if (signo < 0 || signo > _NSIG) return(EINVAL); + if (signo < 0 || signo >= _NSIG) return(EINVAL); /* Return EINVAL for attempts to send SIGKILL to INIT alone. */ if (proc_id == INIT_PID && signo == SIGKILL) return(EINVAL); @@ -518,7 +509,7 @@ int signo; /* signal to send to process (0 to _NSIG) */ * signal may be caught, blocked, ignored, or cause process * termination, possibly with core dump. */ - sig_proc(rmp, signo); + sig_proc(rmp, signo, TRUE /*trace*/); if (proc_id > 0) break; /* only one process being signaled */ } @@ -534,11 +525,8 @@ int signo; /* signal to send to process (0 to _NSIG) */ PUBLIC void check_pending(rmp) register struct mproc *rmp; { - /* Check to see if any pending signals have been unblocked. The - * first such signal found is delivered. - * - * If multiple pending unmasked signals are found, they will be - * delivered sequentially. + /* Check to see if any pending signals have been unblocked. Deliver as many + * of them as we can, until we have to wait for a reply from VFS first. * * There are several places in this file where the signal mask is * changed. At each such place, check_pending() should be called to @@ -547,12 +535,47 @@ register struct mproc *rmp; int i; - for (i = 1; i <= _NSIG; i++) { + for (i = 1; i < _NSIG; i++) { if (sigismember(&rmp->mp_sigpending, i) && !sigismember(&rmp->mp_sigmask, i)) { sigdelset(&rmp->mp_sigpending, i); - sig_proc(rmp, i); - break; + sig_proc(rmp, i, FALSE /*trace*/); + + if (rmp->mp_flags & FS_CALL) + break; + } + } +} + +/*===========================================================================* + * restart_sigs * + *===========================================================================*/ +PUBLIC void restart_sigs(rmp) +struct mproc *rmp; +{ +/* FS has replied to a request from us; do signal-related work. + */ + + if (rmp->mp_flags & (FS_CALL | EXITING)) return; + + if (rmp->mp_flags & TRACE_EXIT) { + /* Tracer requested exit with specific exit value */ + exit_proc(rmp, rmp->mp_exitstatus, FALSE /*dump_core*/); + } + else if (rmp->mp_flags & PM_SIG_PENDING) { + /* We saved signal(s) for after finishing a FS call. Deal with this. + * PM_SIG_PENDING remains set to indicate the process is still stopped. + */ + check_pending(rmp); + + /* The process may now be FS-blocked again, because a signal exited the + * process or was caught. Restart the process only when this is NOT the + * case. + */ + if (!(rmp->mp_flags & FS_CALL)) { + rmp->mp_flags &= ~(PM_SIG_PENDING | UNPAUSED); + + sys_resume(rmp->mp_endpoint); } } } @@ -560,9 +583,8 @@ register struct mproc *rmp; /*===========================================================================* * unpause * *===========================================================================*/ -PRIVATE void unpause(pro, for_trace) -int pro; /* which process number */ -int for_trace; /* for tracing */ +PRIVATE void unpause(rmp) +struct mproc *rmp; /* which process */ { /* A signal is to be sent to a process. If that process is hanging on a * system call, the system call must be terminated with EINTR. Possible @@ -570,31 +592,144 @@ int for_trace; /* for tracing */ * First check if the process is hanging on an PM call. If not, tell FS, * so it can check for READs and WRITEs from pipes, ttys and the like. */ - register struct mproc *rmp; - int r; + message m; + int r, slot; - rmp = &mproc[pro]; + /* If we're already waiting for a delayed call, don't do anything now. */ + if (rmp->mp_flags & DELAY_CALL) + return; /* Check to see if process is hanging on a PAUSE, WAIT or SIGSUSPEND call. */ if (rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED)) { - rmp->mp_flags &= ~(PAUSED | WAITING | SIGSUSPENDED); - setreply(pro, EINTR); + /* Stop process from running. + * This stop request must never result in EBUSY here! + */ + if ((r = sys_stop(rmp->mp_endpoint)) != OK) + panic(__FILE__, "sys_stop failed", r); + + rmp->mp_flags |= UNPAUSED; + + /* We interrupt the actual call from sig_send() below. */ return; } - /* Process is not hanging on an PM call. Ask FS to take a look. */ - if (for_trace) - { - if (rmp->mp_fs_call != PM_IDLE) - panic( __FILE__, "unpause: not idle", rmp->mp_fs_call); - rmp->mp_fs_call= PM_UNPAUSE_TR; + /* Not paused in PM. Let FS try to unpause the process. */ + if (!(rmp->mp_flags & PM_SIG_PENDING)) { + /* Stop process from running. */ + r = sys_stop(rmp->mp_endpoint); + + /* If the process is still busy sending a message, the kernel will give + * us EBUSY now and send a SIGKREADY to the process as soon as sending + * is done. + */ + if (r == EBUSY) { + rmp->mp_flags |= DELAY_CALL; + + return; + } + else if (r != OK) panic(__FILE__, "sys_stop failed", r); + + rmp->mp_flags |= PM_SIG_PENDING; + } + + m.m_type = PM_UNPAUSE; + m.PM_PROC = rmp->mp_endpoint; + + tell_fs(rmp, &m); + + /* Also tell VM. */ + vm_notify_sig_wrapper(rmp->mp_endpoint); +} + +/*===========================================================================* + * sig_send * + *===========================================================================*/ +PRIVATE int sig_send(rmp, signo) +struct mproc *rmp; /* what process to spawn a signal handler in */ +int signo; /* signal to send to process (1 to _NSIG-1) */ +{ +/* The process is supposed to catch this signal. Spawn a signal handler. + * Return TRUE if this succeeded, FALSE otherwise. + */ + struct sigmsg sigmsg; + vir_bytes cur_sp; + int r, sigflags, slot; + + if (!(rmp->mp_flags & UNPAUSED)) + panic(__FILE__, "sig_send: process not unpaused", NO_NUM); + + sigflags = rmp->mp_sigact[signo].sa_flags; + slot = (int) (rmp - mproc); + + if (rmp->mp_flags & SIGSUSPENDED) + sigmsg.sm_mask = rmp->mp_sigmask2; + else + sigmsg.sm_mask = rmp->mp_sigmask; + sigmsg.sm_signo = signo; + sigmsg.sm_sighandler = + (vir_bytes) rmp->mp_sigact[signo].sa_handler; + sigmsg.sm_sigreturn = rmp->mp_sigreturn; + rmp->mp_sigmask |= rmp->mp_sigact[signo].sa_mask; + + if (sigflags & SA_NODEFER) + sigdelset(&rmp->mp_sigmask, signo); + else + sigaddset(&rmp->mp_sigmask, signo); + + if (sigflags & SA_RESETHAND) { + sigdelset(&rmp->mp_catch, signo); + rmp->mp_sigact[signo].sa_handler = SIG_DFL; + } + sigdelset(&rmp->mp_sigpending, signo); + + if(vm_push_sig(rmp->mp_endpoint, &cur_sp) != OK) + return(FALSE); + + sigmsg.sm_stkptr = cur_sp; + + /* Ask the kernel to deliver the signal */ + r = sys_sigsend(rmp->mp_endpoint, &sigmsg); + if (r != OK) + panic(__FILE__, "sys_sigsend failed", r); + + /* Was the process suspended in PM? Then interrupt the blocking call. */ + if (rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED)) { + rmp->mp_flags &= ~(PAUSED | WAITING | SIGSUSPENDED); + + setreply(slot, EINTR); + } + + /* Was the process stopped just for this signal? Then resume it. */ + if ((rmp->mp_flags & (PM_SIG_PENDING | UNPAUSED)) == UNPAUSED) { + rmp->mp_flags &= ~UNPAUSED; + + sys_resume(rmp->mp_endpoint); + } + + return(TRUE); +} + +/*===========================================================================* + * vm_notify_sig_wrapper * + *===========================================================================*/ +PUBLIC void vm_notify_sig_wrapper(endpoint_t ep) +{ +/* get IPC's endpoint, + * the reason that we directly get the endpoint + * instead of from DS server is that otherwise + * it will cause deadlock between PM, VM and DS. + */ + struct mproc *rmp; + endpoint_t ipc_ep = 0; + + for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { + if (!(rmp->mp_flags & IN_USE)) + continue; + if (!strcmp(rmp->mp_name, "ipc")) { + ipc_ep = rmp->mp_endpoint; + vm_notify_sig(ep, ipc_ep); + + return; + } } - else - { - if (rmp->mp_fs_call2 != PM_IDLE) - panic( __FILE__, "unpause: not idle", rmp->mp_fs_call2); - rmp->mp_fs_call2= PM_UNPAUSE; - } - r= notify(FS_PROC_NR); - if (r != OK) panic("pm", "unpause: unable to notify FS", r); } diff --git a/servers/pm/table.c b/servers/pm/table.c index 251872863..7e21d1c13 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -34,11 +34,11 @@ _PROTOTYPE (int (*call_vec[]), (void) ) = { do_brk, /* 17 = break */ no_sys, /* 18 = stat */ no_sys, /* 19 = lseek */ - do_getset, /* 20 = getpid */ + do_get, /* 20 = getpid */ no_sys, /* 21 = mount */ no_sys, /* 22 = umount */ - do_getset, /* 23 = setuid */ - do_getset, /* 24 = getuid */ + do_set, /* 23 = setuid */ + do_get, /* 24 = getuid */ do_stime, /* 25 = stime */ do_trace, /* 26 = ptrace */ do_alarm, /* 27 = alarm */ @@ -60,8 +60,8 @@ _PROTOTYPE (int (*call_vec[]), (void) ) = { do_times, /* 43 = times */ no_sys, /* 44 = (prof) */ no_sys, /* 45 = unused */ - do_getset, /* 46 = setgid */ - do_getset, /* 47 = getgid */ + do_set, /* 46 = setgid */ + do_get, /* 47 = getgid */ no_sys, /* 48 = (signal)*/ no_sys, /* 49 = unused */ no_sys, /* 50 = unused */ @@ -76,8 +76,8 @@ _PROTOTYPE (int (*call_vec[]), (void) ) = { do_exec, /* 59 = execve */ no_sys, /* 60 = umask */ no_sys, /* 61 = chroot */ - do_getset, /* 62 = setsid */ - do_getset, /* 63 = getpgrp */ + do_set, /* 62 = setsid */ + do_get, /* 63 = getpgrp */ do_itimer, /* 64 = itimer */ no_sys, /* 65 = unused */ no_sys, /* 66 = unused */ @@ -105,8 +105,8 @@ _PROTOTYPE (int (*call_vec[]), (void) ) = { do_getsetpriority, /* 88 = getpriority */ do_getsetpriority, /* 89 = setpriority */ do_time, /* 90 = gettimeofday */ - do_getset, /* 91 = seteuid */ - do_getset, /* 92 = setegid */ + do_set, /* 91 = seteuid */ + do_set, /* 92 = setegid */ no_sys, /* 93 = (truncate) */ no_sys, /* 94 = (ftruncate) */ no_sys, /* 95 = (fchmod) */ diff --git a/servers/pm/trace.c b/servers/pm/trace.c index 004fc73c1..4652e639c 100644 --- a/servers/pm/trace.c +++ b/servers/pm/trace.c @@ -14,10 +14,15 @@ * T_RESUME resume execution * T_EXIT exit * T_STEP set trace bit + * T_SYSCALL trace system call + * T_ATTACH attach to an existing process + * T_DETACH detach from a traced process + * T_SETOPT set trace options * - * The T_OK and T_EXIT commands are handled here, and the T_RESUME and - * T_STEP commands are partially handled here and completed by the system - * task. The rest are handled entirely by the system task. + * The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the + * T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled + * here and completed by the system task. The rest are handled entirely by the + * system task. */ #include "pm.h" @@ -27,47 +32,81 @@ #include "mproc.h" #include "param.h" -#define NIL_MPROC ((struct mproc *) 0) - -FORWARD _PROTOTYPE( struct mproc *find_proc, (pid_t lpid) ); - /*===========================================================================* * do_trace * *===========================================================================*/ PUBLIC int do_trace() { register struct mproc *child; - int r; + int i, r, req; - /* the T_OK call is made by the child fork of the debugger before it execs - * the process to be traced + req = m_in.request; + + /* The T_OK call is made by the child fork of the debugger before it execs + * the process to be traced. The T_ATTACH call is made by the debugger itself + * to attach to an existing process. */ - if (m_in.request == T_OK) { /* enable tracing by parent for this proc */ - mp->mp_flags |= TRACED; + switch (req) { + case T_OK: /* enable tracing by parent for this proc */ + if (mp->mp_tracer != NO_TRACER) return(EBUSY); + + mp->mp_tracer = mp->mp_parent; mp->mp_reply.reply_trace = 0; return(OK); - } - if (m_in.request == T_READB_INS) - { - /* Special hack for reading text segments */ - if (mp->mp_effuid != SUPER_USER) - return(EPERM); - if ((child=find_proc(m_in.pid))==NIL_MPROC) - return(ESRCH); - r= sys_trace(m_in.request,child->mp_endpoint,m_in.PMTRACE_ADDR,&m_in.data); + case T_ATTACH: /* attach to an existing process */ + if ((child = find_proc(m_in.pid)) == NIL_MPROC) return(ESRCH); + if (child->mp_flags & EXITING) return(ESRCH); + + /* For non-root processes, user and group ID must match. */ + if (mp->mp_effuid != SUPER_USER && + (mp->mp_effuid != child->mp_effuid || + mp->mp_effgid != child->mp_effgid || + child->mp_effuid != child->mp_realuid || + child->mp_effgid != child->mp_realgid)) return(EPERM); + + /* Only root may trace system servers. */ + if (mp->mp_effuid != SUPER_USER && (child->mp_flags & PRIV_PROC)) + return(EPERM); + + /* System servers may not trace anyone. They can use sys_trace(). */ + if (mp->mp_flags & PRIV_PROC) return(EPERM); + + /* Can't trace self, PM or VM. */ + if (child == mp || child->mp_endpoint == PM_PROC_NR || + child->mp_endpoint == VM_PROC_NR) return(EPERM); + + /* Can't trace a process that is already being traced. */ + if (child->mp_tracer != NO_TRACER) return(EBUSY); + + child->mp_tracer = who_p; + + sig_proc(child, SIGSTOP, TRUE /*trace*/); + + mp->mp_reply.reply_trace = 0; + return(OK); + + case T_STOP: /* stop the process */ + /* This call is not exposed to user programs, because its effect can be + * achieved better by sending the traced process a signal with kill(2). + */ + return(EINVAL); + + case T_READB_INS: /* special hack for reading text segments */ + if (mp->mp_effuid != SUPER_USER) return(EPERM); + if ((child = find_proc(m_in.pid)) == NIL_MPROC) return(ESRCH); + if (child->mp_flags & EXITING) return(ESRCH); + + r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data); if (r != OK) return(r); mp->mp_reply.reply_trace = m_in.data; return(OK); - } - if (m_in.request == T_WRITEB_INS) - { - /* Special hack for patching text segments */ - if (mp->mp_effuid != SUPER_USER) - return(EPERM); - if ((child=find_proc(m_in.pid))==NIL_MPROC) - return(ESRCH); + + case T_WRITEB_INS: /* special hack for patching text segments */ + if (mp->mp_effuid != SUPER_USER) return(EPERM); + if ((child = find_proc(m_in.pid)) == NIL_MPROC) return(ESRCH); + if (child->mp_flags & EXITING) return(ESRCH); #if 0 /* Should check for shared text */ @@ -75,47 +114,32 @@ PUBLIC int do_trace() /* Make sure the text segment is not used as a source for shared * text. */ - child->mp_ino= 0; - child->mp_dev= 0; - child->mp_ctime= 0; + child->mp_ino = 0; + child->mp_dev = 0; + child->mp_ctime = 0; #endif - r= sys_trace(m_in.request,child->mp_endpoint,m_in.PMTRACE_ADDR,&m_in.data); + r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data); if (r != OK) return(r); mp->mp_reply.reply_trace = m_in.data; return(OK); } - /* all the other calls are made by the parent fork of the debugger to - * control execution of the child + /* All the other calls are made by the tracing process to control execution + * of the child. For all these calls, the child must be stopped. */ - if ((child=find_proc(m_in.pid))==NIL_MPROC || child->mp_parent != who_p) - return(ESRCH); + if ((child = find_proc(m_in.pid)) == NIL_MPROC) return(ESRCH); + if (child->mp_flags & EXITING) return(ESRCH); + if (child->mp_tracer != who_p) return(ESRCH); + if (!(child->mp_flags & STOPPED)) return(EBUSY); - if (m_in.request == T_STOP) { - if ((r = sys_trace(T_STOP, child->mp_endpoint, 0L, (long *) 0)) != OK) - return(r); - - child->mp_flags |= STOPPED; - child->mp_sigstatus = 0; - - mp->mp_reply.reply_trace = 0; - return(OK); - } - - /* for calls other than T_STOP, the child must be stopped and the parent - * must have waited for it - */ - if (!(child->mp_flags & STOPPED) || child->mp_sigstatus > 0) - return(ESRCH); - - switch (m_in.request) { + switch (req) { case T_EXIT: /* exit */ child->mp_flags |= TRACE_EXIT; /* Defer the exit if the traced process has an FS call pending. */ - if (child->mp_fs_call != PM_IDLE || child->mp_fs_call2 != PM_IDLE) + if (child->mp_flags & FS_CALL) child->mp_exitstatus = (int) m_in.data; /* save for later */ else exit_proc(child, (int) m_in.data, FALSE /*dump_core*/); @@ -123,41 +147,72 @@ PUBLIC int do_trace() /* Do not reply to the caller until FS has processed the exit * request. */ - return SUSPEND; - case T_RESUME: - case T_STEP: /* resume execution */ - if (m_in.data < 0 || m_in.data > _NSIG) return(EIO); - if (m_in.data > 0) { /* issue signal */ - child->mp_flags &= ~TRACED; /* so signal is not diverted */ - sig_proc(child, (int) m_in.data); - child->mp_flags |= TRACED; + return(SUSPEND); + + case T_SETOPT: /* set trace options */ + child->mp_trace_flags = m_in.data; + + mp->mp_reply.reply_trace = 0; + return(OK); + + case T_DETACH: /* detach from traced process */ + if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL); + + child->mp_tracer = NO_TRACER; + + /* Let all tracer-pending signals through the filter. */ + for (i = 1; i < _NSIG; i++) { + if (sigismember(&child->mp_sigtrace, i)) { + sigdelset(&child->mp_sigtrace, i); + check_sig(child->mp_pid, i); + } } + + if (m_in.data > 0) { /* issue signal */ + sig_proc(child, (int) m_in.data, TRUE /*trace*/); + } + + /* Resume the child as if nothing ever happened. */ child->mp_flags &= ~STOPPED; - break; + child->mp_trace_flags = 0; + req = T_RESUME; + + check_pending(child); + + break; + + case T_RESUME: + case T_STEP: + case T_SYSCALL: /* resume execution */ + if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL); + + if (m_in.data > 0) { /* issue signal */ + sig_proc(child, (int) m_in.data, FALSE /*trace*/); + } + + /* If there are any other signals waiting to be delivered, + * feign a successful resumption. + */ + for (i = 1; i < _NSIG; i++) { + if (sigismember(&child->mp_sigtrace, i)) { + mp->mp_reply.reply_trace = 0; + return(OK); + } + } + + child->mp_flags &= ~STOPPED; + + check_pending(child); + + break; } - r= sys_trace(m_in.request,child->mp_endpoint,m_in.PMTRACE_ADDR,&m_in.data); + r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data); if (r != OK) return(r); mp->mp_reply.reply_trace = m_in.data; return(OK); } -/*===========================================================================* - * find_proc * - *===========================================================================*/ -PRIVATE struct mproc *find_proc(lpid) -pid_t lpid; -{ - register struct mproc *rmp; - - for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) - if ((rmp->mp_flags & (IN_USE | EXITING)) == IN_USE && - rmp->mp_pid == lpid) { - return(rmp); - } - return(NIL_MPROC); -} - /*===========================================================================* * stop_proc * *===========================================================================*/ @@ -167,19 +222,18 @@ int signo; { /* A traced process got a signal so stop it. */ - register struct mproc *rpmp = mproc + rmp->mp_parent; + register struct mproc *rpmp = mproc + rmp->mp_tracer; int r; - r= sys_trace(T_STOP, rmp->mp_endpoint, 0L, (long *) 0); + r = sys_trace(T_STOP, rmp->mp_endpoint, 0L, (long *) 0); if (r != OK) panic("pm", "sys_trace failed", r); rmp->mp_flags |= STOPPED; - if (rpmp->mp_flags & WAITING) { + if (wait_test(rpmp, rmp)) { + sigdelset(&rmp->mp_sigtrace, signo); + rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */ rpmp->mp_reply.reply_res2 = 0177 | (signo << 8); - setreply(rmp->mp_parent, rmp->mp_pid); - } else { - rmp->mp_sigstatus = signo; + setreply(rmp->mp_tracer, rmp->mp_pid); } - return; } diff --git a/servers/pm/utility.c b/servers/pm/utility.c index 2e1838dfc..c75bc0fa4 100644 --- a/servers/pm/utility.c +++ b/servers/pm/utility.c @@ -1,11 +1,12 @@ /* This file contains some utility routines for PM. * * The entry points are: - * find_param: look up a boot monitor parameter * get_free_pid: get a free process or group id * no_sys: called for invalid system call numbers - * proc_from_pid: return process pointer from pid number + * find_param: look up a boot monitor parameter + * find_proc: return process pointer from pid number * pm_isokendpt: check the validity of an endpoint + * tell_fs: send a request to FS on behalf of a process */ #include "pm.h" @@ -88,18 +89,18 @@ const char *name; } /*===========================================================================* - * proc_from_pid * + * find_proc * *===========================================================================*/ -PUBLIC int proc_from_pid(mp_pid) -pid_t mp_pid; +PUBLIC struct mproc *find_proc(lpid) +pid_t lpid; { - int rmp; + register struct mproc *rmp; - for (rmp = 0; rmp < NR_PROCS; rmp++) - if (mproc[rmp].mp_pid == mp_pid) - return rmp; + for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) + if ((rmp->mp_flags & IN_USE) && rmp->mp_pid == lpid) + return(rmp); - return -1; + return(NIL_MPROC); } /*===========================================================================* @@ -117,6 +118,27 @@ PUBLIC int pm_isokendpt(int endpoint, int *proc) return OK; } +/*===========================================================================* + * tell_fs * + *===========================================================================*/ +PUBLIC void tell_fs(rmp, m_ptr) +struct mproc *rmp; +message *m_ptr; +{ +/* Send a request to VFS, without blocking. + */ + int r; + + if (rmp->mp_flags & FS_CALL) + panic(__FILE__, "tell_fs: not idle", m_ptr->m_type); + + r = asynsend3(FS_PROC_NR, m_ptr, AMF_NOREPLY); + if (r != OK) + panic(__FILE__, "unable to send to FS", r); + + rmp->mp_flags |= FS_CALL; +} + int unmap_ok = 0; PUBLIC int munmap(void *addrstart, vir_bytes len) diff --git a/servers/rs/manager.c b/servers/rs/manager.c index cd1ae0e37..5918a9138 100644 --- a/servers/rs/manager.c +++ b/servers/rs/manager.c @@ -897,6 +897,19 @@ endpoint_t *endpoint; printf("RS: start_service: ds_publish_u32 done: %s -> %d\n", rp->r_label, child_proc_nr_e); + /* The purpose of non-blocking forks is to avoid involving VFS in the forking + * process, because VFS may be blocked on a sendrec() to a MFS that is + * waiting for a endpoint update for a dead driver. We have just published + * that update, but VFS may still be blocked. As a result, VFS may not yet + * have received PM's fork message. Hence, if we call mapdriver5() + * immediately, VFS may not know about the process and thus refuse to add the + * driver entry. The following temporary hack works around this by forcing + * blocking communication from PM to VFS. Once VFS has been made non-blocking + * towards MFS instances, this hack and the entire fork_nb() call can go. + */ + if (use_copy) + setuid(0); + if (rp->r_dev_nr > 0) { /* set driver map */ if ((s=mapdriver5(rp->r_label, strlen(rp->r_label), rp->r_dev_nr, rp->r_dev_style, !!use_copy /* force */)) < 0) { diff --git a/servers/vfs/main.c b/servers/vfs/main.c index dc6143a6d..962c98d99 100644 --- a/servers/vfs/main.c +++ b/servers/vfs/main.c @@ -69,9 +69,6 @@ PUBLIC int main(void) SANITYCHECK; get_work(); /* sets who and call_nr */ - if (who_e == PM_PROC_NR && call_nr != PROC_EVENT) - printf("FS: strange, got message %d from PM\n", call_nr); - if (call_nr == DEV_REVIVE) { endpoint_t endpt; @@ -114,8 +111,7 @@ PUBLIC int main(void) if (is_notify(call_nr)) { if (who_p == PM_PROC_NR) { - /* PM tries to get FS to do something */ - service_pm(); + /* Signaled by PM, ignore. */ } else if (who_p == CLOCK) { @@ -152,6 +148,13 @@ PUBLIC int main(void) } #endif + /* Calls from PM. */ + if (who_e == PM_PROC_NR) { + service_pm(); + + continue; + } + /* Calls from VM. */ if(who_e == VM_PROC_NR) { int caught = 1; @@ -502,112 +505,96 @@ PRIVATE void init_root() *===========================================================================*/ PRIVATE void service_pm() { - int r, call; - message m; + int r; - /* Ask PM for work until there is nothing left to do */ - for (;;) - { - m.m_type= PM_GET_WORK; - r= sendrec(PM_PROC_NR, &m); - if (r != OK) - { - panic("VFS", "service_pm: sendrec failed", r); - } - if (m.m_type == PM_IDLE) { - break; - } - call= m.m_type; - switch(call) - { - case PM_SETSID: - pm_setsid(m.PM_SETSID_PROC); + switch (call_nr) { + case PM_SETUID: + pm_setuid(m_in.PM_PROC, m_in.PM_EID, m_in.PM_RID); - /* No need to report status to PM */ - break; + m_out.m_type = PM_SETUID_REPLY; + m_out.PM_PROC = m_in.PM_PROC; - case PM_SETGID: - pm_setgid(m.PM_SETGID_PROC, m.PM_SETGID_EGID, - m.PM_SETGID_RGID); + break; - /* No need to report status to PM */ - break; + case PM_SETGID: + pm_setgid(m_in.PM_PROC, m_in.PM_EID, m_in.PM_RID); - case PM_SETUID: - pm_setuid(m.PM_SETUID_PROC, m.PM_SETUID_EGID, - m.PM_SETUID_RGID); + m_out.m_type = PM_SETGID_REPLY; + m_out.PM_PROC = m_in.PM_PROC; - /* No need to report status to PM */ - break; + break; - case PM_FORK: - pm_fork(m.PM_FORK_PPROC, m.PM_FORK_CPROC, - m.PM_FORK_CPID); + case PM_SETSID: + pm_setsid(m_in.PM_PROC); - /* No need to report status to PM */ - break; + m_out.m_type = PM_SETSID_REPLY; + m_out.PM_PROC = m_in.PM_PROC; - case PM_EXIT: - pm_exit(m.PM_EXIT_PROC); + break; - /* Reply dummy status to PM for synchronization */ - m.m_type= PM_EXIT_REPLY; - /* Keep m.PM_EXIT_PROC */ + case PM_EXEC: + r = pm_exec(m_in.PM_PROC, m_in.PM_PATH, m_in.PM_PATH_LEN, + m_in.PM_FRAME, m_in.PM_FRAME_LEN); - r= send(PM_PROC_NR, &m); - if (r != OK) - panic(__FILE__, "service_pm: send failed", r); - break; + /* Reply status to PM */ + m_out.m_type = PM_EXEC_REPLY; + m_out.PM_PROC = m_in.PM_PROC; + m_out.PM_STATUS = r; - case PM_UNPAUSE: - case PM_UNPAUSE_TR: - unpause(m.PM_UNPAUSE_PROC); + break; - /* No need to report status to PM */ - break; + case PM_EXIT: + pm_exit(m_in.PM_PROC); - case PM_REBOOT: - pm_reboot(); + /* Reply dummy status to PM for synchronization */ + m_out.m_type = PM_EXIT_REPLY; + m_out.PM_PROC = m_in.PM_PROC; - /* Reply dummy status to PM for synchronization */ - m.m_type= PM_REBOOT_REPLY; - r= send(PM_PROC_NR, &m); - if (r != OK) - panic(__FILE__, "service_pm: send failed", r); - break; + break; - case PM_EXEC: - r= pm_exec(m.PM_EXEC_PROC, m.PM_EXEC_PATH, - m.PM_EXEC_PATH_LEN, m.PM_EXEC_FRAME, - m.PM_EXEC_FRAME_LEN); + case PM_DUMPCORE: + r = pm_dumpcore(m_in.PM_PROC, + NULL /* (struct mem_map *) m_in.PM_SEGPTR */); - /* Reply status to PM */ - m.m_type= PM_EXEC_REPLY; - /* Keep m.PM_EXEC_PROC */ - m.PM_EXEC_STATUS= r; - - r= send(PM_PROC_NR, &m); - if (r != OK) - panic(__FILE__, "service_pm: send failed", r); - break; + /* Reply status to PM */ + m_out.m_type = PM_CORE_REPLY; + m_out.PM_PROC = m_in.PM_PROC; + m_out.PM_STATUS = r; + + break; - case PM_DUMPCORE: - r= pm_dumpcore(m.PM_CORE_PROC, - (struct mem_map *)m.PM_CORE_SEGPTR); + case PM_FORK: + case PM_FORK_NB: + pm_fork(m_in.PM_PPROC, m_in.PM_PROC, m_in.PM_CPID); - /* Reply status to PM */ - m.m_type= PM_CORE_REPLY; - /* Keep m.PM_CORE_PROC */ - m.PM_CORE_STATUS= r; - - r= send(PM_PROC_NR, &m); - if (r != OK) - panic(__FILE__, "service_pm: send failed", r); - break; + m_out.m_type = (call_nr == PM_FORK) ? PM_FORK_REPLY : PM_FORK_NB_REPLY; + m_out.PM_PROC = m_in.PM_PROC; - default: - panic("VFS", "service_pm: unknown call", m.m_type); - } - } + break; + + case PM_UNPAUSE: + unpause(m_in.PM_PROC); + + m_out.m_type = PM_UNPAUSE_REPLY; + m_out.PM_PROC = m_in.PM_PROC; + + break; + + case PM_REBOOT: + pm_reboot(); + + /* Reply dummy status to PM for synchronization */ + m_out.m_type = PM_REBOOT_REPLY; + + break; + + default: + printf("VFS: don't know how to handle PM request %x\n", call_nr); + + return; + } + + r = send(PM_PROC_NR, &m_out); + if (r != OK) + panic(__FILE__, "service_pm: send failed", r); } - diff --git a/test/Makefile b/test/Makefile index dde86756a..6262d8d89 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,8 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \ test10 test12 test13 test14 test15 test16 test17 test18 test19 \ test21 test22 test23 test25 test26 test27 test28 test29 \ test30 test31 test32 test34 test35 test36 test37 test38 \ - test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 + test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \ + test42 BIGOBJ= test20 test24 ROOTOBJ= test11 test33 @@ -81,3 +82,4 @@ t40d: t40d.c t40e: t40e.c t40f: t40f.c test41: test41.c +test42: test42.c diff --git a/test/run b/test/run index 5a63df35f..7fba9a7fc 100755 --- a/test/run +++ b/test/run @@ -12,13 +12,13 @@ badones= # list of tests that failed # Print test welcome message clr -echo "Running POSIX compliance test suite. There are 43 tests in total." +echo "Running POSIX compliance test suite. There are 44 tests in total." echo " " # Run all the tests, keeping track of who failed. for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ - 41 sh1.sh sh2.sh + 41 42 sh1.sh sh2.sh do total=`expr $total + 1` FAIL=0 if [ $USER = root -a \( $i = 11 -o $i = 33 \) ] diff --git a/test/test42.c b/test/test42.c new file mode 100644 index 000000000..35e371cb3 --- /dev/null +++ b/test/test42.c @@ -0,0 +1,1349 @@ +/* Tests for MINIX3 ptrace(2) - by D.C. van Moolenbroek */ +#define _POSIX_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include + +#define ITERATIONS 3 +#define MAX_ERROR 4 + +#define _WIFSTOPPED(s) (WIFSTOPPED(s) && !WIFSIGNALED(s) && !WIFEXITED(s)) +#define _WIFSIGNALED(s) (!WIFSTOPPED(s) && WIFSIGNALED(s) && !WIFEXITED(s)) +#define _WIFEXITED(s) (!WIFSTOPPED(s) && !WIFSIGNALED(s) && WIFEXITED(s)) + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void test, (int m, int a)); +_PROTOTYPE(pid_t traced_fork, (_PROTOTYPE(void (*c), (void)))); +_PROTOTYPE(pid_t traced_pfork, (_PROTOTYPE(void (*c), (void)))); +_PROTOTYPE(void WRITE, (int value)); +_PROTOTYPE(int READ, (void)); +_PROTOTYPE(void traced_wait, (void)); +_PROTOTYPE(void detach_running, (pid_t pid)); +_PROTOTYPE(void dummy_handler, (int sig)); +_PROTOTYPE(void exit_handler, (int sig)); +_PROTOTYPE(void count_handler, (int sig)); +_PROTOTYPE(void catch_handler, (int sig)); +_PROTOTYPE(void test_wait_child, (void)); +_PROTOTYPE(void test_wait, (void)); +_PROTOTYPE(void test_exec_child, (void)); +_PROTOTYPE(void test_exec, (void)); +_PROTOTYPE(void test_step_child, (void)); +_PROTOTYPE(void test_step, (void)); +_PROTOTYPE(void test_sig_child, (void)); +_PROTOTYPE(void test_sig, (void)); +_PROTOTYPE(void test_exit_child, (void)); +_PROTOTYPE(void test_exit, (void)); +_PROTOTYPE(void test_term_child, (void)); +_PROTOTYPE(void test_term, (void)); +_PROTOTYPE(void test_catch_child, (void)); +_PROTOTYPE(void test_catch, (void)); +_PROTOTYPE(void test_kill_child, (void)); +_PROTOTYPE(void test_kill, (void)); +_PROTOTYPE(void test_attach_child, (void)); +_PROTOTYPE(void test_attach, (void)); +_PROTOTYPE(void test_detach_child, (void)); +_PROTOTYPE(void test_detach, (void)); +_PROTOTYPE(void test_death_child, (void)); +_PROTOTYPE(void test_death, (void)); +_PROTOTYPE(void test_zdeath_child, (void)); +_PROTOTYPE(void test_zdeath, (void)); +_PROTOTYPE(void test_syscall_child, (void)); +_PROTOTYPE(void test_syscall, (void)); +_PROTOTYPE(void test_tracefork_child, (void)); +_PROTOTYPE(void test_tracefork, (void)); +_PROTOTYPE(void altexec, (int setflag, int *traps, int *stop)); +_PROTOTYPE(void test_altexec, (void)); +_PROTOTYPE(void test_noaltexec, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +static char *executable; +static int errct = 0, subtest; +static int child = 0, attach; +static pid_t ppid; +static int pfd[4]; +static int sigs, caught; + +int main(argc, argv) +int argc; +char **argv; +{ + int i, m = 0xFFFF, n = 0xF; + + if (strcmp(argv[0], "DO CHECK") == 0) { + exit(42); + } + + printf("Test 42 "); + fflush(stdout); + + executable = argv[0]; + + if (argc >= 2) m = atoi(argv[1]); + if (argc >= 3) n = atoi(argv[2]); + + for (i = 0; i < ITERATIONS; i++) { + if (n & 001) test(m, 0); + if (n & 002) test(m, 1); + if (n & 004) test(m, 2); + if (n & 010) test(m, 3); + } + + quit(); + return(-1); /* impossible */ +} + +void test(m, a) +int m; +int a; +{ + attach = a; + + if (m & 0000001) test_wait(); + if (m & 0000002) test_exec(); + if (m & 0000004) test_step(); + if (m & 0000010) test_sig(); + if (m & 0000020) test_exit(); + if (m & 0000040) test_term(); + if (m & 0000100) test_catch(); + if (m & 0000200) test_kill(); + if (m & 0000400) test_attach(); + if (m & 0001000) test_detach(); + if (m & 0002000) test_death(); + if (m & 0004000) test_zdeath(); + if (m & 0010000) test_syscall(); + if (m & 0020000) test_tracefork(); + if (m & 0040000) test_altexec(); + if (m & 0100000) test_noaltexec(); +} + +pid_t traced_fork(c) +_PROTOTYPE(void (*c), (void)); +{ + pid_t pid; + int r, status; + + if (pipe(pfd) != 0) e(200); + if (pipe(&pfd[2]) != 0) e(201); + + switch (attach) { + case 0: /* let child volunteer to be traced */ + pid = fork(); + + if (pid < 0) e(202); + + if (pid == 0) { + child = 1; + + if (ptrace(T_OK, 0, 0, 0) != 0) e(203); + + WRITE(0); + + c(); + + e(204); + } + + if (READ() != 0) e(205); + + break; + + case 1: /* attach to child process */ + pid = fork(); + + if (pid < 0) e(206); + + if (pid == 0) { + child = 1; + + if (READ() != 0) e(207); + + c(); + + e(208); + } + + if (ptrace(T_ATTACH, pid, 0, 0) != 0) e(209); + + if (waitpid(pid, &status, 0) != pid) e(210); + if (!_WIFSTOPPED(status)) e(211); + if (WSTOPSIG(status) != SIGSTOP) e(212); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(213); + + WRITE(0); + + break; + + case 2: /* attach to non-child process */ + ppid = fork(); + + if (ppid < 0) e(214); + + if (ppid == 0) { + pid = fork(); + + if (pid < 0) exit(215); + + if (pid == 0) { + child = 1; + + if (READ() != 0) e(216); + + c(); + + e(217); + } + + child = 1; + + WRITE(pid); + + if (waitpid(pid, &status, 0) != pid) e(218); + if (_WIFSTOPPED(status)) e(219); + if (_WIFEXITED(status) && (r = WEXITSTATUS(status)) != 42) e(r); + + exit(0); + } + + pid = READ(); + + if (ptrace(T_ATTACH, pid, 0, 0) != 0) e(220); + + if (waitpid(pid, &status, 0) != pid) e(221); + if (!_WIFSTOPPED(status)) e(222); + if (WSTOPSIG(status) != SIGSTOP) e(223); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(224); + + WRITE(0); + + break; + + case 3: /* attach by forking from child */ + ppid = fork(); + + if (ppid < 0) e(225); + + if (ppid == 0) { + child = 1; + + if (ptrace(T_OK, 0, 0, 0) != 0) e(226); + + WRITE(0); + + if (READ() != 0) e(227); + + pid = fork(); + + if (pid < 0) e(228); + + if (pid == 0) { + c(); + + e(229); + } + + WRITE(pid); + + if (waitpid(pid, &status, 0) != pid) e(230); + if (_WIFSTOPPED(status)) e(231); + if (_WIFEXITED(status) && (r = WEXITSTATUS(status)) != 42) e(r); + + exit(0); + } + + if (READ() != 0) e(232); + + if (kill(ppid, SIGSTOP) != 0) e(233); + + if (waitpid(ppid, &status, 0) != ppid) e(234); + if (!_WIFSTOPPED(status)) e(235); + if (WSTOPSIG(status) != SIGSTOP) e(236); + + if (ptrace(T_SETOPT, ppid, 0, TO_TRACEFORK) != 0) e(237); + + if (ptrace(T_RESUME, ppid, 0, 0) != 0) e(238); + + WRITE(0); + + pid = READ(); + + if (waitpid(pid, &status, 0) != pid) e(239); + if (!_WIFSTOPPED(status)) e(240); + if (WSTOPSIG(status) != SIGSTOP) e(241); + + if (ptrace(T_SETOPT, pid, 0, 0) != 0) e(242); + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(243); + + detach_running(ppid); + + break; + } + + return pid; +} + +pid_t traced_pfork(c) +_PROTOTYPE(void (*c), (void)); +{ + pid_t pid; + + if (pipe(pfd) != 0) e(300); + if (pipe(&pfd[2]) != 0) e(301); + + pid = fork(); + + if (pid < 0) e(302); + + if (pid == 0) { + child = 1; + + c(); + + e(303); + } + + return pid; +} + +void WRITE(value) +int value; +{ + if (write(pfd[child*2+1], &value, sizeof(value)) != sizeof(value)) e(400); +} + +int READ() +{ + int value; + + if (read(pfd[2-child*2], &value, sizeof(value)) != sizeof(value)) e(401); + + return value; +} + +void traced_wait() +{ + int r, status; + + if (attach == 2) { + if (waitpid(ppid, &status, 0) != ppid) e(500); + if (!_WIFEXITED(status)) e(501); + if ((r = WEXITSTATUS(status)) != 0) e(r); + } + else { + /* Quick hack to clean up detached children */ + waitpid(-1, NULL, WNOHANG); + } + + close(pfd[0]); + close(pfd[1]); + close(pfd[2]); + close(pfd[3]); +} + +void detach_running(pid) +pid_t pid; +{ +/* Detach from a process that is not already stopped. This is the way to do it. + * We have to stop the child in order to detach from it, but as the child may + * have other signals pending for the tracer, we cannot assume we get our own + * signal back immediately. However, because we know that the kill is instant + * and resuming with pending signals will only stop the process immediately + * again, we can use T_RESUME for all the signals until we get our own signal, + * and then detach. A complicating factor is that anywhere during this + * procedure, the child may die (e.g. by getting a SIGKILL). In our tests, this + * will not happen. + */ + int status; + + if (kill(pid, SIGSTOP) != 0) e(600); + + if (waitpid(pid, &status, 0) != pid) e(601); + + while (_WIFSTOPPED(status)) { + if (WSTOPSIG(status) == SIGSTOP) { + if (ptrace(T_DETACH, pid, 0, 0) != 0) e(602); + + return; + } + + if (ptrace(T_RESUME, pid, 0, WSTOPSIG(status)) != 0) e(603); + + if (waitpid(pid, &status, 0) != pid) e(604); + } + + /* Apparently the process exited. */ + if (!_WIFEXITED(status) && !_WIFSIGNALED(status)) e(605); + + /* In our tests, that should not happen. */ + e(606); +} + +void dummy_handler(sig) +int sig; +{ +} + +void exit_handler(sig) +int sig; +{ + exit(42); +} + +void count_handler(sig) +int sig; +{ + sigs++; +} + +void catch_handler(sig) +int sig; +{ + sigset_t set; + int bit; + + switch (sig) { + case SIGUSR1: bit = 1; break; + case SIGUSR2: bit = 2; break; + case SIGTERM: bit = 4; break; + default: e(100); + } + + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + + if (caught & bit) e(101); + caught |= bit; +} + +void test_wait_child() +{ + exit(42); +} + +void test_wait() +{ + pid_t pid; + int status; + + subtest = 1; + + pid = traced_fork(test_wait_child); + + if (waitpid(pid, &status, 0) != pid) e(1); + if (!_WIFEXITED(status)) e(2); + if (WEXITSTATUS(status) != 42) e(3); + + traced_wait(); +} + +void test_exec_child() +{ + if (READ() != 0) e(100); + + execl(executable, "DO CHECK", NULL); + + e(101); +} + +void test_exec() +{ + pid_t pid; + int r, status; + + subtest = 2; + + pid = traced_fork(test_exec_child); + + WRITE(0); + + /* An exec() should result in a trap signal. */ + if (waitpid(pid, &status, 0) != pid) e(1); + if (!_WIFSTOPPED(status)) e(2); + if (WSTOPSIG(status) != SIGTRAP) e(3); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(4); + + if (waitpid(pid, &status, 0) != pid) e(5); + if (!_WIFEXITED(status)) e(6); + if ((r = WEXITSTATUS(status)) != 42) e(r); + + traced_wait(); +} + +void test_step_child() +{ + sigset_t set; + + signal(SIGUSR1, SIG_IGN); + + WRITE(0); + + if (READ() != 0) e(100); + + /* It must not be possible for the child to stop the single-step signal. */ + signal(SIGTRAP, SIG_IGN); + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + + exit(42); +} + +void test_step() +{ + pid_t pid; + int r, status, count; + + subtest = 3; + + pid = traced_fork(test_step_child); + + if (READ() != 0) e(1); + + /* While the child is running, neither waitpid() nor ptrace() should work. */ + if (waitpid(pid, &status, WNOHANG) != 0) e(2); + if (ptrace(T_RESUME, pid, 0, 0) != -1) e(3); + if (errno != EBUSY) e(4); + + if (kill(pid, SIGUSR1) != 0) e(5); + + WRITE(0); + + /* A kill() signal (other than SIGKILL) should be delivered to the tracer. */ + if (waitpid(pid, &status, 0) != pid) e(6); + if (!_WIFSTOPPED(status)) e(7); + if (WSTOPSIG(status) != SIGUSR1) e(8); + + /* ptrace(T_STEP) should result in instruction-wise progress. */ + for (count = 0; ; count++) { + if (ptrace(T_STEP, pid, 0, 0) != 0) e(9); + + if (waitpid(pid, &status, 0) != pid) e(10); + if (_WIFEXITED(status)) break; + if (!_WIFSTOPPED(status)) e(11); + if (WSTOPSIG(status) != SIGTRAP) e(12); + } + + if ((r = WEXITSTATUS(status)) != 42) e(r); + + if (count < 10) e(13); /* in practice: hundreds */ + + traced_wait(); +} + +void test_sig_child() +{ + signal(SIGUSR1, exit_handler); + + if (READ() != 0) e(100); + + pause(); + + e(101); +} + +void test_sig() +{ + pid_t pid; + int r, sig, status; + + subtest = 4; + + pid = traced_fork(test_sig_child); + + WRITE(0); + + /* allow the child to enter the pause */ + sleep(1); + + if (kill(pid, SIGUSR1) != 0) e(1); + if (kill(pid, SIGUSR2) != 0) e(2); + + /* All signals should arrive at the tracer, although in "random" order. */ + if (waitpid(pid, &status, 0) != pid) e(3); + if (!_WIFSTOPPED(status)) e(4); + if (WSTOPSIG(status) != SIGUSR1 && WSTOPSIG(status) != SIGUSR2) e(5); + + /* The tracer should see kills arriving while the tracee is stopped. */ + if (kill(pid, WSTOPSIG(status)) != 0) e(6); + + if (waitpid(pid, &status, WNOHANG) != pid) e(7); + if (!_WIFSTOPPED(status)) e(8); + if (WSTOPSIG(status) != SIGUSR1 && WSTOPSIG(status) != SIGUSR2) e(9); + sig = (WSTOPSIG(status) == SIGUSR1) ? SIGUSR2 : SIGUSR1; + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(10); + + if (waitpid(pid, &status, 0) != pid) e(11); + if (!_WIFSTOPPED(status)) e(12); + if (WSTOPSIG(status) != sig) e(13); + + if (waitpid(pid, &status, WNOHANG) != 0) e(14); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(15); + + /* Ignored signals passed via ptrace() should be ignored. */ + if (kill(pid, SIGUSR1) != 0) e(16); + + if (waitpid(pid, &status, 0) != pid) e(17); + if (!_WIFSTOPPED(status)) e(18); + if (WSTOPSIG(status) != SIGUSR1) e(19); + + if (ptrace(T_RESUME, pid, 0, SIGCHLD) != 0) e(20); + + /* if the pause has been aborted (shouldn't happen!), let the child exit */ + sleep(1); + + if (waitpid(pid, &status, WNOHANG) != 0) e(21); + + /* Caught signals passed via ptrace() should invoke their signal handlers. */ + if (kill(pid, SIGUSR1) != 0) e(22); + + if (waitpid(pid, &status, 0) != pid) e(23); + if (!_WIFSTOPPED(status)) e(24); + if (WSTOPSIG(status) != SIGUSR1) e(25); + + if (ptrace(T_RESUME, pid, 0, SIGUSR1) != 0) e(26); + + if (waitpid(pid, &status, 0) != pid) e(27); + if (!_WIFEXITED(status)) e(28); + if ((r = WEXITSTATUS(status)) != 42) e(29); + + traced_wait(); +} + +void test_exit_child() +{ + WRITE(0); + + for(;;); +} + +void test_exit() +{ + pid_t pid; + int r, status; + + subtest = 5; + + pid = traced_fork(test_exit_child); + + if (READ() != 0) e(1); + + sleep(1); + + if (kill(pid, SIGSTOP) != 0) e(2); + + if (waitpid(pid, &status, 0) != pid) e(3); + if (!_WIFSTOPPED(status)) e(4); + if (WSTOPSIG(status) != SIGSTOP) e(5); + + /* There should be no more signals pending for the tracer now. */ + if (waitpid(pid, &status, WNOHANG) != 0) e(6); + + /* ptrace(T_EXIT) should terminate the process with the given exit value. */ + if (ptrace(T_EXIT, pid, 0, 42) != 0) e(7); + + if (waitpid(pid, &status, 0) != pid) e(8); + if (!_WIFEXITED(status)) e(9); + if ((r = WEXITSTATUS(status)) != 42) e(r); + + traced_wait(); +} + +void test_term_child() +{ + signal(SIGUSR2, dummy_handler); + + WRITE(0); + + pause(); + + e(100); +} + +void test_term() +{ + pid_t pid; + int status; + + subtest = 6; + + pid = traced_fork(test_term_child); + + if (READ() != 0) e(1); + + /* If the first of two signals terminates the traced child, the second signal + * may or may not be delivered to the tracer - this is merely a policy issue. + * However, nothing unexpected should happen. + */ + if (kill(pid, SIGUSR1) != 0) e(2); + if (kill(pid, SIGUSR2) != 0) e(3); + + if (waitpid(pid, &status, 0) != pid) e(4); + if (!_WIFSTOPPED(status)) e(5); + + if (ptrace(T_RESUME, pid, 0, SIGUSR1) != 0) e(6); + + if (waitpid(pid, &status, 0) != pid) e(7); + + if (_WIFSTOPPED(status)) { + if (ptrace(T_RESUME, pid, 0, SIGUSR1) != 0) e(8); + + if (waitpid(pid, &status, 0) != pid) e(9); + } + + if (!_WIFSIGNALED(status)) e(10); + if (WTERMSIG(status) != SIGUSR1) e(11); + + traced_wait(); +} + +void test_catch_child() +{ + struct sigaction sa; + sigset_t set, oset; + + sa.sa_handler = catch_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, &oset); + + caught = 0; + + WRITE(0); + + while (caught != 7) sigsuspend(&oset); + + exit(42); +} + +void test_catch() +{ + pid_t pid; + int r, sig, status; + + subtest = 7; + + pid = traced_fork(test_catch_child); + + if (READ() != 0) e(1); + + if (kill(pid, SIGUSR1) != 0) e(2); + if (kill(pid, SIGUSR2) != 0) e(3); + + if (waitpid(pid, &status, 0) != pid) e(4); + if (!_WIFSTOPPED(status)) e(5); + if (WSTOPSIG(status) != SIGUSR1 && WSTOPSIG(status) != SIGUSR2) e(6); + sig = (WSTOPSIG(status) == SIGUSR1) ? SIGUSR2 : SIGUSR1; + + if (ptrace(T_RESUME, pid, 0, WSTOPSIG(status)) != 0) e(7); + + if (kill(pid, SIGTERM) != 0) e(8); + + if (waitpid(pid, &status, 0) != pid) e(9); + if (!_WIFSTOPPED(status)) e(10); + if (WSTOPSIG(status) != sig && WSTOPSIG(status) != SIGTERM) e(11); + if (WSTOPSIG(status) == sig) sig = SIGTERM; + + if (ptrace(T_RESUME, pid, 0, WSTOPSIG(status)) != 0) e(12); + + if (kill(pid, SIGBUS) != 0) e(13); + + if (waitpid(pid, &status, 0) != pid) e(14); + if (!_WIFSTOPPED(status)) e(15); + if (WSTOPSIG(status) != sig && WSTOPSIG(status) != SIGBUS) e(16); + + if (ptrace(T_RESUME, pid, 0, sig) != 0) e(17); + + if (WSTOPSIG(status) == sig) sig = SIGBUS; + + if (waitpid(pid, &status, 0) != pid) e(18); + if (!_WIFSTOPPED(status)) e(19); + if (WSTOPSIG(status) != sig) e(20); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(21); + + if (waitpid(pid, &status, 0) != pid) e(22); + if (!_WIFEXITED(status)) e(23); + if ((r = WEXITSTATUS(status)) != 42) e(r); + + traced_wait(); +} + +void test_kill_child() +{ + sigset_t set; + + signal(SIGKILL, SIG_IGN); + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + + WRITE(0); + + pause(); + + e(100); +} + +void test_kill() +{ + pid_t pid; + int status; + + subtest = 8; + + pid = traced_fork(test_kill_child); + + if (READ() != 0) e(1); + + /* SIGKILL must be unstoppable in every way. */ + if (kill(pid, SIGKILL) != 0) e(2); + + if (waitpid(pid, &status, 0) != pid) e(3); + if (!_WIFSIGNALED(status)) e(4); + if (WTERMSIG(status) != SIGKILL) e(5); + + /* After termination, the child must no longer be visible to the tracer. */ + if (waitpid(pid, &status, WNOHANG) != -1) e(6); + if (errno != ECHILD) e(7); + + traced_wait(); +} + +void test_attach_child() +{ + if (ptrace(T_OK, 0, 0, 0) != -1) e(100); + if (errno != EBUSY) e(101); + + WRITE(0); + + if (READ() != 0) e(102); + + exit(42); +} + +void test_attach() +{ + pid_t pid; + int r, status; + + subtest = 9; + + /* Attaching to kernel processes is not allowed. */ + if (ptrace(T_ATTACH, -1, 0, 0) != -1) e(1); + if (errno != ESRCH) e(2); + + /* Attaching to self is not allowed. */ + if (ptrace(T_ATTACH, getpid(), 0, 0) != -1) e(3); + if (errno != EPERM) e(4); + + /* Attaching to PM is not allowed. */ + if (ptrace(T_ATTACH, 0, 0, 0) != -1) e(5); + if (errno != EPERM) e(6); + + pid = traced_fork(test_attach_child); + + /* Attaching more than once is not allowed. */ + if (ptrace(T_ATTACH, pid, 0, 0) != -1) e(7); + if (errno != EBUSY) e(8); + + if (READ() != 0) e(9); + + /* Detaching a running child should not succeed. */ + if (ptrace(T_DETACH, pid, 0, 0) == 0) e(10); + if (errno != EBUSY) e(11); + + detach_running(pid); + + WRITE(0); + + traced_wait(); +} + +void test_detach_child() +{ + struct sigaction sa; + sigset_t set, sset, oset; + + sa.sa_handler = catch_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, &oset); + + sigfillset(&sset); + sigdelset(&sset, SIGUSR1); + + caught = 0; + + WRITE(0); + + if (sigsuspend(&sset) != -1) e(102); + if (errno != EINTR) e(103); + + if (caught != 1) e(104); + + if (READ() != 0) e(105); + + while (caught != 7) sigsuspend(&oset); + + exit(42); +} + +void test_detach() +{ + pid_t pid; + int r, status; + + /* Can't use traced_fork(), so simplify a bit */ + if (attach != 0) return; + + subtest = 10; + + pid = traced_pfork(test_detach_child); + + if (READ() != 0) e(1); + + /* The tracer should not see signals sent to the process before attaching. */ + if (kill(pid, SIGUSR2) != 0) e(2); + + if (ptrace(T_ATTACH, pid, 0, 0) != 0) e(3); + + if (waitpid(pid, &status, 0) != pid) e(4); + if (!_WIFSTOPPED(status)) e(5); + if (WSTOPSIG(status) != SIGSTOP) e(6); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) e(7); + + if (kill(pid, SIGUSR1) != 0) e(8); + + if (waitpid(pid, &status, 0) != pid) e(9); + if (!_WIFSTOPPED(status)) e(10); + if (WSTOPSIG(status) != SIGUSR1) e(11); + + /* Signals pending at the tracer should be passed on after detaching. */ + if (kill(pid, SIGTERM) != 0) e(12); + + /* A signal may be passed with the detach request. */ + if (ptrace(T_DETACH, pid, 0, SIGUSR1) != 0) e(13); + + WRITE(0); + + if (waitpid(pid, &status, 0) != pid) e(14); + if (!_WIFEXITED(status)) e(15); + if ((r = WEXITSTATUS(status)) != 42) e(r); + + traced_wait(); +} + +void test_death_child() +{ + pid_t pid; + + pid = fork(); + + if (pid < 0) e(100); + + if (pid == 0) { + ptrace(T_OK, 0, 0, 0); + + WRITE(getpid()); + + for (;;) pause(); + } + + if (READ() != 0) e(101); + + kill(getpid(), SIGKILL); + + e(102); +} + +void test_death() +{ + pid_t pid, cpid; + int status; + + subtest = 11; + + pid = traced_fork(test_death_child); + + cpid = READ(); + + if (kill(cpid, 0) != 0) e(1); + + WRITE(0); + + if (waitpid(pid, &status, 0) != pid) e(2); + if (!_WIFSIGNALED(status)) e(3); + if (WTERMSIG(status) != SIGKILL) e(4); + + /* The children of killed tracers should be terminated. */ + while (kill(cpid, 0) == 0) sleep(1); + if (errno != ESRCH) e(5); + + traced_wait(); +} + +void test_zdeath_child() +{ + if (READ() != 0) e(100); + + exit(42); +} + +void test_zdeath() +{ + pid_t pid, tpid; + int r, status; + + /* Can't use traced_fork(), so simplify a bit */ + if (attach != 0) return; + + subtest = 12; + + pid = traced_pfork(test_zdeath_child); + + tpid = fork(); + + if (tpid < 0) e(1); + + if (tpid == 0) { + if (ptrace(T_ATTACH, pid, 0, 0) != 0) exit(101); + + if (waitpid(pid, &status, 0) != pid) exit(102); + if (!_WIFSTOPPED(status)) exit(103); + if (WSTOPSIG(status) != SIGSTOP) exit(104); + + if (ptrace(T_RESUME, pid, 0, 0) != 0) exit(105); + + WRITE(0); + + /* Unwaited-for traced zombies should be passed to their parent. */ + sleep(2); + + exit(84); + } + + sleep(1); + + /* However, that should only happen once the tracer has actually died. */ + if (waitpid(pid, &status, WNOHANG) != 0) e(2); + + if (waitpid(tpid, &status, 0) != tpid) e(3); + if (!_WIFEXITED(status)) e(4); + if ((r = WEXITSTATUS(status)) != 84) e(r); + + if (waitpid(pid, &status, 0) != pid) e(5); + if (!_WIFEXITED(status)) e(6); + if ((r = WEXITSTATUS(status)) != 42) e(r); + + traced_wait(); +} + +void test_syscall_child() +{ + signal(SIGUSR1, count_handler); + signal(SIGUSR2, count_handler); + + sigs = 0; + + WRITE(0); + + if (READ() != 0) e(100); + + /* Three calls (may fail) */ + setuid(0); + close(123); + getpid(); + + if (sigs != 2) e(101); + + exit(42); +} + +void test_syscall() +{ + pid_t pid; + int i, r, sig, status; + + subtest = 13; + + pid = traced_fork(test_syscall_child); + + if (READ() != 0) e(1); + + if (kill(pid, SIGSTOP) != 0) e(2); + + if (waitpid(pid, &status, 0) != pid) e(3); + if (!_WIFSTOPPED(status)) e(4); + if (WSTOPSIG(status) != SIGSTOP) e(5); + + WRITE(0); + + /* Upon resuming a first system call, no syscall leave event must be sent. */ + if (ptrace(T_SYSCALL, pid, 0, 0) != 0) e(6); + + if (waitpid(pid, &status, 0) != pid) e(7); + + for (i = 0; _WIFSTOPPED(status); i++) { + if (WSTOPSIG(status) != SIGTRAP) e(8); + + /* Signals passed via T_SYSCALL should arrive, on enter and exit. */ + if (i == 3) sig = SIGUSR1; + else if (i == 6) sig = SIGUSR2; + else sig = 0; + + if (ptrace(T_SYSCALL, pid, 0, sig) != 0) e(9); + + if (waitpid(pid, &status, 0) != pid) e(10); + } + + if (!_WIFEXITED(status)) e(11); + if ((r = WEXITSTATUS(status)) != 42) e(r); + + /* The number of events seen is deterministic but libc-dependent. */ + if (i < 10 || i > 100) e(12); + + /* The last system call event must be for entering exit(). */ + if (!(i % 2)) e(13); + + traced_wait(); +} + +void test_tracefork_child() +{ + pid_t pid; + + signal(SIGHUP, SIG_IGN); + + pid = setsid(); + + WRITE(pid); + + if (READ() != 0) e(100); + + if ((pid = fork()) < 0) e(101); + + exit(pid > 0 ? 42 : 84); +} + +void test_tracefork() +{ + pid_t pgrp, ppid, cpid, wpid; + int r, status, gotstop, ptraps, ctraps; + + subtest = 14; + + ppid = traced_fork(test_tracefork_child); + + if ((pgrp = READ()) <= 0) e(1); + + if (kill(ppid, SIGSTOP) != 0) e(2); + + if (waitpid(ppid, &status, 0) != ppid) e(3); + if (!_WIFSTOPPED(status)) e(4); + if (WSTOPSIG(status) != SIGSTOP) e(5); + + if (ptrace(T_SETOPT, ppid, 0, TO_TRACEFORK) != 0) e(6); + + WRITE(0); + + if (ptrace(T_SYSCALL, ppid, 0, 0) != 0) e(7); + + cpid = -1; + gotstop = -1; + + /* Count how many traps we get for parent and child, until they both exit. */ + for (ptraps = ctraps = 0; ppid || cpid; ) { + wpid = waitpid(-pgrp, &status, 0); + + if (wpid <= 0) e(8); + if (cpid < 0 && wpid != ppid) { + cpid = wpid; + gotstop = 0; + } + if (wpid != ppid && wpid != cpid) e(9); + + if (_WIFEXITED(status)) { + if (wpid == ppid) { + if ((r = WEXITSTATUS(status)) != 42) e(r); + ppid = 0; + } + else { + if ((r = WEXITSTATUS(status)) != 84) e(r); + cpid = 0; + } + } + else { + if (!_WIFSTOPPED(status)) e(10); + + switch (WSTOPSIG(status)) { + case SIGCHLD: + case SIGHUP: + break; + case SIGSTOP: + if (wpid != cpid) e(11); + if (gotstop) e(12); + gotstop = 1; + break; + case SIGTRAP: + if (wpid == ppid) ptraps++; + else ctraps++; + break; + default: + e(13); + } + + if (ptrace(T_SYSCALL, wpid, 0, 0) != 0) e(14); + } + } + + /* The parent should get an odd number of traps: the first one is a syscall + * enter trap (typically for the fork()), the last one is the syscall enter + * trap for its exit(). + */ + if (ptraps < 3) e(15); + if (!(ptraps % 2)) e(16); + + /* The child should get an even number of traps: the first one is a syscall + * leave trap from the fork(), the last one is the syscall enter trap for + * its exit(). + */ + if (ctraps < 2) e(17); + if (ctraps % 2) e(18); + + traced_wait(); +} + +void altexec(setflag, traps, stop) +int setflag; +int *traps; +int *stop; +{ + pid_t pid; + int r, status; + + pid = traced_fork(test_exec_child); + + if (kill(pid, SIGSTOP) != 0) e(1); + + if (waitpid(pid, &status, 0) != pid) e(2); + if (!_WIFSTOPPED(status)) e(3); + if (WSTOPSIG(status) != SIGSTOP) e(4); + + if (setflag && ptrace(T_SETOPT, pid, 0, TO_ALTEXEC) != 0) e(5); + + WRITE(0); + + if (ptrace(T_SYSCALL, pid, 0, 0) != 0) e(6); + + *traps = 0; + *stop = -1; + + for (;;) { + if (waitpid(pid, &status, 0) != pid) e(7); + + if (_WIFEXITED(status)) break; + + if (!_WIFSTOPPED(status)) e(8); + + switch (WSTOPSIG(status)) { + case SIGTRAP: + (*traps)++; + break; + case SIGSTOP: + if (*stop >= 0) e(9); + *stop = *traps; + break; + default: + e(10); + } + + if (ptrace(T_SYSCALL, pid, 0, 0) != 0) e(11); + } + + if ((r = WEXITSTATUS(status)) != 42) e(r); + + traced_wait(); +} + +void test_altexec() +{ + int traps, stop; + + subtest = 15; + + altexec(1, &traps, &stop); + + /* The exec causes a SIGSTOP. This gives us an odd number of traps: a pair + * for each system call, plus one for the final exit(). The stop must have + * taken place after a syscall enter event, i.e. must be odd as well. + */ + if (traps < 3) e(12); + if (!(traps % 2)) e(13); + if (stop < 0) e(14); + if (!(stop % 2)) e(15); +} + +void test_noaltexec() +{ + int traps, stop; + + subtest = 16; + + altexec(0, &traps, &stop); + + /* The exec does not cause a SIGSTOP. This gives us an even number of traps; + * as above, but plus the exec()'s extra SIGTRAP. This trap is + * indistinguishable from a syscall trap, especially when considering failed + * exec() calls and immediately following signal handler invocations. + */ + if (traps < 4) e(12); + if (traps % 2) e(13); + if (stop >= 0) e(14); +} + +void e(n) +int n; +{ + + if (child) exit(n); + + printf("Subtest %d, attach type %d, error %d, errno %d: %s\n", + subtest, attach, n, errno, strerror(errno)); + + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + exit(1); + } +} + +void quit() +{ + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +}