]> Zhao Yanbai Git Server - minix.git/commitdiff
Merge of David's ptrace branch. Summary:
authorDavid van Moolenbroek <david@minix3.org>
Wed, 30 Sep 2009 09:57:22 +0000 (09:57 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 30 Sep 2009 09:57:22 +0000 (09:57 +0000)
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

70 files changed:
drivers/audio/framework/audio_fw.c
drivers/dp8390/dp8390.c
drivers/dpeth/dp.c
drivers/floppy/floppy.c
drivers/fxp/fxp.c
drivers/lance/lance.c
drivers/libdriver/driver.c
drivers/libdriver/driver.h
drivers/libdriver_asyn/driver.c
drivers/libdriver_asyn/driver.h
drivers/log/diag.c
drivers/log/log.c
drivers/log/log.h
drivers/orinoco/orinoco.c
drivers/printer/printer.c
drivers/rtl8139/rtl8139.c
include/minix/com.h
include/minix/ipc.h
include/minix/syslib.h
include/minix/sysutil.h
include/minix/vm.h
include/signal.h
include/sys/ptrace.h
include/sys/resource.h
kernel/arch/i386/exception.c
kernel/arch/i386/mpx386.s
kernel/arch/i386/system.c
kernel/clock.c
kernel/config.h
kernel/debug.c
kernel/main.c
kernel/proc.c
kernel/proc.h
kernel/proto.h
kernel/system.c
kernel/system.h
kernel/system/Makefile
kernel/system/do_fork.c
kernel/system/do_nice.c
kernel/system/do_runctl.c [new file with mode: 0644]
kernel/system/do_sigsend.c
kernel/system/do_trace.c
lib/ansi/signal.c
lib/posix/_sigset.c
lib/syslib/Makefile.in
lib/syslib/sys_runctl.c [new file with mode: 0644]
lib/syslib/vm_fork.c
lib/sysutil/asynsend.c
man/man2/ptrace.2
servers/inet/mnx_eth.c
servers/is/dmp_kernel.c
servers/is/dmp_pm.c
servers/pm/const.h
servers/pm/exec.c
servers/pm/forkexit.c
servers/pm/getset.c
servers/pm/glo.h
servers/pm/main.c
servers/pm/misc.c
servers/pm/mproc.h
servers/pm/proto.h
servers/pm/signal.c
servers/pm/table.c
servers/pm/trace.c
servers/pm/utility.c
servers/rs/manager.c
servers/vfs/main.c
test/Makefile
test/run
test/test42.c [new file with mode: 0644]

index cebc5f9b3a088670985039156f82620faa00ddcf..70c7c1bf83ae76dc11439020e0865dd3ad073f7d 100755 (executable)
@@ -44,6 +44,7 @@
 
 #include "audio_fw.h"
 #include <sys/vm.h>
+#include <minix/endpoint.h>
 #include <minix/ds.h>
 #include <sys/vm_i386.h>
 
@@ -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);
index 5caa732821e38005bcbd571164ea8b967cd13ab8..cce2c0ab818850caf783de31c87cfab6fbda02c5 100644 (file)
@@ -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);
index efde4943f65e73e7a48c8652738b8a3887831ac8..afbb73861493eeea66497a7d0e99f25107c662fb 100644 (file)
@@ -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 */
index 1354cfd4c88a3fbde6d5f231acdda957d1b3f0a2..e9cd3619843e6ed4a8c8028ce0ee99eb55d811d1 100644 (file)
@@ -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); 
index f733aef5c6473397c33e804b9ea3145cbbd51c02..78ff9fbbc0191c688b7583a45494d6a693c1d901 100644 (file)
@@ -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);
 }
 
 /*===========================================================================*
index 3c8adea6f9da1e242092310535feb225d69f5d8f..ec94bbf5bc823b1112da3be2bf6cceb596b0475b 100644 (file)
@@ -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;i<EC_PORT_NR_MAX;++i)
                              {
@@ -348,7 +344,16 @@ void main( int argc, char **argv )
                              }
                              break;
                      case PM_PROC_NR:
+                     {
+                             sigset_t set;
+
+                             if (getsigset(&set) != 0) break;
+
+                             if (sigismember(&set, SIGTERM))
+                                     lance_stop();
+
                              break;
+                     }
                      default:
                              panic( "lance", "illegal notify source", m.m_source);
              }
@@ -469,7 +474,7 @@ static void lance_stop()
    printf("LANCE driver stopped.\n");
 #endif
 
-   sys_exit( 0 );
+   exit( 0 );
 }
 
 
index 079444614011c2c1270ae517803615b9fe9e1604..fcb68c072280e51aa9743776c5ff0bdc374ee05b 100644 (file)
@@ -67,6 +67,7 @@ struct driver *dp;    /* Device dependent entry points. */
 
   int r, proc_nr;
   message mess;
