diff --git a/src/configure.in b/src/configure.in index f011be0c2447e0b0ee5186181c3af1c563b05698..5a1cb3f92bb0ab69584fa480b5930fb6e2c26410 100644 --- a/src/configure.in +++ b/src/configure.in @@ -1,4 +1,4 @@ -AC_REVISION("$Id: configure.in,v 1.703 2003/02/27 17:22:55 mirar Exp $") +AC_REVISION("$Id: configure.in,v 1.704 2003/02/28 18:48:29 grubba Exp $") AC_INIT(interpret.c) AC_CONFIG_HEADER(machine.h) @@ -2383,7 +2383,7 @@ AC_HEADER_STDC AC_CHECK_HEADERS(sys/rusage.h time.h sys/time.h sys/types.h unistd.h stdlib.h \ memory.h values.h string.h strings.h fcntl.h sys/filio.h sys/sockio.h crypt.h \ locale.h sys/select.h net/socket.h sys/mman.h setjmp.h \ -limits.h pthread.h crt/signal.h sys/id.h mach-o/dyld.h \ +limits.h pthread.h crt/signal.h sys/id.h mach-o/dyld.h sys/ptrace.h \ thread.h dlfcn.h dld.h dl.h sys/times.h sched.h sys/procfs.h sys/param.h \ winsock.h sys/ioct.h sys/socket.h malloc.h netinet/in.h sys/wait.h winbase.h \ grp.h pwd.h passwd.h group.h winsock2.h signal.h sys/file.h poll.h sys/poll.h \ @@ -3115,6 +3115,7 @@ AC_CHECK_FUNCS( \ memchr \ mktime \ perror \ + ptrace \ rindex \ setbuf \ setlocale \ diff --git a/src/signal_handler.c b/src/signal_handler.c index 82f1f011090c822aa1e5a10525a7798f479be5ef..2fcf880fb57005e3c48f110da656dfed26706151 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -2,7 +2,7 @@ || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. -|| $Id: signal_handler.c,v 1.239 2003/01/11 19:45:24 per Exp $ +|| $Id: signal_handler.c,v 1.240 2003/02/28 18:48:29 grubba Exp $ */ #include "global.h" @@ -26,7 +26,7 @@ #include "main.h" #include <signal.h> -RCSID("$Id: signal_handler.c,v 1.239 2003/01/11 19:45:24 per Exp $"); +RCSID("$Id: signal_handler.c,v 1.240 2003/02/28 18:48:29 grubba Exp $"); #ifdef HAVE_PASSWD_H # include <passwd.h> @@ -97,6 +97,10 @@ RCSID("$Id: signal_handler.c,v 1.239 2003/01/11 19:45:24 per Exp $"); # include <sys/termios.h> #endif +#ifdef HAVE_SYS_PTRACE_H +#include <sys/ptrace.h> +#endif + #ifdef __amigaos__ #define timeval amigaos_timeval #include <exec/types.h> @@ -135,12 +139,16 @@ RCSID("$Id: signal_handler.c,v 1.239 2003/01/11 19:45:24 per Exp $"); #endif /* HAVE_UNION_WAIT */ #endif /* !WEXITSTATUS */ +#ifndef WUNTRACED +#define WUNTRACED 0 +#endif /* !WUNTRACED */ + /* Number of EBADF's before the set_close_on_exec() loop terminates. */ #ifndef PIKE_BADF_LIMIT #define PIKE_BADF_LIMIT 1024 #endif /* !PIKE_BADF_LIMIT */ -/* #define PROC_DEBUG */ +#define PROC_DEBUG #ifndef __NT__ #define USE_PID_MAPPING @@ -260,7 +268,8 @@ DECLARE_FIFO(sig, unsigned char); #ifdef USE_PID_MAPPING static void report_child(int pid, - WAITSTATUSTYPE status); + WAITSTATUSTYPE status, + const char *called_from); #endif @@ -555,7 +564,7 @@ void process_started(pid_t pid) } } -void process_done(pid_t pid, char *from) +void process_done(pid_t pid, const char *from) { if(pid < 1) Pike_fatal("Pid out of range in %s: %ld\n",from,(long)pid); @@ -695,9 +704,9 @@ PMOD_EXPORT void check_signals(struct callback *foo, void *bar, void *gazonk) #endif "defined.\n"); - process_done(FIFO_DATA(wait,wait_data).pid,"check_signals"); report_child(FIFO_DATA(wait,wait_data).pid, - FIFO_DATA(wait,wait_data).status); + FIFO_DATA(wait,wait_data).status, + "check_signals"); END_FIFO_LOOP(wait,wait_data); } @@ -949,7 +958,7 @@ static RETSIGTYPE receive_sigchild(int signum) try_reap_again: /* We carefully reap what we saw */ - pid=MY_WAIT_ANY(&status, WNOHANG); + pid=MY_WAIT_ANY(&status, WNOHANG|WUNTRACED); if(pid>0) { @@ -974,9 +983,12 @@ static RETSIGTYPE receive_sigchild(int signum) #endif -#define PROCESS_UNKNOWN -1 -#define PROCESS_RUNNING 0 -#define PROCESS_EXITED 1 +#define PROCESS_UNKNOWN -1 +#define PROCESS_RUNNING 0 +#define PROCESS_STOPPED 1 +#define PROCESS_EXITED 2 + +#define PROCESS_FLAG_TRACED 0x01 #undef THIS #define THIS ((struct pid_status *)CURRENT_STORAGE) @@ -991,6 +1003,8 @@ struct pid_status #ifdef __NT__ HANDLE handle; #else + int sig; + int flags; int state; int result; #endif @@ -1004,6 +1018,8 @@ static void init_pid_status(struct object *o) #ifdef __NT__ THIS->handle = DO_NOT_WARN(INVALID_HANDLE_VALUE); #else + THIS->sig=0; + THIS->flags=0; THIS->state=PROCESS_UNKNOWN; THIS->result=-1; #endif @@ -1028,12 +1044,17 @@ static void exit_pid_status(struct object *o) #ifdef USE_PID_MAPPING static void report_child(int pid, - WAITSTATUSTYPE status) + WAITSTATUSTYPE status, + const char *from) { #ifdef PROC_DEBUG /* FIXME: This won't work if WAITSTATUSTYPE == union wait. */ - fprintf(stderr, "report_child(%d, %d)\n", (int)pid, *(int *) &status); + fprintf(stderr, "report_child(%d, %d, \"%s\")\n", + (int)pid, *(int *) &status, from); #endif /* PROC_DEBUG */ + if (!WIFSTOPPED(status)) { + process_done(pid, from); + } if(pid_mapping) { struct svalue *s, key; @@ -1047,12 +1068,23 @@ static void report_child(int pid, if((p=(struct pid_status *)get_storage(s->u.object, pid_status_program))) { - if(WIFEXITED(status)) { - p->result = WEXITSTATUS(status); + if(WIFSTOPPED(status)) { + p->sig = WSTOPSIG(status); + p->state = PROCESS_STOPPED; + + /* Make sure we don't remove the entry from the mapping... */ + return; } else { - p->result=-1; + if(WIFEXITED(status)) { + p->result = WEXITSTATUS(status); + } else { + if (WIFSIGNALED(status)) { + p->sig = WTERMSIG(status); + } + p->result=-1; + } + p->state = PROCESS_EXITED; } - p->state = PROCESS_EXITED; } } /* FIXME: Is this a good idea? @@ -1062,6 +1094,11 @@ static void report_child(int pid, * But that will only happen if there isn't a proper pid status * object in the value, i.e. either a destructed object or * garbage that shouldn't be in the mapping to begin with. /mast + * + * Now when we wait(2) with WUNTRACED, there are cases + * when we want to keep the entry. This is currently + * achieved with the return above. + * /grubba 2003-02-28 */ map_delete(pid_mapping, &key); #ifdef PROC_DEBUG @@ -1119,7 +1156,7 @@ static TH_RETURN_TYPE wait_thread(void *data) #endif mt_lock(&wait_thread_mutex); - pid=MY_WAIT_ANY(&status, WNOHANG); + pid=MY_WAIT_ANY(&status, WNOHANG|WUNTRACED); if(pid < 0 && errno==ECHILD) { @@ -1139,7 +1176,7 @@ static TH_RETURN_TYPE wait_thread(void *data) mt_unlock(&wait_thread_mutex); - if(pid <= 0) pid=MY_WAIT_ANY(&status, 0); + if(pid <= 0) pid=MY_WAIT_ANY(&status, 0|WUNTRACED); #ifdef PROC_DEBUG fprintf(stderr, "wait thread: pid=%d errno=%d\n",pid,errno); @@ -1156,8 +1193,7 @@ static TH_RETURN_TYPE wait_thread(void *data) #ifdef PROC_DEBUG fprintf(stderr, "wait thread: reporting the event!\n"); #endif - process_done(pid,"wait_thread"); - report_child(pid, status); + report_child(pid, status, "wait_thread"); co_broadcast(& process_status_change); mt_unlock_interpreter(); @@ -1189,16 +1225,32 @@ static TH_RETURN_TYPE wait_thread(void *data) *! system process, with methods for various process housekeeping. */ -/*! @decl int wait() - *! Waits for the process to end. +/*! @decl int wait(int(0..1)|void wait_for_stopped) + *! Waits for the process to end. + *! + *! @param wait_for_stopped + *! If non-zero, wait for the process to stop running. + *! *! @returns - *! The exit code of the process. + *! @int + *! @value 0.. + *! The exit code of the process. + *! @value -1 + *! The process was killed by a signal. + *! @value -2 + *! The process was stopped by a non-lethal signal. + *! @endint */ static void f_pid_status_wait(INT32 args) { - pop_n_elems(args); + int wait_for_stopped; if(THIS->pid == -1) Pike_error("This process object has no process associated with it.\n"); + + wait_for_stopped = args && !UNSAFE_IS_ZERO(Pike_sp-args); + + pop_n_elems(args); + #ifdef __NT__ { int err=0; @@ -1229,7 +1281,8 @@ static void f_pid_status_wait(INT32 args) #ifdef USE_WAIT_THREAD - while(THIS->state == PROCESS_RUNNING) + while(THIS->state == PROCESS_RUNNING || + !wait_for_stopped && THIS->state == PROCESS_STOPPED) { SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter( & process_status_change); @@ -1240,7 +1293,8 @@ static void f_pid_status_wait(INT32 args) { int err=0; - while(THIS->state == PROCESS_RUNNING) + while((THIS->state == PROCESS_RUNNING) || + (!wait_for_stopped && (THIS->state == PROCESS_STOPPED))) { int pid; WAITSTATUSTYPE status; @@ -1266,14 +1320,13 @@ static void f_pid_status_wait(INT32 args) } else { THREADS_ALLOW(); - pid=WAITPID(pid,& status,0); + pid = WAITPID(pid, &status, 0|WUNTRACED); THREADS_DISALLOW(); } if(pid > 0) { - process_done(pid,"->wait"); - report_child(pid, status); + report_child(pid, status, "->wait"); } else { @@ -1297,9 +1350,12 @@ static void f_pid_status_wait(INT32 args) * unless we can poll the wait data pipe. /mast */ pid = THIS->pid; - if (THIS->state == PROCESS_RUNNING) { + if ((THIS->state == PROCESS_RUNNING) || + (!wait_for_stopped && (THIS->state == PROCESS_STOPPED))) { + struct pid_status *this = THIS; THREADS_ALLOW(); - while (!kill(pid, 0)) { + while (!kill(pid, 0) && + !wait_for_stopped && (this->state == PROCESS_STOPPED)) { #ifdef PROC_DEBUG fprintf(stderr, "wait(%d): Sleeping...\n", pid); #endif /* PROC_DEBUG */ @@ -1326,7 +1382,8 @@ static void f_pid_status_wait(INT32 args) #ifdef PROC_DEBUG fprintf(stderr, "wait(%d): Process dead.\n", pid); #endif /* PROC_DEBUG */ - if (THIS->state == PROCESS_RUNNING) { + if (THIS->state == PROCESS_RUNNING && + !wait_for_stopped && THIS->state == PROCESS_STOPPED) { /* The child hasn't been reaped yet. * Try waiting some more, and if that * doesn't work, let the main loop complain. @@ -1364,20 +1421,29 @@ static void f_pid_status_wait(INT32 args) } } #endif - push_int(THIS->result); + if (THIS->state == PROCESS_STOPPED) { + push_int(-2); + } else { + push_int(THIS->result); + } #endif /* __NT__ */ } -/*! @decl int(-1..1) status() - *! Returns the status of the process: +/*! @decl int(-1..2) status() + *! Returns the status of the process: *! @int *! @value -1 *! Unknown *! @value 0 *! Running *! @value 1 + *! Stopped + *! @value 2 *! Exited *! @endint + *! + *! @note + *! Prior to Pike 7.5 the value 1 was returned for exited processes. */ static void f_pid_status_status(INT32 args) { @@ -1397,6 +1463,19 @@ static void f_pid_status_status(INT32 args) #endif } +/*! @decl int(0..) last_signal() + *! Returns the last signal that was sent to the process. + */ +static void f_pid_status_last_signal(INT32 args) +{ + pop_n_elems(args); +#ifdef __NT__ + push_int(0); +#else + push_int(THIS->sig); +#endif +} + /*! @decl int pid() *! Returns the process identifier of the process. */ @@ -1431,6 +1510,58 @@ static void f_pid_status_set_priority(INT32 args) push_int(r); } + +#ifdef HAVE_PTRACE +/*! @decl void continue(int|void signal) + *! Allow a traced process to continue. + *! + *! @param signal + *! Deliver this signal to the process. + *! + *! @note + *! This function is only useful for traced processes. + *! + *! This function currently only exists on systems that + *! implement @tt{ptrace()@}. + */ +static void f_pid_status_continue(INT32 args) +{ + int cont_signal = 0; +#ifdef PIKE_SECURITY + if(!CHECK_SECURITY(SECURITY_BIT_SECURITY)) + Pike_error("pid_status_wait: permission denied.\n"); +#endif + if(THIS->pid == -1) + Pike_error("This process object has no process associated with it.\n"); + + if (!(THIS->flags & PROCESS_FLAG_TRACED)) { + Pike_error("This process is not being traced.\n"); + } + + if (THIS->state != PROCESS_STOPPED) { + if (THIS->state == PROCESS_RUNNING) { + Pike_error("Process already running.\n"); + } + Pike_error("Process not stopped\n"); + } + + if (args && Pike_sp[-args].type == T_INT) { + cont_signal = Pike_sp[-args].u.integer; + } + + THIS->state = PROCESS_RUNNING; + + if (ptrace(PTRACE_CONT, THIS->pid, NULL, cont_signal) == -1) { + int err = errno; + THIS->state = PROCESS_STOPPED; + /* FIXME: Better diagnostics. */ + Pike_error("Failed to release process. errno:%d\n", err); + } + pop_n_elems(args); + push_int(0); +} +#endif /* HAVE_PTRACE */ + /*! @endclass */ @@ -1640,6 +1771,7 @@ extern int pike_make_pipe(int *); #define PROCE_DUP 10 #define PROCE_SETSID 11 #define PROCE_SETCTTY 12 +#define PROCE_PTRACE 13 #define PROCERROR(err, id) do { int _l, _i; \ buf[0] = err; buf[1] = errno; buf[2] = id; \ @@ -2012,6 +2144,10 @@ static void internal_add_limit( struct perishables *storage, *! will be inherited from the current process rather than changed to *! the approperiate values for that uid. *! + *! @member int(0..1) "trace" + *! This parameter starts the child process in trace mode. + *! See @[trace()]. + *! *! @member string "priority" *! This sets the priority of the new process, see *! @[set_priority] for more info. @@ -2096,6 +2232,9 @@ static void internal_add_limit( struct perishables *storage, *! The modifiers @tt{"fds"@}, @tt{"uid"@}, @tt{"gid"@}, @tt{"nice"@}, *! @tt{"noinitgroups"@}, @tt{"setgroups"@}, @tt{"keep_signals"@} *! and @tt{"rlimit"@} only exist on unix. + *! + *! The modifier @tt{"trace"@} currently only exists on OS's that + *! implement @tt{ptrace()@}. */ /*! @endclass */ @@ -2452,6 +2591,7 @@ void f_create_process(INT32 args) struct passwd *pw=0; struct perishables storage; int do_initgroups=1; + int do_trace=0; uid_t wanted_uid; gid_t wanted_gid; int gid_request=0; @@ -2751,6 +2891,10 @@ void f_create_process(INT32 args) if(!SAFE_IS_ZERO(tmp)) do_initgroups=0; + if((tmp=simple_mapping_string_lookup(optional, "trace"))) + if(!SAFE_IS_ZERO(tmp)) + do_trace=1; + if((tmp=simple_mapping_string_lookup(optional, "keep_signals"))) keep_signals = !SAFE_IS_ZERO(tmp); } @@ -2982,6 +3126,7 @@ void f_create_process(INT32 args) THIS->pid = pid; THIS->state = PROCESS_RUNNING; + THIS->flags = do_trace?PROCESS_FLAG_TRACED:0; ref_push_object(Pike_fp->current_object); push_int(pid); mapping_insert(pid_mapping, Pike_sp-1, Pike_sp-2); @@ -3103,6 +3248,9 @@ void f_create_process(INT32 args) case PROCE_SETCTTY: Pike_error("Process.create_process(): failed to set controlling TTY. errno:%d\n", buf[1]); break; + case PROCE_PTRACE: + Pike_error("Process.create_process(): failed to enter trace mode. errno:%d\n", buf[1]); + break; case PROCE_EXEC: switch(buf[1]) { case ENOENT: @@ -3426,6 +3574,15 @@ void f_create_process(INT32 args) setresuid(wanted_uid, wanted_uid, -1); #endif /* HAVE_SETRESUID */ #endif /* HAVE_SETEUID */ + +#ifdef HAVE_PTRACE + if (do_trace) { + long code = ptrace(PTRACE_TRACEME, 0, NULL, NULL); + if (code == -1) { + PROCERROR(PROCE_PTRACE, 0); + } + } +#endif /* HAVE_PTRACE */ do_set_close_on_exec(); set_close_on_exec(0,0); @@ -4115,10 +4272,17 @@ void init_signals(void) set_exit_callback(exit_pid_status); /* function(string:int) */ ADD_FUNCTION("set_priority",f_pid_status_set_priority,tFunc(tStr,tInt),0); - /* function(:int) */ - ADD_FUNCTION("wait",f_pid_status_wait,tFunc(tNone,tInt),0); + /* function(int(0..1)|void:int) */ + ADD_FUNCTION("wait",f_pid_status_wait,tFunc(tOr(tInt01,tVoid),tInt),0); /* function(:int) */ ADD_FUNCTION("status",f_pid_status_status,tFunc(tNone,tInt),0); +#ifdef HAVE_PTRACE + /* function(int|void:void) */ + ADD_FUNCTION("continue",f_pid_status_continue, + tFunc(tOr(tVoid,tInt),tVoid),0); +#endif /* HAVE_PTRACE */ + /* function(:int) */ + ADD_FUNCTION("last_signal", f_pid_status_last_signal,tFunc(tNone,tInt),0); /* function(:int) */ ADD_FUNCTION("pid",f_pid_status_pid,tFunc(tNone,tInt),0); #ifdef HAVE_KILL diff --git a/src/signal_handler.h b/src/signal_handler.h index 8a073ce0b5e20a5be913d349cf238566744be4d0..6fac60084e146b32ce2c036b20cb85153f628695 100644 --- a/src/signal_handler.h +++ b/src/signal_handler.h @@ -2,7 +2,7 @@ || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. -|| $Id: signal_handler.h,v 1.15 2002/10/11 01:39:37 nilsson Exp $ +|| $Id: signal_handler.h,v 1.16 2003/02/28 18:48:29 grubba Exp $ */ #ifndef SIGNAL_H @@ -16,7 +16,7 @@ void my_signal(int sig, sigfunctype fun); PMOD_EXPORT void check_signals(struct callback *foo, void *bar, void *gazonk); void set_default_signal_handler(int signum, void (*func)(INT32)); void process_started(pid_t pid); -void process_done(pid_t pid, char *from); +void process_done(pid_t pid, const char *from); struct wait_data; struct pid_status; struct perishables;