diff options
Diffstat (limited to 'src/process.c')
-rw-r--r-- | src/process.c | 573 |
1 files changed, 157 insertions, 416 deletions
diff --git a/src/process.c b/src/process.c index 2fb5b16a19f..5b15ade1122 100644 --- a/src/process.c +++ b/src/process.c @@ -91,6 +91,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <pty.h> #endif +#include <c-ctype.h> +#include <sig2str.h> + #endif /* subprocesses */ #include "systime.h" @@ -130,14 +133,6 @@ extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *, EMACS_TIME *, void *); #endif -#ifndef WNOHANG -# undef waitpid -# define waitpid(pid, status, options) wait (status) -#endif -#ifndef WUNTRACED -# define WUNTRACED 0 -#endif - /* Work around GCC 4.7.0 bug with strict overflow checking; see <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>. These lines can be removed once the GCC bug is fixed. */ @@ -185,10 +180,6 @@ static Lisp_Object Qlast_nonmenu_event; #define SERIALCONN_P(p) (EQ (XPROCESS (p)->type, Qserial)) #define SERIALCONN1_P(p) (EQ (p->type, Qserial)) -#ifndef HAVE_H_ERRNO -extern int h_errno; -#endif - /* Number of events of change of status of a process. */ static EMACS_INT process_tick; /* Number of events for which the user or sentinel has been notified. */ @@ -204,11 +195,9 @@ static EMACS_INT update_tick; #ifndef NON_BLOCKING_CONNECT #ifdef HAVE_SELECT #if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX) -#if defined (O_NONBLOCK) || defined (O_NDELAY) #if defined (EWOULDBLOCK) || defined (EINPROGRESS) #define NON_BLOCKING_CONNECT #endif /* EWOULDBLOCK || EINPROGRESS */ -#endif /* O_NONBLOCK || O_NDELAY */ #endif /* HAVE_GETPEERNAME || GNU_LINUX */ #endif /* HAVE_SELECT */ #endif /* NON_BLOCKING_CONNECT */ @@ -336,9 +325,6 @@ static struct sockaddr_and_len { #define DATAGRAM_CONN_P(proc) (0) #endif -/* Maximum number of bytes to send to a pty without an eof. */ -static int pty_max_bytes; - /* These setters are used only in this file, so they can be private. */ static void pset_buffer (struct Lisp_Process *p, Lisp_Object val) @@ -650,13 +636,7 @@ allocate_pty (void) #ifdef PTY_OPEN PTY_OPEN; #else /* no PTY_OPEN */ - { -# ifdef O_NONBLOCK - fd = emacs_open (pty_name, O_RDWR | O_NONBLOCK, 0); -# else - fd = emacs_open (pty_name, O_RDWR | O_NDELAY, 0); -# endif - } + fd = emacs_open (pty_name, O_RDWR | O_NONBLOCK, 0); #endif /* no PTY_OPEN */ if (fd >= 0) @@ -668,7 +648,7 @@ allocate_pty (void) #else sprintf (pty_name, "/dev/tty%c%x", c, i); #endif /* no PTY_TTY_NAME_SPRINTF */ - if (access (pty_name, 6) != 0) + if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0) { emacs_close (fd); # ifndef __sgi @@ -792,14 +772,22 @@ get_process (register Lisp_Object name) } -#ifdef SIGCHLD /* Fdelete_process promises to immediately forget about the process, but in reality, Emacs needs to remember those processes until they have been - treated by the SIGCHLD handler; otherwise this handler would consider the - process as being synchronous and say that the synchronous process is - dead. */ + treated by the SIGCHLD handler and waitpid has been invoked on them; + otherwise they might fill up the kernel's process table. + + Some processes created by call-process are also put onto this list. */ static Lisp_Object deleted_pid_list; -#endif + +void +record_deleted_pid (pid_t pid) +{ + deleted_pid_list = Fcons (make_fixnum_or_float (pid), + /* GC treated elements set to nil. */ + Fdelq (Qnil, deleted_pid_list)); + +} DEFUN ("delete-process", Fdelete_process, Sdelete_process, 1, 1, 0, doc: /* Delete PROCESS: kill it and forget about it immediately. @@ -820,32 +808,22 @@ nil, indicating the current buffer's process. */) status_notify (p); redisplay_preserve_echo_area (13); } - else if (p->infd >= 0) + else { -#ifdef SIGCHLD - Lisp_Object symbol; - pid_t pid = p->pid; - - /* No problem storing the pid here, as it is still in Vprocess_alist. */ - deleted_pid_list = Fcons (make_fixnum_or_float (pid), - /* GC treated elements set to nil. */ - Fdelq (Qnil, deleted_pid_list)); - /* If the process has already signaled, remove it from the list. */ - if (p->raw_status_new) - update_status (p); - symbol = p->status; - if (CONSP (p->status)) - symbol = XCAR (p->status); - if (EQ (symbol, Qsignal) || EQ (symbol, Qexit)) - deleted_pid_list - = Fdelete (make_fixnum_or_float (pid), deleted_pid_list); - else -#endif + if (p->alive) + record_kill_process (p); + + if (p->infd >= 0) { - Fkill_process (process, Qnil); - /* Do this now, since remove_process will make the - SIGCHLD handler do nothing. */ - pset_status (p, Fcons (Qsignal, Fcons (make_number (SIGKILL), Qnil))); + /* Update P's status, since record_kill_process will make the + SIGCHLD handler update deleted_pid_list, not *P. */ + Lisp_Object symbol; + if (p->raw_status_new) + update_status (p); + symbol = CONSP (p->status) ? XCAR (p->status) : p->status; + if (! (EQ (symbol, Qsignal) || EQ (symbol, Qexit))) + pset_status (p, list2 (Qsignal, make_number (SIGKILL))); + p->tick = ++process_tick; status_notify (p); redisplay_preserve_echo_area (13); @@ -1595,20 +1573,15 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) int inchannel, outchannel; pid_t pid; int sv[2]; -#if !defined (WINDOWSNT) && defined (FD_CLOEXEC) +#ifndef WINDOWSNT int wait_child_setup[2]; #endif -#ifdef SIGCHLD sigset_t blocked; -#endif /* Use volatile to protect variables from being clobbered by vfork. */ volatile int forkin, forkout; volatile int pty_flag = 0; volatile Lisp_Object lisp_pty_name = Qnil; volatile Lisp_Object encoded_current_dir; -#if HAVE_WORKING_VFORK - char **volatile save_environ; -#endif inchannel = outchannel = -1; @@ -1621,13 +1594,9 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) #if ! defined (USG) || defined (USG_SUBTTY_WORKS) /* On most USG systems it does not work to open the pty's tty here, then close it and reopen it in the child. */ -#ifdef O_NOCTTY /* Don't let this terminal become our controlling terminal (in case we don't have one). */ forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0); -#else - forkout = forkin = emacs_open (pty_name, O_RDWR, 0); -#endif if (forkin < 0) report_file_error ("Opening pty", Qnil); #else @@ -1656,7 +1625,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) forkin = sv[0]; } -#if !defined (WINDOWSNT) && defined (FD_CLOEXEC) +#ifndef WINDOWSNT { int tem; @@ -1675,15 +1644,8 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) } #endif -#ifdef O_NONBLOCK fcntl (inchannel, F_SETFL, O_NONBLOCK); fcntl (outchannel, F_SETFL, O_NONBLOCK); -#else -#ifdef O_NDELAY - fcntl (inchannel, F_SETFL, O_NDELAY); - fcntl (outchannel, F_SETFL, O_NDELAY); -#endif -#endif /* Record this as an active process, with its channels. As a result, child_setup will close Emacs's side of the pipes. */ @@ -1704,35 +1666,18 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) if (inchannel > max_process_desc) max_process_desc = inchannel; - /* Until we store the proper pid, enable the SIGCHLD handler - to recognize an unknown pid as standing for this process. - It is very important not to let this `marker' value stay - in the table after this function has returned; if it does - it might cause call-process to hang and subsequent asynchronous - processes to get their return values scrambled. */ - XPROCESS (process)->pid = -1; - - /* This must be called after the above line because it may signal an - error. */ + /* This may signal an error. */ setup_process_coding_systems (process); encoded_current_dir = ENCODE_FILE (current_dir); block_input (); -#ifdef SIGCHLD /* Block SIGCHLD until we have a chance to store the new fork's pid in its process structure. */ sigemptyset (&blocked); sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); -#endif - -#if HAVE_WORKING_VFORK - /* child_setup must clobber environ on systems with true vfork. - Protect it from permanent change. */ - save_environ = environ; -#endif #ifndef WINDOWSNT pid = vfork (); @@ -1745,7 +1690,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) /* Make the pty be the controlling terminal of the process. */ #ifdef HAVE_PTYS /* First, disconnect its current controlling terminal. */ -#ifdef HAVE_SETSID /* We tried doing setsid only if pty_flag, but it caused process_set_signal to fail on SGI when using a pipe. */ setsid (); @@ -1758,12 +1702,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) ioctl (xforkin, TIOCSCTTY, 0); #endif } -#else /* not HAVE_SETSID */ - /* It's very important to call setpgid here and no time - afterwards. Otherwise, we lose our controlling tty which - is set when we open the pty. */ - setpgid (0, 0); -#endif /* not HAVE_SETSID */ #if defined (LDISC1) if (pty_flag && xforkin >= 0) { @@ -1796,22 +1734,15 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) ioctl (j, TIOCNOTTY, 0); emacs_close (j); } -#ifndef USG - /* In order to get a controlling terminal on some versions - of BSD, it is necessary to put the process in pgrp 0 - before it opens the terminal. */ - setpgid (0, 0); -#endif } #endif /* TIOCNOTTY */ #if !defined (DONT_REOPEN_PTY) /*** There is a suggestion that this ought to be a - conditional on TIOCSPGRP, - or !(defined (HAVE_SETSID) && defined (TIOCSCTTY)). + conditional on TIOCSPGRP, or !defined TIOCSCTTY. Trying the latter gave the wrong results on Debian GNU/Linux 1.1; that system does seem to need this code, even though - both HAVE_SETSID and TIOCSCTTY are defined. */ + both TIOCSCTTY is defined. */ /* Now close the pty (if we had it open) and reopen it. This makes the pty the controlling terminal of the subprocess. */ if (pty_flag) @@ -1854,10 +1785,8 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) /* Emacs ignores SIGPIPE, but the child should not. */ signal (SIGPIPE, SIG_DFL); -#ifdef SIGCHLD /* Stop blocking signals in the child. */ pthread_sigmask (SIG_SETMASK, &empty_mask, 0); -#endif if (pty_flag) child_setup_tty (xforkout); @@ -1865,9 +1794,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) pid = child_setup (xforkin, xforkout, xforkout, new_argv, 1, encoded_current_dir); #else /* not WINDOWSNT */ -#ifdef FD_CLOEXEC emacs_close (wait_child_setup[0]); -#endif child_setup (xforkin, xforkout, xforkout, new_argv, 1, encoded_current_dir); #endif /* not WINDOWSNT */ @@ -1875,16 +1802,12 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) /* Back in the parent process. */ -#if HAVE_WORKING_VFORK - environ = save_environ; -#endif - XPROCESS (process)->pid = pid; + if (0 <= pid) + XPROCESS (process)->alive = 1; /* Stop blocking signals in the parent. */ -#ifdef SIGCHLD pthread_sigmask (SIG_SETMASK, &empty_mask, 0); -#endif unblock_input (); if (pid < 0) @@ -1924,11 +1847,11 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) pset_tty_name (XPROCESS (process), lisp_pty_name); -#if !defined (WINDOWSNT) && defined (FD_CLOEXEC) +#ifndef WINDOWSNT /* Wait for child_setup to complete in case that vfork is actually defined as fork. The descriptor wait_child_setup[1] of a pipe is closed at the child side either by close-on-exec - on successful execvp or the _exit call in child_setup. */ + on successful execve or the _exit call in child_setup. */ { char dummy; @@ -1961,13 +1884,9 @@ create_pty (Lisp_Object process) #if ! defined (USG) || defined (USG_SUBTTY_WORKS) /* On most USG systems it does not work to open the pty's tty here, then close it and reopen it in the child. */ -#ifdef O_NOCTTY /* Don't let this terminal become our controlling terminal (in case we don't have one). */ int forkout = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0); -#else - int forkout = emacs_open (pty_name, O_RDWR, 0); -#endif if (forkout < 0) report_file_error ("Opening pty", Qnil); #if defined (DONT_REOPEN_PTY) @@ -1981,15 +1900,8 @@ create_pty (Lisp_Object process) } #endif /* HAVE_PTYS */ -#ifdef O_NONBLOCK fcntl (inchannel, F_SETFL, O_NONBLOCK); fcntl (outchannel, F_SETFL, O_NONBLOCK); -#else -#ifdef O_NDELAY - fcntl (inchannel, F_SETFL, O_NDELAY); - fcntl (outchannel, F_SETFL, O_NDELAY); -#endif -#endif /* Record this as an active process, with its channels. As a result, child_setup will close Emacs's side of the pipes. */ @@ -2945,13 +2857,9 @@ usage: (make-network-process &rest ARGS) */) { /* Don't support network sockets when non-blocking mode is not available, since a blocked Emacs is not useful. */ -#if !defined (O_NONBLOCK) && !defined (O_NDELAY) - error ("Network servers not supported"); -#else is_server = 1; if (TYPE_RANGED_INTEGERP (int, tem)) backlog = XINT (tem); -#endif } /* Make QCaddress an alias for :local (server) or :remote (client). */ @@ -3211,11 +3119,7 @@ usage: (make-network-process &rest ARGS) */) #ifdef NON_BLOCKING_CONNECT if (is_non_blocking_client) { -#ifdef O_NONBLOCK ret = fcntl (s, F_SETFL, O_NONBLOCK); -#else - ret = fcntl (s, F_SETFL, O_NDELAY); -#endif if (ret < 0) { xerrno = errno; @@ -3428,13 +3332,7 @@ usage: (make-network-process &rest ARGS) */) chan_process[inch] = proc; -#ifdef O_NONBLOCK fcntl (inch, F_SETFL, O_NONBLOCK); -#else -#ifdef O_NDELAY - fcntl (inch, F_SETFL, O_NDELAY); -#endif -#endif p = XPROCESS (proc); @@ -4163,13 +4061,7 @@ server_accept_connection (Lisp_Object server, int channel) chan_process[s] = proc; -#ifdef O_NONBLOCK fcntl (s, F_SETFL, O_NONBLOCK); -#else -#ifdef O_NDELAY - fcntl (s, F_SETFL, O_NDELAY); -#endif -#endif p = XPROCESS (proc); @@ -4326,7 +4218,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (time_limit == 0 && nsecs == 0 && wait_proc && !NILP (Vinhibit_quit) && !(CONSP (wait_proc->status) && EQ (XCAR (wait_proc->status), Qexit))) - message ("Blocking call to accept-process-output with quit inhibited!!"); + message1 ("Blocking call to accept-process-output with quit inhibited!!"); /* If wait_proc is a process to watch, set wait_channel accordingly. */ if (wait_proc != NULL) @@ -4437,7 +4329,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (EMACS_TIME_LT (timer_delay, timeout)) { timeout = timer_delay; - timeout_reduced_for_timers = 1; + timeout_reduced_for_timers = 1; } } else @@ -4517,14 +4409,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, total_nread += nread; got_some_input = 1; } -#ifdef EIO - else if (nread == -1 && EIO == errno) - break; -#endif -#ifdef EAGAIN - else if (nread == -1 && EAGAIN == errno) + else if (nread == -1 && (errno == EIO || errno == EAGAIN)) break; -#endif #ifdef EWOULDBLOCK else if (nread == -1 && EWOULDBLOCK == errno) break; @@ -4713,7 +4599,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, yielding EBADF here or at select() call above. So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF in m/ibmrt-aix.h), and here we just ignore the select error. - Cleanup occurs c/o status_notify after SIGCLD. */ + Cleanup occurs c/o status_notify after SIGCHLD. */ no_avail = 1; /* Cannot depend on values returned */ #else emacs_abort (); @@ -4865,23 +4751,17 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, else if (nread == -1 && errno == EWOULDBLOCK) ; #endif - /* ISC 4.1 defines both EWOULDBLOCK and O_NONBLOCK, - and Emacs uses O_NONBLOCK, so what we get is EAGAIN. */ -#ifdef O_NONBLOCK - else if (nread == -1 && errno == EAGAIN) - ; -#else -#ifdef O_NDELAY else if (nread == -1 && errno == EAGAIN) ; +#ifdef WINDOWSNT + /* FIXME: Is this special case still needed? */ /* Note that we cannot distinguish between no input available now and a closed pipe. With luck, a closed pipe will be accompanied by subprocess termination and SIGCHLD. */ else if (nread == 0 && !NETCONN_P (proc) && !SERIALCONN_P (proc)) ; -#endif /* O_NDELAY */ -#endif /* O_NONBLOCK */ +#endif #ifdef HAVE_PTYS /* On some OSs with ptys, when the process on one end of a pty exits, the other end gets an error reading with @@ -4889,11 +4769,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, Therefore, if we get an error reading and errno = EIO, just continue, because the child process has exited and should clean itself up soon (e.g. when we - get a SIGCHLD). - - However, it has been known to happen that the SIGCHLD - got lost. So raise the signal again just in case. - It can't hurt. */ + get a SIGCHLD). */ else if (nread == -1 && errno == EIO) { struct Lisp_Process *p = XPROCESS (proc); @@ -4911,16 +4787,12 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, p->tick = ++process_tick; pset_status (p, Qfailed); } - else - handle_child_signal (SIGCHLD); } #endif /* HAVE_PTYS */ /* If we can detect process termination, don't consider the process gone just because its pipe is closed. */ -#ifdef SIGCHLD else if (nread == 0 && !NETCONN_P (proc) && !SERIALCONN_P (proc)) ; -#endif else { /* Preserve status of processes already terminated. */ @@ -5550,19 +5422,6 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len, buf = SSDATA (object); } - if (pty_max_bytes == 0) - { -#if defined (HAVE_FPATHCONF) && defined (_PC_MAX_CANON) - pty_max_bytes = fpathconf (p->outfd, _PC_MAX_CANON); - if (pty_max_bytes < 0) - pty_max_bytes = 250; -#else - pty_max_bytes = 250; -#endif - /* Deduct one, to leave space for the eof. */ - pty_max_bytes--; - } - /* If there is already data in the write_queue, put the new data in the back of queue. Otherwise, ignore it. */ if (!NILP (p->write_queue)) @@ -5621,13 +5480,10 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len, if (rv < 0) { - if (0 + if (errno == EAGAIN #ifdef EWOULDBLOCK || errno == EWOULDBLOCK #endif -#ifdef EAGAIN - || errno == EAGAIN -#endif ) /* Buffer is full. Wait, accepting input; that may allow the program @@ -5696,19 +5552,19 @@ it is sent in several bunches. This may happen even for shorter regions. Output from processes can arrive in between bunches. */) (Lisp_Object process, Lisp_Object start, Lisp_Object end) { - Lisp_Object proc; - ptrdiff_t start1, end1; + Lisp_Object proc = get_process (process); + ptrdiff_t start_byte, end_byte; - proc = get_process (process); validate_region (&start, &end); + start_byte = CHAR_TO_BYTE (XINT (start)); + end_byte = CHAR_TO_BYTE (XINT (end)); + if (XINT (start) < GPT && XINT (end) > GPT) - move_gap (XINT (start)); + move_gap_both (XINT (start), start_byte); - start1 = CHAR_TO_BYTE (XINT (start)); - end1 = CHAR_TO_BYTE (XINT (end)); - send_process (proc, (char *) BYTE_POS_ADDR (start1), end1 - start1, - Fcurrent_buffer ()); + send_process (proc, (char *) BYTE_POS_ADDR (start_byte), + end_byte - start_byte, Fcurrent_buffer ()); return Qnil; } @@ -5799,7 +5655,7 @@ return t unconditionally. */) If we can, we try to signal PROCESS by sending control characters down the pty. This allows us to signal inferiors who have changed - their uid, for which killpg would return an EPERM error. */ + their uid, for which kill would return an EPERM error. */ static void process_send_signal (Lisp_Object process, int signo, Lisp_Object current_group, @@ -5937,7 +5793,7 @@ process_send_signal (Lisp_Object process, int signo, Lisp_Object current_group, if (!NILP (current_group)) { if (ioctl (p->infd, TIOCSIGSEND, signo) == -1) - EMACS_KILLPG (gid, signo); + kill (-gid, signo); } else { @@ -5945,7 +5801,7 @@ process_send_signal (Lisp_Object process, int signo, Lisp_Object current_group, kill (gid, signo); } #else /* ! defined (TIOCSIGSEND) */ - EMACS_KILLPG (gid, signo); + kill (-gid, signo); #endif /* ! defined (TIOCSIGSEND) */ } @@ -6050,6 +5906,27 @@ traffic. */) return process; } +/* Return the integer value of the signal whose abbreviation is ABBR, + or a negative number if there is no such signal. */ +static int +abbr_to_signal (char const *name) +{ + int i, signo; + char sigbuf[20]; /* Large enough for all valid signal abbreviations. */ + + if (!strncmp (name, "SIG", 3) || !strncmp (name, "sig", 3)) + name += 3; + + for (i = 0; i < sizeof sigbuf; i++) + { + sigbuf[i] = c_toupper (name[i]); + if (! sigbuf[i]) + return str2sig (sigbuf, &signo) == 0 ? signo : -1; + } + + return -1; +} + DEFUN ("signal-process", Fsignal_process, Ssignal_process, 2, 2, "sProcess (name or number): \nnSignal code: ", doc: /* Send PROCESS the signal with code SIGCODE. @@ -6060,6 +5937,7 @@ SIGCODE may be an integer, or a symbol whose name is a signal name. */) (Lisp_Object process, Lisp_Object sigcode) { pid_t pid; + int signo; if (STRINGP (process)) { @@ -6089,12 +5967,11 @@ SIGCODE may be an integer, or a symbol whose name is a signal name. */) error ("Cannot signal process %s", SDATA (XPROCESS (process)->name)); } -#define parse_signal(NAME, VALUE) \ - else if (!xstrcasecmp (name, NAME)) \ - XSETINT (sigcode, VALUE) - if (INTEGERP (sigcode)) - CHECK_TYPE_RANGED_INTEGER (int, sigcode); + { + CHECK_TYPE_RANGED_INTEGER (int, sigcode); + signo = XINT (sigcode); + } else { char *name; @@ -6102,96 +5979,12 @@ SIGCODE may be an integer, or a symbol whose name is a signal name. */) CHECK_SYMBOL (sigcode); name = SSDATA (SYMBOL_NAME (sigcode)); - if (!strncmp (name, "SIG", 3) || !strncmp (name, "sig", 3)) - name += 3; - - if (0) - ; -#ifdef SIGUSR1 - parse_signal ("usr1", SIGUSR1); -#endif -#ifdef SIGUSR2 - parse_signal ("usr2", SIGUSR2); -#endif - parse_signal ("term", SIGTERM); -#ifdef SIGHUP - parse_signal ("hup", SIGHUP); -#endif - parse_signal ("int", SIGINT); -#ifdef SIGQUIT - parse_signal ("quit", SIGQUIT); -#endif - parse_signal ("ill", SIGILL); - parse_signal ("abrt", SIGABRT); -#ifdef SIGEMT - parse_signal ("emt", SIGEMT); -#endif -#ifdef SIGKILL - parse_signal ("kill", SIGKILL); -#endif - parse_signal ("fpe", SIGFPE); -#ifdef SIGBUS - parse_signal ("bus", SIGBUS); -#endif - parse_signal ("segv", SIGSEGV); -#ifdef SIGSYS - parse_signal ("sys", SIGSYS); -#endif -#ifdef SIGPIPE - parse_signal ("pipe", SIGPIPE); -#endif -#ifdef SIGALRM - parse_signal ("alrm", SIGALRM); -#endif -#ifdef SIGURG - parse_signal ("urg", SIGURG); -#endif -#ifdef SIGSTOP - parse_signal ("stop", SIGSTOP); -#endif -#ifdef SIGTSTP - parse_signal ("tstp", SIGTSTP); -#endif -#ifdef SIGCONT - parse_signal ("cont", SIGCONT); -#endif -#ifdef SIGCHLD - parse_signal ("chld", SIGCHLD); -#endif -#ifdef SIGTTIN - parse_signal ("ttin", SIGTTIN); -#endif -#ifdef SIGTTOU - parse_signal ("ttou", SIGTTOU); -#endif -#ifdef SIGIO - parse_signal ("io", SIGIO); -#endif -#ifdef SIGXCPU - parse_signal ("xcpu", SIGXCPU); -#endif -#ifdef SIGXFSZ - parse_signal ("xfsz", SIGXFSZ); -#endif -#ifdef SIGVTALRM - parse_signal ("vtalrm", SIGVTALRM); -#endif -#ifdef SIGPROF - parse_signal ("prof", SIGPROF); -#endif -#ifdef SIGWINCH - parse_signal ("winch", SIGWINCH); -#endif -#ifdef SIGINFO - parse_signal ("info", SIGINFO); -#endif - else + signo = abbr_to_signal (name); + if (signo < 0) error ("Undefined signal name %s", name); } -#undef parse_signal - - return make_number (kill (pid, XINT (sigcode))); + return make_number (kill (pid, signo)); } DEFUN ("process-send-eof", Fprocess_send_eof, Sprocess_send_eof, 0, 1, 0, @@ -6273,9 +6066,35 @@ process has been transmitted to the serial port. */) return process; } -/* On receipt of a signal that a child status has changed, loop asking - about children with changed statuses until the system says there - are no more. +/* The main Emacs thread records child processes in three places: + + - Vprocess_alist, for asynchronous subprocesses, which are child + processes visible to Lisp. + + - deleted_pid_list, for child processes invisible to Lisp, + typically because of delete-process. These are recorded so that + the processes can be reaped when they exit, so that the operating + system's process table is not cluttered by zombies. + + - the local variable PID in Fcall_process, call_process_cleanup and + call_process_kill, for synchronous subprocesses. + record_unwind_protect is used to make sure this process is not + forgotten: if the user interrupts call-process and the child + process refuses to exit immediately even with two C-g's, + call_process_kill adds PID's contents to deleted_pid_list before + returning. + + The main Emacs thread invokes waitpid only on child processes that + it creates and that have not been reaped. This avoid races on + platforms such as GTK, where other threads create their own + subprocesses which the main thread should not reap. For example, + if the main thread attempted to reap an already-reaped child, it + might inadvertently reap a GTK-created process that happened to + have the same process ID. */ + +/* Handle a SIGCHLD signal by looking for known child processes of + Emacs whose status have changed. For each one found, record its + new status. All we do is change the status; we do not run sentinels or print notifications. That is saved for the next time keyboard input is @@ -6298,132 +6117,64 @@ process has been transmitted to the serial port. */) ** Malloc WARNING: This should never call malloc either directly or indirectly; if it does, that is a bug */ -/* Record the changed status of the child process PID with wait status W. */ -void -record_child_status_change (pid_t pid, int w) +static void +handle_child_signal (int sig) { -#ifdef SIGCHLD - Lisp_Object proc; - struct Lisp_Process *p; Lisp_Object tail; /* Find the process that signaled us, and record its status. */ - /* The process can have been deleted by Fdelete_process. */ + /* The process can have been deleted by Fdelete_process, or have + been started asynchronously by Fcall_process. */ for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail)) { + bool all_pids_are_fixnums + = (MOST_NEGATIVE_FIXNUM <= TYPE_MINIMUM (pid_t) + && TYPE_MAXIMUM (pid_t) <= MOST_POSITIVE_FIXNUM); Lisp_Object xpid = XCAR (tail); - if ((INTEGERP (xpid) && pid == XINT (xpid)) - || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid))) + if (all_pids_are_fixnums ? INTEGERP (xpid) : NUMBERP (xpid)) { - XSETCAR (tail, Qnil); - return; + pid_t deleted_pid; + if (INTEGERP (xpid)) + deleted_pid = XINT (xpid); + else + deleted_pid = XFLOAT_DATA (xpid); + if (child_status_changed (deleted_pid, 0, 0)) + XSETCAR (tail, Qnil); } } /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */ - p = 0; for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) { - proc = XCDR (XCAR (tail)); - p = XPROCESS (proc); - if (EQ (p->type, Qreal) && p->pid == pid) - break; - p = 0; - } - - /* Look for an asynchronous process whose pid hasn't been filled - in yet. */ - if (! p) - for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) - { - proc = XCDR (XCAR (tail)); - p = XPROCESS (proc); - if (p->pid == -1) - break; - p = 0; - } - - /* Change the status of the process that was found. */ - if (p) - { - int clear_desc_flag = 0; - - p->tick = ++process_tick; - p->raw_status = w; - p->raw_status_new = 1; - - /* If process has terminated, stop waiting for its output. */ - if ((WIFSIGNALED (w) || WIFEXITED (w)) - && p->infd >= 0) - clear_desc_flag = 1; - - /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */ - if (clear_desc_flag) - { - FD_CLR (p->infd, &input_wait_mask); - FD_CLR (p->infd, &non_keyboard_wait_mask); - } - - /* Tell wait_reading_process_output that it needs to wake up and - look around. */ - if (input_available_clear_time) - *input_available_clear_time = make_emacs_time (0, 0); - } - /* There was no asynchronous process found for that pid: we have - a synchronous process. */ - else - { - synch_process_alive = 0; - - /* Report the status of the synchronous process. */ - if (WIFEXITED (w)) - synch_process_retcode = WEXITSTATUS (w); - else if (WIFSIGNALED (w)) - synch_process_termsig = WTERMSIG (w); - - /* Tell wait_reading_process_output that it needs to wake up and - look around. */ - if (input_available_clear_time) - *input_available_clear_time = make_emacs_time (0, 0); - } -#endif -} - -#ifdef SIGCHLD - -/* On some systems, the SIGCHLD handler must return right away. If - any more processes want to signal us, we will get another signal. - Otherwise, loop around to use up all the processes that have - something to tell us. */ -#if (defined WINDOWSNT \ - || (defined USG && !defined GNU_LINUX \ - && !(defined HPUX && defined WNOHANG))) -enum { CAN_HANDLE_MULTIPLE_CHILDREN = 0 }; -#else -enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 }; -#endif - -static void -handle_child_signal (int sig) -{ - do - { - pid_t pid; + Lisp_Object proc = XCDR (XCAR (tail)); + struct Lisp_Process *p = XPROCESS (proc); int status; - do - pid = waitpid (-1, &status, WNOHANG | WUNTRACED); - while (pid < 0 && errno == EINTR); + if (p->alive && child_status_changed (p->pid, &status, WUNTRACED)) + { + /* Change the status of the process that was found. */ + p->tick = ++process_tick; + p->raw_status = status; + p->raw_status_new = 1; - /* PID == 0 means no processes found, PID == -1 means a real failure. - Either way, we have done all our job. */ - if (pid <= 0) - break; + /* If process has terminated, stop waiting for its output. */ + if (WIFSIGNALED (status) || WIFEXITED (status)) + { + int clear_desc_flag = 0; + p->alive = 0; + if (p->infd >= 0) + clear_desc_flag = 1; - record_child_status_change (pid, status); + /* clear_desc_flag avoids a compiler bug in Microsoft C. */ + if (clear_desc_flag) + { + FD_CLR (p->infd, &input_wait_mask); + FD_CLR (p->infd, &non_keyboard_wait_mask); + } + } + } } - while (CAN_HANDLE_MULTIPLE_CHILDREN); } static void @@ -6431,8 +6182,6 @@ deliver_child_signal (int sig) { deliver_process_signal (sig, handle_child_signal); } - -#endif /* SIGCHLD */ static Lisp_Object @@ -7281,7 +7030,6 @@ init_process_emacs (void) inhibit_sentinels = 0; -#ifdef SIGCHLD #ifndef CANNOT_DUMP if (! noninteractive || initialized) #endif @@ -7290,7 +7038,6 @@ init_process_emacs (void) emacs_sigaction_init (&action, deliver_child_signal); sigaction (SIGCHLD, &action, 0); } -#endif FD_ZERO (&input_wait_mask); FD_ZERO (&non_keyboard_wait_mask); @@ -7317,9 +7064,7 @@ init_process_emacs (void) #endif Vprocess_alist = Qnil; -#ifdef SIGCHLD deleted_pid_list = Qnil; -#endif for (i = 0; i < MAXDESC; i++) { chan_process[i] = Qnil; @@ -7357,9 +7102,7 @@ init_process_emacs (void) #ifdef HAVE_GETSOCKNAME ADD_SUBFEATURE (QCservice, Qt); #endif -#if defined (O_NONBLOCK) || defined (O_NDELAY) ADD_SUBFEATURE (QCserver, Qt); -#endif for (sopt = socket_options; sopt->name; sopt++) subfeatures = pure_cons (intern_c_string (sopt->name), subfeatures); @@ -7448,9 +7191,7 @@ syms_of_process (void) DEFSYM (Qlast_nonmenu_event, "last-nonmenu-event"); staticpro (&Vprocess_alist); -#ifdef SIGCHLD staticpro (&deleted_pid_list); -#endif #endif /* subprocesses */ |