+  sigset_t set;
 
   system_hz = sys_hz();
 
@@ -104,8 +105,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);     
@@ -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. */
 }
index 655f00cd6298200dfec541107fe4e78e21ba32c3..7ee9fa4a8062bf63ed8454cd5714aee2669d84cb 100644 (file)
@@ -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) );
index 8adf5f318fc4dfe93244682f43b6928b20a7e8d3..ad11c55fb038d49dc7f670643b362cf98cec9639 100644 (file)
@@ -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. */
 }
index 0737f1748139348a0549d06df2fdd4c8cb95c62d..7e9423180d45273f1599460bcce2f639210ff8f7 100644 (file)
@@ -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) );
index 658950c0415a1dde3b08efdba61b11acfb8c1c75..5994d0e1d163c697d5ad72476ca21ad334cf7361 100644 (file)
@@ -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;
index c23b94843ee6f94a2cbd07b21903eebc1142f1b4..13ef2d63ac8bf52c2b59d0fc2306df6b9c5be429 100644 (file)
@@ -9,6 +9,7 @@
 #include "log.h"
 #include <sys/time.h>
 #include <sys/select.h>
+#include <minix/endpoint.h>
 
 #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;
index 7b5001eee77a320257fac2ab512d6d33ace9b18f..e064cf7e1926d220c7627c8bd39a7aeb4a211091 100644 (file)
@@ -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)                              );
 
index 2822875527be4638d4a8ecb8a9d82127693d815b..8d8268a1107501ec440600ac9e74f1e47c53387f 100755 (executable)
@@ -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);
 }
 
 /*****************************************************************************
index 4dc9815f41497cf83ff44cadaa78ebeceaf54a3a..00ec2e469ed12b8ac252c237a6ebcba56798ff7a 100644 (file)
@@ -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)) {
index 15e974e072c47dd68a0c50dba9180cd00f73df1e..e736972ea3e0034a0a6a8d13b8c7a1dbf6fe4d9e 100755 (executable)
@@ -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);
index b4d24d8f24eb48221a341425987e34ff62cd59a3..f507d2f993e757ce13aaafeab5566049549a75f6 100755 (executable)
 /* 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                       *
 #  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)
 #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                     *
  *===========================================================================*/
 
 #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) */
-
-/* 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 */
+/*===========================================================================*
+ *                Messages used between PM and VFS                          *
+ *===========================================================================*/
+
+#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 */
index 37711da2d19c04cbb4e4cd1fbe91a9306441206c..811dad192d31fbea4e47a3efd69039e9c9a9f939 100644 (file)
@@ -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
index d4772e59a1378b68e075732f78439a149490168c..b3f6d0cad1de1311840b241329d0c4df763b4bad 100755 (executable)
@@ -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));
index e0b2987b8897d58bd33b9f02aa91752d486e3eb1..7318bd94dc9d76b562d068008f0bb8fd0cb9db6a 100644 (file)
@@ -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__); }
 
index 7ac40f9c5f0f90249026cb498b7c83d945bbe697..ef7692fa3b74594641ece7814f83ef69befbf060 100755 (executable)
@@ -7,7 +7,7 @@
 #include <minix/endpoint.h>
 
 _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));
index a52c7742043b4e5df0a9986b265678d277d321b6..2b47fc26993c38b3725d0f12c5e5f370ca8ab238 100755 (executable)
@@ -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 */
index ccb4503129792dcdc286a8aa078f7624928ea20a..e9694dd9eaa3d637fa589c9c74cc805cfac519b1 100755 (executable)
@@ -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 */
 #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)
                                 * 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 <ansi.h>
index 1b14af4e2bfdfab4405e11fd6fb6a56b044c64e1..6dc5bd8696e39ea8e5df430cdd01ba86d5bd9d81 100755 (executable)
@@ -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
index 7e54f2745a533cb8d96d03e99eb027e2062e2f96..995e9601b4a66f60d7458dfc15f58351ff15d2a9 100755 (executable)
@@ -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;
   }
