#include "audio_fw.h"
#include <sys/vm.h>
+#include <minix/endpoint.h>
#include <minix/ds.h>
#include <sys/vm_i386.h>
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) */
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);
{
#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);
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);
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;
/* Status request from RS */
notify(m.m_source);
break;
- case SYSTEM:
- handle_system_signal(&m);
- break;
case HARDWARE:
/* Interrupt from device */
handle_hw_intr();
do_dump(&m);
break;
case PM_PROC_NR:
+ handle_system_signal(&m);
break;
default:
/* Invalid message type */
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) );
/*===========================================================================*
* 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);
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;
printf("%s: resetting device\n", fp->fxp_name);
fxp_outl(port, CSR_PORT, CP_CMD_SOFT_RESET);
}
- sys_exit(0);
+ exit(0);
}
/*===========================================================================*
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)
{
}
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);
}
printf("LANCE driver stopped.\n");
#endif
- sys_exit( 0 );
+ exit( 0 );
}
int r, proc_nr;
message mess;
+ sigset_t set;
system_hz = sys_hz();
}
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);
/*============================================================================*
* 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. */
}
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) );
_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) );
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;
int r, proc_nr;
message mess, reply_mess;
+ sigset_t set;
/* Init MQ library. */
mq_init();
}
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);
/*============================================================================*
* 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. */
}
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) );
_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) );
/*==========================================================================*
* 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 */
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;
#include "log.h"
#include <sys/time.h>
#include <sys/select.h>
+#include <minix/endpoint.h>
#define LOG_DEBUG 0 /* enable/ disable debugging */
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) );
/*============================================================================*
* 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);
}
}
/* 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);
r = EDONTREPLY;
break;
}
- case NOTIFY_FROM(TTY_PROC_NR):
- do_new_kmess(m_ptr);
- r = EDONTREPLY;
- break;
default:
r = EINVAL;
break;
};
/* 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) );
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)
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:",
continue;
/* TODO: send a signal to the card to shut it down */
}
- sys_exit(0);
+ exit(0);
}
/*****************************************************************************
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 */
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) );
/*===========================================================================*
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,
/*===========================================================================*
* 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)) {
*/
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)
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);
/* 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 */
* 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
_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));
_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__); }
#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));
*/
#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
_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 */
#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>
#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
* k_reenter larger than zero.
*/
if (k_reenter == 0 && ! iskernelp(saved_proc)) {
+#if 0
{
kprintf(
proc_stacktrace(saved_proc);
}
+#endif
cause_sig(proc_nr(saved_proc), ep->signum);
return;
}
_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
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);
+}
#include "proc.h"
#include <signal.h>
#include <minix/com.h>
+#include <minix/endpoint.h>
#include <minix/portio.h>
/* Function prototype for PRIVATE functions.
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);
- }
+ }
}
}
#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
#define FLAG(n) if(flags & n) { strcat(str, #n " "); }
FLAG(SLOT_FREE);
- FLAG(NO_PRIORITY);
+ FLAG(PROC_STOP);
FLAG(SENDING);
FLAG(RECEIVING);
FLAG(SIGNALED);
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.
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);
}
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));
}
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););
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",
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 */
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 */
}
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;
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
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;
}
/*===========================================================================*
* 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;
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;
}
/* 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");
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
/* 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 */
#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. */
} 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
_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))
_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 */
* 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
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 */
}
}
+/*===========================================================================*
+ * 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
/*===========================================================================*
#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)
$(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) \
$(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
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;
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 */
--- /dev/null
+/* 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 */
+
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");
* 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.
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 */
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 */
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;
break;
default:
- return(EIO);
+ return(EINVAL);
}
return(OK);
}
#include <lib.h>
#define sigaction _sigaction
-#define sigemptyset _sigemptyset
+#define _SYSTEM 1
#include <signal.h>
PUBLIC sighandler_t signal(sig, disp)
#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
sys_out.c \
sys_physcopy.c \
sys_readbios.c \
+ sys_runctl.c \
sys_safecopy.c \
sys_sysctl.c \
sys_vsafecopy.c \
--- /dev/null
+#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));
+}
/*===========================================================================*
* 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;
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;
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.
*/
-.\" 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
.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>
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;
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 $
*/
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' : '-';
*===========================================================================*/
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;
}
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));
#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)
#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;
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;
/* 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;
}
}
+ /* 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);
}
* 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"
#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) );
/*===========================================================================*
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.
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;
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
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))
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;
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);
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)
* 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);
}
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;
/* 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*/);
}
}
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 */
* 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 */
*/
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);
- }
}
}
}
}
+/*===========================================================================*
+ * 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*/);
+ }
}
/*===========================================================================*
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 *
*===========================================================================*/
-/* 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;
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);
}
* 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];
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))
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) {
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
/* 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
}
/*===========================================================================*
- * 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);
}
*/
/* 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;
#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 */
*===========================================================================*/
PUBLIC int do_reboot()
{
+ message m;
int r;
/* Check permission to abort the system. */
*/
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 */
}
*===========================================================================*/
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;
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;
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);
}
/*===========================================================================*
#include <timers.h>
#include <signal.h>
+/* Needs to be included here, for 'ps' etc */
#include "const.h"
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 */
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. */
} 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)
/* 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) );
_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) );
_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) );
/* 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) );
/* 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) );
* 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"
#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 *
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,
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);
}
* 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) {
}
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;
* 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",
}
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);
+ }
}
/*===========================================================================*
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.
* 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*/);
*===========================================================================*/
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
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);
* 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 */
}
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
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);
}
}
}
/*===========================================================================*
* 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
* 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);
}
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 */
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 */
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 */
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) */
* 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 */
/* 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*/);
/* 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 *
*===========================================================================*/
{
/* 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;
}
/* 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"
}
/*===========================================================================*
- * 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);
}
/*===========================================================================*
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)
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) {
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;
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)
{
}
#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;
*===========================================================================*/
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);
+}
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
t40e: t40e.c
t40f: t40f.c
test41: test41.c
+test42: test42.c
# 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 \) ]
--- /dev/null
+/* 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);
+ }
+}