index 3c2248c48ed0f96705eee3123ccecc84c790fab3..742891545750b790b4e9e7db041ef940a96289f3 100755 (executable)
@@ -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
index 80a7fb9a3227cd538bfea067a3b86738871c664c..b8bb73f227871bce32fefd0cde17c88ec7ccdf39 100644 (file)
@@ -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);
+}
index 0be366e4b18f9d37f32f798e5dbcae8f236ba175..d9aa3ba2aaba1c6fb316c27bb3953b664a8bd068 100755 (executable)
@@ -34,6 +34,7 @@
 #include "proc.h"
 #include <signal.h>
 #include <minix/com.h>
+#include <minix/endpoint.h>
 #include <minix/portio.h>
 
 /* 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);
-    }
+       }
   }
 }
 
index 07b9c8770047e030e0c64b4d6092502c550d0129..f1a0fcbf403881268a13ac16b3399eb642b2f2b8 100644 (file)
@@ -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
index 7324d4857aaa95438677a7e1b970fb0baaf53ced..e710f6a21a893b2d8461b0bb2fc030f705b90c92 100644 (file)
@@ -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);
index f9c710ab76e6788cd7ef3a6f49e2ba2e89fa078a..885dddb94a0b8e6d2e2ff8ef4aced4681d5c6fb3 100755 (executable)
@@ -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);
   }
index 24289c5e7c8988ba64c09fbdec557a885fb96930..5ef4555e9fde898eee72f092b58f9bf542ae85ee 100755 (executable)
@@ -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
index ac07514b04468354f264f8db948df8d8a1d1117a..8debbb88fe8a8a90babcbda0a531357cf231fc64 100755 (executable)
@@ -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 
index 929950585813574d96e21e1612075c1325dc6edd..769040f7b0bfc8bca5929802b29f09347fa19c03 100755 (executable)
@@ -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 */
index c0b8afdae6792acccf657c8fb6f90bdf885307fa..3e7fff8e9e476c5d45106a064f71ca329fc914a9 100755 (executable)
@@ -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
 
 /*===========================================================================*
index d35c7a474ddad94db61733ed23e48fc345f9466a..06aafb4feb88646cedc9f5a038daa2daedb82ebf 100644 (file)
@@ -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)
index a93e64f532d55f92bef899e0ce0da70c0a4a777a..f0e4e378986f297c4f63a4a5106b13f635d3e647 100644 (file)
@@ -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
 
index aa94a3418fceaf89f8311dbaf29a3930d015a9aa..3e8fc83c39032424446e2defe379749ce09bccf7 100644 (file)
@@ -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;
 
index e31403fc3c1e1f9365ee12a8ab4c498752387810..dd9d4447df8f9a95d26e0510dede56bd9a3a2eeb 100644 (file)
@@ -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.
-       */
-      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);
-
-      return(OK);
-  }
-  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.
+   */
+  if (pri < PRIO_MIN || pri > PRIO_MAX) return(EINVAL);
+
+  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, SYS_LOCK);
+  rp->p_max_priority = rp->p_priority = new_q;
+  RTS_LOCK_UNSET(rp, SYS_LOCK);
+
+  return(OK);
 }
 
 #endif /* USE_NICE */
diff --git a/kernel/system/do_runctl.c b/kernel/system/do_runctl.c
new file mode 100644 (file)
index 0000000..5b24e25
--- /dev/null
@@ -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 <minix/type.h>
+
+#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 */
+
index 879a05ad099265b460d662566c201bc98a8f3c00..e710b1c675d2c25f12f89eb8c26b04c2a64487a8 100644 (file)
@@ -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");
index b13b66b3c3eadfa7d7f4f5f187bbc788a33b671e..9511c0f993315fffcfacec458dc3aaab8e67eb4d 100644 (file)
@@ -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);
 }
index a8cdca1fe64e15ac2eeb6039c11530084220d759..bae9b529079b2818c2b5ff60915f02e91e03153e 100755 (executable)
@@ -2,7 +2,7 @@
 
 #include <lib.h>
 #define sigaction      _sigaction
-#define sigemptyset    _sigemptyset
+#define _SYSTEM 1
 #include <signal.h>
 
 PUBLIC sighandler_t signal(sig, disp)
index d199546f1318aa964559156737478225b89db1ba..91226de5f27a1834db6742758620b837fc0a2108 100755 (executable)
@@ -1,12 +1,7 @@
 #include <lib.h>
-/* 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
index 3fc7305e346ca9f64553dd248646c222474c5a11..3c27938567287da5ad01eb54ea75a05cf6a169b7 100644 (file)
@@ -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 (file)
index 0000000..7c31eef
--- /dev/null
@@ -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));
+}
index c67e2cb707984fef4206ce8c8c208e566bd4f050..7a433b697890de26969d243efb3ca690fe2234d5 100644 (file)
@@ -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;
index f7607618dae3ec4f26f4d8990d3e7124ae8bb2e2..19bab8160bb095d129bae4d5e0fccc678efdc08b 100644 (file)
 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.
                                                 */
index 35c5752b02813545877cbfa1576ff89c0bcd8652..b4b3766c39b35599cc095fb6f05fdff7035baee8 100644 (file)
@@ -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 <sys/types.h>
-#include <sys/signal.h>
 #include <sys/ptrace.h>
 
-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.
-.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.
+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 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.
+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
-The error indication, \-1, is a legitimate function value;
-.BR errno ,
-(see
-.BR intro (2)),
-can be used to disambiguate.
+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 should be possible to stop a process on occurrence of a system
-call;
-in this way a completely controlled environment could
-be provided.
+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 kill (2),
+.BR mdb (1)
+.SH AUTHOR
+Manual page written by David van Moolenbroek <dcvmoole@cs.vu.nl>
index 88d03068117e6d061087960f1396eea37620232b..210e59dc81f2c01155b34d104194dd457ef44630 100644 (file)
@@ -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; i++)
-                       msgtable[i].flags= AMF_EMPTY;
-       }
-
-       /* Find slot in table */
-       for (i= 0; i<msgtable_n; i++)
-       {
-               flags= msgtable[i].flags;
-               if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE))
-               {
-                       if (msgtable[i].result != OK)
-                       {
-                               printf(
-                       "asynsend: found completed entry %d with error %d\n",
-                                       i, msgtable[i].result);
-                       }
-                       break;
-               }
-               if (flags == AMF_EMPTY)
-                       break;
-       }
-       if (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 $
  */
index 3f01f5112701ba0a4068f1686b64f00c4aa31c07..e5c2cc3dd768b04885a64628c5d5267b6636fa0a 100644 (file)
@@ -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' : '-';
index a3e57c824cdb0fce6ce3c698ba871223dc85fa9d..2c8071901578be600e14bc095d8caa76256de065 100644 (file)
@@ -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; i<NR_PROCS; i++) {
        mp = &mproc[i];
        if (mp->mp_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)); 
index cf75e72c5cac853c308692f966b774db5506684a..2fa9bdad1b33d98ffa64e6f02cb6c8bc4a3f3ccd 100644 (file)
@@ -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)
index 927d1cfc4c2d8cf92684652b1521484045aa29c4..7113eefcb05c2ffc71505964ec580e82f8d819d1 100644 (file)
@@ -27,6 +27,7 @@
 #include <a.out.h>
 #include <signal.h>
 #include <string.h>
+#include <sys/ptrace.h>
 #include "mproc.h"
 #include "param.h"
 
  *===========================================================================*/
 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);
 }
 
index 36fe0a36b8582021112ad23f1ca19ea4f3d96d7b..30b58fd316a826176ff59a4e9da03d8fcc62ad67 100644 (file)
@@ -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 <minix/callnr.h>
 #include <minix/com.h>
 #include <minix/vm.h>
+#include <sys/ptrace.h>
 #include <sys/resource.h>
 #include <signal.h>
 #include "mproc.h"
 #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);
-       }
+       panic(__FILE__, "exit_proc: FS died", r);
   }
-  else
+
+
+  /* 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)
   {
-       panic(__FILE__, "pm_exit: FS died", r);
+       /* 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);
   }
 
-  /* 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.
+  /* 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;
+
+       /* 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 */
+       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];
+
+       /* 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 (parent_waiting && right_child)
-       tell_parent(rmp);               /* tell parent */
-  else
-       sig_proc(p_mp, SIGCHLD);        /* send parent a "child died" signal */
+  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                                      *
  *===========================================================================*/
index 7b26a2482621808dba1875415657629f89511c14..dce214deaf31c30bcdeed43cedf2975e2dab991e 100644 (file)
@@ -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.
 #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,81 +45,76 @@ PUBLIC int do_getset()
                        rmp->mp_reply.reply_res3 = mproc[proc].mp_pid;
                break;
 
-       case SETEUID:
+       case GETPGRP:
+               r = rmp->mp_procgrp;
+               break;
+
+       default:
+               r = EINVAL;
+               break;  
+  }
+  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;
 
-               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;
+               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 SETEGID:
        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;
 
-               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;
+               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;
 
-               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;
+               m.m_type = PM_SETSID;
+               m.PM_PROC = rmp->mp_endpoint;
 
-       case GETPGRP:
-               r = rmp->mp_procgrp;
                break;
 
        default:
-               r = EINVAL;
-               break;  
+               return(EINVAL);
   }
-  return(r);
+
+  /* Send the request to FS */
+  tell_fs(rmp, &m);
+
+  /* Do not reply until FS has processed the request */
+  return(SUSPEND);
 }
index b9adae444d94ef68ae2c9bcba28f707c3ddc4a47..ed4f3d3269a8f4fe17725063c98b624e954fa173 100644 (file)
@@ -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];         
index 80de2170762dd3934770b12488b661409624d15e..ce6fd90589ae6f3ebfea8008fd518418e8350fd1 100644 (file)
@@ -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
@@ -405,298 +401,108 @@ void checkme(char *str, int line)
 }
 
 /*===========================================================================*
- *                             send_work                                    *
+ *                             handle_fs_reply                              *
  *===========================================================================*/
-PRIVATE void send_work()
+PRIVATE void handle_fs_reply()
 {
-       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;
+  struct mproc *rmp;
+  endpoint_t proc_e;
+  int r, proc_n;
 
-                       parent_p = rmp->mp_parent;
-                       parent_mp = &mproc[parent_p];
+  /* 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;
 
-                       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;
+       /* 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);
 
-                       /* FS does not reply */
-                       rmp->mp_fs_call= PM_IDLE;
+       return;
+  }
 
-                       break;
-               }
+  /* Get the process associated with this call */
+  proc_e = m_in.PM_PROC;
 
-               case PM_DUMPCORE:
-                       m.m_type= call;
-                       m.PM_CORE_PROC= rmp->mp_endpoint;
-                       /* XXX
-                       m.PM_CORE_SEGPTR= (char *)rmp->mp_seg;
-                       */
+  if (pm_isokendpt(proc_e, &proc_n) != OK) {
+       panic(__FILE__, "handle_fs_reply: got bad endpoint from FS", proc_e);
+  }
 
-                       /* Mark the process as busy */
-                       rmp->mp_fs_call= PM_BUSY;
+  rmp = &mproc[proc_n];
 
-                       break;
+  /* 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);
 
-               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);
-}
+  rmp->mp_flags &= ~FS_CALL;
 
-/*===========================================================================*
- *                             handle_fs_reply                              *
- *===========================================================================*/
-PRIVATE void handle_fs_reply(m_ptr)
-message *m_ptr;
-{
-       int r, proc_e, proc_n, s;
-       struct mproc *rmp;
+  if (rmp->mp_flags & UNPAUSED)
+       panic(__FILE__, "handle_fs_reply: UNPAUSED set on entry", call_nr);
 
-       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];
+  /* Call-specific handler code */
+  switch (call_nr) {
+  case PM_SETUID_REPLY:
+  case PM_SETGID_REPLY:
+       /* Wake up the original caller */
+       setreply(rmp-mproc, OK);
 
-               /* Call is finished */
-               rmp->mp_fs_call= PM_IDLE;
+       break;
 
-               exit_restart(rmp, FALSE /*dump_core*/);
+  case PM_SETSID_REPLY:
+       /* Wake up the original caller */
+       setreply(rmp-mproc, rmp->mp_procgrp);
 
-               break;
+       break;
 
-       case PM_REBOOT_REPLY:
-       {
-               vir_bytes code_addr;
-               size_t code_size;
+  case PM_EXEC_REPLY:
+       exec_restart(rmp, m_in.PM_STATUS);
 
-               /* 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;
-       }
+       break;
 
-       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];
+  case PM_EXIT_REPLY:
+       exit_restart(rmp, FALSE /*dump_core*/);
 
-               /* Call is finished */
-               rmp->mp_fs_call= PM_IDLE;
+       break;
 
-               exec_restart(rmp, m_ptr->PM_EXEC_STATUS);
+  case PM_CORE_REPLY:
+       if (m_in.PM_STATUS == OK)
+               rmp->mp_sigstatus |= DUMPED;
 
-               restart_sigs(rmp);
+       exit_restart(rmp, TRUE /*dump_core*/);
 
-               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_FORK_REPLY:
+       /* Wake up the newly created process */
+       setreply(proc_n, OK);
 
-               if (m_ptr->PM_CORE_STATUS == OK)
-                       rmp->mp_sigstatus |= DUMPED;
+       /* Wake up the parent */
+       setreply(rmp->mp_parent, rmp->mp_pid);
 
-               /* Call is finished */
-               rmp->mp_fs_call= PM_IDLE;
+       break;
 
-               exit_restart(rmp, TRUE /*dump_core*/);
+  case PM_FORK_NB_REPLY:
+       /* Nothing to do */
 
-               break;
-       }
-       default:
-               panic(__FILE__, "handle_fs_reply: unknown reply type",
-                       m_ptr->m_type);
-               break;
-       }
+       break;
 
-}
+  case PM_UNPAUSE_REPLY:
+       /* Process is now unpaused */
+       rmp->mp_flags |= UNPAUSED;
 
-/*===========================================================================*
- *                             restart_sigs                                 *
- *===========================================================================*/
-PRIVATE void restart_sigs(rmp)
-struct mproc *rmp;
-{
+       break;
 
-       if (rmp->mp_fs_call != PM_IDLE || rmp->mp_fs_call2 != PM_IDLE)
-               return;
+  default:
+       panic(__FILE__, "handle_fs_reply: unknown reply code", call_nr);
+  }
 
-       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);
-               }
-       }
+  /* Now that the process is idle again, look at pending signals */
+  if ((rmp->mp_flags & (IN_USE | EXITING)) == IN_USE)
+         restart_sigs(rmp);
 }
index fd752f242684eeae513d27ac1d745f2e7de3b7cf..98f9cc42e5974e04aa65327b3b07305e418e747b 100644 (file)
@@ -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);
 }
 
 /*===========================================================================*
index c50b3764dbc99a9afaa56e63eb2f79dd3b40179d..9b6e9b48e8df17192cd6eb2680303cf373d6516a 100644 (file)
@@ -7,6 +7,7 @@
 #include <timers.h>
 #include <signal.h>
 
+/* 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)
 
index b70f4837d2aa0269b69de37e6fe9c223c99c4993..836bdbde55baa1a852ce32c50745d260f0901747 100644 (file)
@@ -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)          );
index 678945bbb6222fdcdce261d5b5fb6b57c8e8e0aa..47bbedb5e3e29edfd818ffc6e52500d25a2024e4 100644 (file)
@@ -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)
-  {
-       sigaddset(&rmp->mp_sigpending, signo);
-       rmp->mp_flags |= PM_SIG_PENDING;
-       /* keep the process from running */
-       sys_nice(rmp->mp_endpoint, PRIO_STOP);
+
+  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 & 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 & FS_CALL) {
+       sigaddset(&rmp->mp_sigpending, signo);
+
+       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;
+  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;
+  }
 
-        rmp->mp_sigmsg.sm_stkptr = cur_sp;
+  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;
+  }
 
-       /* Check to see if process is hanging on a PAUSE, WAIT or SIGSUSPEND
-        * call.
+  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 & (PAUSED | WAITING | SIGSUSPENDED)) {
-               rmp->mp_flags &= ~(PAUSED | WAITING | SIGSUSPENDED);
-               setreply(slot, EINTR);
+       if (!(rmp->mp_flags & UNPAUSED)) {
+               unpause(rmp);
 
-               /* 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);
+               if (!(rmp->mp_flags & UNPAUSED)) {
+                       /* not yet unpaused; continue later */
+                       sigaddset(&rmp->mp_sigpending, signo);
 
-               /* Done */
-               return;
+                       return;
+               }
        }
 
-       /* Ask FS to unpause the process. Deliver the signal when FS is
-        * ready.
+       /* Then send the actual signal to the process, by setting up a signal
+        * handler.
         */
-       unpause(slot, FALSE /*!for_trace*/);
-       vm_notify_sig_wrapper(rmp->mp_endpoint);
-       return;
-  }
-  else if (sigismember(&rmp->mp_sig2mess, signo)) {
+       if (sig_send(rmp, signo))
+               return;
 
-       /* Mark event pending in process slot and send notification. */
-       sigaddset(&rmp->mp_sigpending, signo);
-       notify(rmp->mp_endpoint);
-       return;
+       /* We were unable to spawn a signal handler. Kill the process. */
   }
-
-doterminate:
-  /* Signal should not or cannot be caught.  Take default action. */
-  if (sigismember(&ign_sset, signo)) {
+  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
-  {
-         if (rmp->mp_fs_call2 != PM_IDLE)
-               panic( __FILE__, "unpause: not idle", rmp->mp_fs_call2);
-         rmp->mp_fs_call2= PM_UNPAUSE;
+       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;
+       }
   }
-  r= notify(FS_PROC_NR);
-  if (r != OK) panic("pm", "unpause: unable to notify FS", r);
 }
index 25187286398048ef440c369a6b116e42836f6ba6..7e21d1c1351fee12e43e7c877fa3520e339284e8 100644 (file)
@@ -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) */
index 004fc73c1baa31637ab69c5cb39812029788ef89..4652e639c8f6524d9716ad616d391dc24a78f402 100644 (file)
  * 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"
 #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;
+
+  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_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)
+
+  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);
-       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);
+       /* 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
-   */
-  if ((child=find_proc(m_in.pid))==NIL_MPROC || child->mp_parent != who_p)
-       return(ESRCH);
-
-  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
+  /* 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->mp_flags & STOPPED) || child->mp_sigstatus > 0)
-       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);
 
-  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;
+       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;
+       child->mp_trace_flags = 0;
+       req = T_RESUME;
+
+       check_pending(child);
+
+       break;
+
   case T_RESUME: 
-  case T_STEP:                 /* resume execution */
-       if (m_in.data < 0 || m_in.data > _NSIG) return(EIO);
+  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 */
-               child->mp_flags &= ~TRACED;  /* so signal is not diverted */
-               sig_proc(child, (int) m_in.data);
-               child->mp_flags |= TRACED;
+               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;
-       break;
+
+       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;
 }
index 2e1838dfc8764e638fcbc1ae6caee2ef03fac17d..c75bc0fa465abe61c944e225e79c0277f4f537f5 100644 (file)
@@ -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)
index cd1ae0e37406caaf429dc4380cc02df90ba6319e..5918a9138954a5d0eac0080818edf7ae4b53e5ed 100644 (file)
@@ -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) {
index dc6143a6d17c26e117e239e6d9854e0e80358618..962c98d996eb2d5719d64166e889a4358b3aaf44 100644 (file)
@@ -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);
-
-                       /* 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;
+  case PM_DUMPCORE:
+       r = pm_dumpcore(m_in.PM_PROC,
+               NULL /* (struct mem_map *) m_in.PM_SEGPTR */);
 
-               case PM_DUMPCORE:
-                       r= pm_dumpcore(m.PM_CORE_PROC,
-                               (struct mem_map *)m.PM_CORE_SEGPTR);
-
-                       /* 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;
+       /* 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;
 
-               default:
-                       panic("VFS", "service_pm: unknown call", m.m_type);
-               }
-       }
-}
+  case PM_FORK:
+  case PM_FORK_NB:
+       pm_fork(m_in.PM_PPROC, m_in.PM_PROC, m_in.PM_CPID);
+
+       m_out.m_type = (call_nr == PM_FORK) ? PM_FORK_REPLY : PM_FORK_NB_REPLY;
+       m_out.PM_PROC = m_in.PM_PROC;
+
+       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);
+}
index dde86756a75b2be3eb84eae7b6cf55c753da05d7..6262d8d897de1a3f4a720dc4cadebf4d19d0f0ba 100644 (file)
@@ -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
index 5a63df35ffc20e991234435bab61ab18a940b4b6..7fba9a7fcbeb6aa17abc146af945faf66f0cf32f 100755 (executable)
--- 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 (file)
index 0000000..35e371c
--- /dev/null
@@ -0,0 +1,1349 @@
+/* Tests for MINIX3 ptrace(2) - by D.C. van Moolenbroek */
+#define _POSIX_SOURCE 1
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+
+#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);
+  }
+}