X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/fabc1281e9cde34ff9a19d843316d2ceca8647ad..f9d1448f46d3d645e68fdd449758b50a1402d729:/src/sysdep.c diff --git a/src/sysdep.c b/src/sysdep.c index 183ee00522..5291c5d59a 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -21,9 +21,8 @@ along with GNU Emacs. If not, see . */ #define SYSTIME_INLINE EXTERN_INLINE -#include +#include #include -#include #ifdef HAVE_PWD_H #include #include @@ -55,6 +54,9 @@ along with GNU Emacs. If not, see . */ #ifdef WINDOWSNT #define read sys_read #define write sys_write +#ifndef STDERR_FILENO +#define STDERR_FILENO fileno(GetStdHandle(STD_ERROR_HANDLE)) +#endif #include #endif /* not WINDOWSNT */ @@ -99,7 +101,6 @@ along with GNU Emacs. If not, see . */ #define _P_WAIT 0 int _cdecl _spawnlp (int, const char *, const char *, ...); int _cdecl _getpid (void); -extern char *getwd (char *); #endif #include "syssignal.h" @@ -107,9 +108,6 @@ extern char *getwd (char *); static int emacs_get_tty (int, struct emacs_tty *); static int emacs_set_tty (int, struct emacs_tty *, int); -#if defined TIOCNOTTY || defined USG5 || defined CYGWIN -static _Noreturn void croak (char *); -#endif /* ULLONG_MAX is missing on Red Hat Linux 7.3; see Bug#11781. */ #ifndef ULLONG_MAX @@ -135,12 +133,12 @@ char* get_current_dir_name (void) { char *buf; - char *pwd; + char *pwd = getenv ("PWD"); struct stat dotstat, pwdstat; - /* If PWD is accurate, use it instead of calling getwd. PWD is + /* If PWD is accurate, use it instead of calling getcwd. PWD is sometimes a nicer name, and using it may avoid a fatal error if a parent directory is searchable but not readable. */ - if ((pwd = getenv ("PWD")) != 0 + if (pwd && (IS_DIRECTORY_SEP (*pwd) || (*pwd && IS_DEVICE_SEP (pwd[1]))) && stat (pwd, &pwdstat) == 0 && stat (".", &dotstat) == 0 @@ -156,7 +154,6 @@ get_current_dir_name (void) return NULL; strcpy (buf, pwd); } -#ifdef HAVE_GETCWD else { size_t buf_size = 1024; @@ -180,22 +177,6 @@ get_current_dir_name (void) return NULL; } } -#else - else - { - /* We need MAXPATHLEN here. */ - buf = malloc (MAXPATHLEN + 1); - if (!buf) - return NULL; - if (getwd (buf) == NULL) - { - int tmp_errno = errno; - free (buf); - errno = tmp_errno; - return NULL; - } - } -#endif return buf; } #endif @@ -283,71 +264,76 @@ init_baud_rate (int fd) -/* Set nonzero to make following function work under dbx - (at least for bsd). */ -int wait_debugging EXTERNALLY_VISIBLE; - #ifndef MSDOS -static void -wait_for_termination_1 (pid_t pid, int interruptible) +/* Wait for the subprocess with process id CHILD to terminate or change status. + CHILD must be a child process that has not been reaped. + If STATUS is non-null, store the waitpid-style exit status into *STATUS + and tell wait_reading_process_output that it needs to look around. + Use waitpid-style OPTIONS when waiting. + If INTERRUPTIBLE, this function is interruptible by a signal. + + Return CHILD if successful, 0 if no status is available; + the latter is possible only when options & NOHANG. */ +static pid_t +get_child_status (pid_t child, int *status, int options, bool interruptible) { - while (1) - { -#if (defined (BSD_SYSTEM) || defined (HPUX)) && !defined (__GNU__) - /* Note that kill returns -1 even if the process is just a zombie now. - But inevitably a SIGCHLD interrupt should be generated - and child_sig will do waitpid and make the process go away. */ - /* There is some indication that there is a bug involved with - termination of subprocesses, perhaps involving a kernel bug too, - but no idea what it is. Just as a hunch we signal SIGCHLD to see - if that causes the problem to go away or get worse. */ - sigsetmask (sigmask (SIGCHLD)); - if (0 > kill (pid, 0)) - { - sigsetmask (SIGEMPTYMASK); - kill (getpid (), SIGCHLD); - break; - } - if (wait_debugging) - sleep (1); - else - sigpause (SIGEMPTYMASK); -#else /* not BSD_SYSTEM, and not HPUX version >= 6 */ -#ifdef WINDOWSNT - wait (0); - break; -#else /* not WINDOWSNT */ - sigblock (sigmask (SIGCHLD)); - errno = 0; - if (kill (pid, 0) == -1 && errno == ESRCH) - { - sigunblock (sigmask (SIGCHLD)); - break; - } + pid_t pid; - sigsuspend (&empty_mask); -#endif /* not WINDOWSNT */ -#endif /* not BSD_SYSTEM, and not HPUX version >= 6 */ + /* Invoke waitpid only with a known process ID; do not invoke + waitpid with a nonpositive argument. Otherwise, Emacs might + reap an unwanted process by mistake. For example, invoking + waitpid (-1, ...) can mess up glib by reaping glib's subprocesses, + so that another thread running glib won't find them. */ + eassert (0 < child); + + while ((pid = waitpid (child, status, options)) < 0) + { + /* Check that CHILD is a child process that has not been reaped, + and that STATUS and OPTIONS are valid. Otherwise abort, + as continuing after this internal error could cause Emacs to + become confused and kill innocent-victim processes. */ + if (errno != EINTR) + emacs_abort (); + + /* Note: the MS-Windows emulation of waitpid calls QUIT + internally. */ if (interruptible) QUIT; } -} -/* Wait for subprocess with process id `pid' to terminate and - make sure it will get eliminated (not remain forever as a zombie) */ + /* If successful and status is requested, tell wait_reading_process_output + that it needs to wake up and look around. */ + if (pid && status && input_available_clear_time) + *input_available_clear_time = make_emacs_time (0, 0); + return pid; +} + +/* Wait for the subprocess with process id CHILD to terminate. + CHILD must be a child process that has not been reaped. + If STATUS is non-null, store the waitpid-style exit status into *STATUS + and tell wait_reading_process_output that it needs to look around. + If INTERRUPTIBLE, this function is interruptible by a signal. */ void -wait_for_termination (pid_t pid) +wait_for_termination (pid_t child, int *status, bool interruptible) { - wait_for_termination_1 (pid, 0); + get_child_status (child, status, 0, interruptible); } -/* Like the above, but allow keyboard interruption. */ -void -interruptible_wait_for_termination (pid_t pid) +/* Report whether the subprocess with process id CHILD has changed status. + Termination counts as a change of status. + CHILD must be a child process that has not been reaped. + If STATUS is non-null, store the waitpid-style exit status into *STATUS + and tell wait_reading_process_output that it needs to look around. + Use waitpid-style OPTIONS to check status, but do not wait. + + Return CHILD if successful, 0 if no status is available because + the process's state has not changed. */ +pid_t +child_status_changed (pid_t child, int *status, int options) { - wait_for_termination_1 (pid, 1); + return get_child_status (child, status, WNOHANG | options, 0); } /* @@ -456,11 +442,11 @@ child_setup_tty (int out) #endif /* not MSDOS */ -/* Record a signal code and the handler for it. */ +/* Record a signal code and the action for it. */ struct save_signal { int code; - void (*handler) (int); + struct sigaction action; }; static void save_signal_handlers (struct save_signal *); @@ -471,20 +457,15 @@ static void restore_signal_handlers (struct save_signal *); void sys_suspend (void) { -#if defined (SIGTSTP) && !defined (MSDOS) - - { - int pgrp = EMACS_GETPGRP (0); - EMACS_KILLPG (pgrp, SIGTSTP); - } - -#else /* No SIGTSTP */ +#ifndef DOS_NT + kill (0, SIGTSTP); +#else /* On a system where suspending is not implemented, instead fork a subshell and let it talk directly to the terminal while we wait. */ sys_subshell (); -#endif /* no SIGTSTP */ +#endif } /* Fork a subshell. */ @@ -496,7 +477,8 @@ sys_subshell (void) int st; char oldwd[MAXPATHLEN+1]; /* Fixed length is safe on MSDOS. */ #endif - int pid; + pid_t pid; + int status; struct save_signal saved_handlers[5]; Lisp_Object dir; unsigned char *volatile str_volatile = 0; @@ -506,7 +488,7 @@ sys_subshell (void) saved_handlers[0].code = SIGINT; saved_handlers[1].code = SIGQUIT; saved_handlers[2].code = SIGTERM; -#ifdef SIGIO +#ifdef USABLE_SIGIO saved_handlers[3].code = SIGIO; saved_handlers[4].code = 0; #else @@ -534,7 +516,6 @@ sys_subshell (void) #ifdef DOS_NT pid = 0; save_signal_handlers (saved_handlers); - synch_process_alive = 1; #else pid = vfork (); if (pid == -1) @@ -546,7 +527,7 @@ sys_subshell (void) const char *sh = 0; #ifdef DOS_NT /* MW, Aug 1993 */ - getwd (oldwd); + getcwd (oldwd, sizeof oldwd); if (sh == 0) sh = (char *) egetenv ("SUSPEND"); /* KFS, 1994-12-14 */ #endif @@ -603,14 +584,12 @@ sys_subshell (void) /* Do this now if we did not do it before. */ #ifndef MSDOS save_signal_handlers (saved_handlers); - synch_process_alive = 1; #endif #ifndef DOS_NT - wait_for_termination (pid); + wait_for_termination (pid, &status, 0); #endif restore_signal_handlers (saved_handlers); - synch_process_alive = 0; } static void @@ -618,8 +597,9 @@ save_signal_handlers (struct save_signal *saved_handlers) { while (saved_handlers->code) { - saved_handlers->handler - = (void (*) (int)) signal (saved_handlers->code, SIG_IGN); + struct sigaction action; + emacs_sigaction_init (&action, SIG_IGN); + sigaction (saved_handlers->code, &action, &saved_handlers->action); saved_handlers++; } } @@ -629,118 +609,149 @@ restore_signal_handlers (struct save_signal *saved_handlers) { while (saved_handlers->code) { - signal (saved_handlers->code, saved_handlers->handler); + sigaction (saved_handlers->code, &saved_handlers->action, 0); saved_handlers++; } } -#ifndef SIGIO -/* If SIGIO is broken, don't do anything. */ -void -init_sigio (int fd) -{ -} - -static void -reset_sigio (int fd) -{ -} - -void -request_sigio (void) -{ -} - -void -unrequest_sigio (void) -{ -} - -#else -#ifdef F_SETFL - +#ifdef USABLE_SIGIO static int old_fcntl_flags[MAXDESC]; +#endif void init_sigio (int fd) { -#ifdef FASYNC +#ifdef USABLE_SIGIO old_fcntl_flags[fd] = fcntl (fd, F_GETFL, 0) & ~FASYNC; fcntl (fd, F_SETFL, old_fcntl_flags[fd] | FASYNC); -#endif interrupts_deferred = 0; +#endif } static void reset_sigio (int fd) { -#ifdef FASYNC +#ifdef USABLE_SIGIO fcntl (fd, F_SETFL, old_fcntl_flags[fd]); #endif } -#ifdef FASYNC /* F_SETFL does not imply existence of FASYNC */ -/* XXX Uhm, FASYNC is not used anymore here. */ -/* XXX Yeah, but you need it for SIGIO, don't you? */ - void request_sigio (void) { +#ifdef USABLE_SIGIO + sigset_t unblocked; + if (noninteractive) return; -#ifdef SIGWINCH - sigunblock (sigmask (SIGWINCH)); -#endif - sigunblock (sigmask (SIGIO)); + sigemptyset (&unblocked); +# ifdef SIGWINCH + sigaddset (&unblocked, SIGWINCH); +# endif + sigaddset (&unblocked, SIGIO); + pthread_sigmask (SIG_UNBLOCK, &unblocked, 0); interrupts_deferred = 0; +#endif } void unrequest_sigio (void) { +#ifdef USABLE_SIGIO + sigset_t blocked; + if (noninteractive) return; -#if 0 /* XXX What's wrong with blocking SIGIO under X? */ - if (x_display_list) - return; + sigemptyset (&blocked); +# ifdef SIGWINCH + sigaddset (&blocked, SIGWINCH); +# endif + sigaddset (&blocked, SIGIO); + pthread_sigmask (SIG_BLOCK, &blocked, 0); + interrupts_deferred = 1; #endif +} -#ifdef SIGWINCH - sigblock (sigmask (SIGWINCH)); +void +ignore_sigio (void) +{ +#ifdef USABLE_SIGIO + signal (SIGIO, SIG_IGN); #endif - sigblock (sigmask (SIGIO)); - interrupts_deferred = 1; } -#else /* no FASYNC */ -#ifndef MSDOS + +/* Saving and restoring the process group of Emacs's terminal. */ -void -request_sigio (void) -{ - if (noninteractive || read_socket_hook) - return; +/* The process group of which Emacs was a member when it initially + started. - croak ("request_sigio"); -} + If Emacs was in its own process group (i.e. inherited_pgroup == + getpid ()), then we know we're running under a shell with job + control (Emacs would never be run as part of a pipeline). + Everything is fine. + + If Emacs was not in its own process group, then we know we're + running under a shell (or a caller) that doesn't know how to + separate itself from Emacs (like sh). Emacs must be in its own + process group in order to receive SIGIO correctly. In this + situation, we put ourselves in our own pgroup, forcibly set the + tty's pgroup to our pgroup, and make sure to restore and reinstate + the tty's pgroup just like any other terminal setting. If + inherited_group was not the tty's pgroup, then we'll get a + SIGTTmumble when we try to change the tty's pgroup, and a CONT if + it goes foreground in the future, which is what should happen. */ + +static pid_t inherited_pgroup; void -unrequest_sigio (void) +init_foreground_group (void) { - if (noninteractive || read_socket_hook) - return; + pid_t pgrp = getpgrp (); + inherited_pgroup = getpid () == pgrp ? 0 : pgrp; +} - croak ("unrequest_sigio"); +/* Safely set a controlling terminal FD's process group to PGID. + If we are not in the foreground already, POSIX requires tcsetpgrp + to deliver a SIGTTOU signal, which would stop us. This is an + annoyance, so temporarily ignore the signal. + + In practice, platforms lacking SIGTTOU also lack tcsetpgrp, so + skip all this unless SIGTTOU is defined. */ +static void +tcsetpgrp_without_stopping (int fd, pid_t pgid) +{ +#ifdef SIGTTOU + signal_handler_t handler; + block_input (); + handler = signal (SIGTTOU, SIG_IGN); + tcsetpgrp (fd, pgid); + signal (SIGTTOU, handler); + unblock_input (); +#endif } -#endif /* MSDOS */ -#endif /* FASYNC */ -#endif /* F_SETFL */ -#endif /* SIGIO */ +/* Split off the foreground process group to Emacs alone. When we are + in the foreground, but not started in our own process group, + redirect the tty device handle FD to point to our own process + group. FD must be the file descriptor of the controlling tty. */ +static void +narrow_foreground_group (int fd) +{ + if (inherited_pgroup && setpgid (0, 0) == 0) + tcsetpgrp_without_stopping (fd, getpid ()); +} +/* Set the tty to our original foreground group. */ +static void +widen_foreground_group (int fd) +{ + if (inherited_pgroup && setpgid (0, inherited_pgroup) == 0) + tcsetpgrp_without_stopping (fd, inherited_pgroup); +} /* Getting and setting emacs_tty structures. */ @@ -858,6 +869,8 @@ init_sys_modes (struct tty_display_info *tty_out) if (!tty_out->output) return; /* The tty is suspended. */ + narrow_foreground_group (fileno (tty_out->input)); + if (! tty_out->old_tty) tty_out->old_tty = xmalloc (sizeof *tty_out->old_tty); @@ -1027,8 +1040,7 @@ init_sys_modes (struct tty_display_info *tty_out) #endif #endif -#ifdef F_SETFL -#ifdef F_GETOWN /* F_SETFL does not imply existence of F_GETOWN */ +#ifdef F_GETOWN if (interrupt_input) { old_fcntl_owner[fileno (tty_out->input)] = @@ -1046,7 +1058,6 @@ init_sys_modes (struct tty_display_info *tty_out) #endif /* HAVE_GPM */ } #endif /* F_GETOWN */ -#endif /* F_SETFL */ #ifdef _IOFBF /* This symbol is defined on recent USG systems. @@ -1266,8 +1277,8 @@ reset_sys_modes (struct tty_display_info *tty_out) fsync (fileno (tty_out->output)); #endif -#ifdef F_SETFL -#ifdef F_SETOWN /* F_SETFL does not imply existence of F_SETOWN */ +#ifndef DOS_NT +#ifdef F_SETOWN if (interrupt_input) { reset_sigio (fileno (tty_out->input)); @@ -1275,11 +1286,9 @@ reset_sys_modes (struct tty_display_info *tty_out) old_fcntl_owner[fileno (tty_out->input)]); } #endif /* F_SETOWN */ -#ifdef O_NDELAY fcntl (fileno (tty_out->input), F_SETFL, - fcntl (fileno (tty_out->input), F_GETFL, 0) & ~O_NDELAY); + fcntl (fileno (tty_out->input), F_GETFL, 0) & ~O_NONBLOCK); #endif -#endif /* F_SETFL */ if (tty_out->old_tty) while (emacs_set_tty (fileno (tty_out->input), @@ -1290,6 +1299,7 @@ reset_sys_modes (struct tty_display_info *tty_out) dos_ttcooked (); #endif + widen_foreground_group (fileno (tty_out->input)); } #ifdef HAVE_PTYS @@ -1470,121 +1480,224 @@ init_system_name (void) } } -/* POSIX signals support - DJB */ -/* Anyone with POSIX signals should have ANSI C declarations */ - sigset_t empty_mask; -#ifndef WINDOWSNT +static struct sigaction process_fatal_action; -signal_handler_t -sys_signal (int signal_number, signal_handler_t action) -{ - struct sigaction new_action, old_action; - sigemptyset (&new_action.sa_mask); - new_action.sa_handler = action; - new_action.sa_flags = 0; -#if defined (SA_RESTART) - /* Emacs mostly works better with restartable system services. If this - flag exists, we probably want to turn it on here. - However, on some systems (only hpux11 at present) this resets the - timeout of `select' which means that `select' never finishes if - it keeps getting signals. - We define BROKEN_SA_RESTART on those systems. */ - /* It's not clear why the comment above says "mostly works better". --Stef - When SYNC_INPUT is set, we don't want SA_RESTART because we need to poll +static int +emacs_sigaction_flags (void) +{ +#ifdef SA_RESTART + /* SA_RESTART causes interruptible functions with timeouts (e.g., + 'select') to reset their timeout on some platforms (e.g., + HP-UX 11), which is not what we want. Also, when Emacs is + interactive, we don't want SA_RESTART because we need to poll for pending input so we need long-running syscalls to be interrupted - after a signal that sets the interrupt_input_pending flag. */ - /* Non-interactive keyboard input goes through stdio, where we always - want restartable system calls. */ -# if defined (BROKEN_SA_RESTART) || defined (SYNC_INPUT) + after a signal that sets pending_signals. + + Non-interactive keyboard input goes through stdio, where we + always want restartable system calls. */ if (noninteractive) -# endif - new_action.sa_flags = SA_RESTART; + return SA_RESTART; #endif - sigaction (signal_number, &new_action, &old_action); - return (old_action.sa_handler); + return 0; } -#endif /* WINDOWSNT */ +/* Store into *ACTION a signal action suitable for Emacs, with handler + HANDLER. */ +void +emacs_sigaction_init (struct sigaction *action, signal_handler_t handler) +{ + sigemptyset (&action->sa_mask); + + /* When handling a signal, block nonfatal system signals that are caught + by Emacs. This makes race conditions less likely. */ + sigaddset (&action->sa_mask, SIGALRM); + sigaddset (&action->sa_mask, SIGCHLD); +#ifdef SIGDANGER + sigaddset (&action->sa_mask, SIGDANGER); +#endif +#ifdef PROFILER_CPU_SUPPORT + sigaddset (&action->sa_mask, SIGPROF); +#endif +#ifdef SIGWINCH + sigaddset (&action->sa_mask, SIGWINCH); +#endif + if (! noninteractive) + { + sigaddset (&action->sa_mask, SIGINT); + sigaddset (&action->sa_mask, SIGQUIT); +#ifdef USABLE_SIGIO + sigaddset (&action->sa_mask, SIGIO); +#endif + } + + if (! IEEE_FLOATING_POINT) + sigaddset (&action->sa_mask, SIGFPE); + + action->sa_handler = handler; + action->sa_flags = emacs_sigaction_flags (); +} -#ifndef __GNUC__ -/* If we're compiling with GCC, we don't need this function, since it - can be written as a macro. */ -sigset_t -sys_sigmask (int sig) +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD +static pthread_t main_thread; +#endif + +/* SIG has arrived at the current process. Deliver it to the main + thread, which should handle it with HANDLER. + + If we are on the main thread, handle the signal SIG with HANDLER. + Otherwise, redirect the signal to the main thread, blocking it from + this thread. POSIX says any thread can receive a signal that is + associated with a process, process group, or asynchronous event. + On GNU/Linux that is not true, but for other systems (FreeBSD at + least) it is. */ +void +deliver_process_signal (int sig, signal_handler_t handler) { - sigset_t mask; - sigemptyset (&mask); - sigaddset (&mask, sig); - return mask; + /* Preserve errno, to avoid race conditions with signal handlers that + might change errno. Races can occur even in single-threaded hosts. */ + int old_errno = errno; + + bool on_main_thread = true; +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD + if (! pthread_equal (pthread_self (), main_thread)) + { + sigset_t blocked; + sigemptyset (&blocked); + sigaddset (&blocked, sig); + pthread_sigmask (SIG_BLOCK, &blocked, 0); + pthread_kill (main_thread, sig); + on_main_thread = false; + } +#endif + if (on_main_thread) + handler (sig); + + errno = old_errno; +} + +/* Static location to save a fatal backtrace in a thread. + FIXME: If two subsidiary threads fail simultaneously, the resulting + backtrace may be garbage. */ +enum { BACKTRACE_LIMIT_MAX = 500 }; +static void *thread_backtrace_buffer[BACKTRACE_LIMIT_MAX + 1]; +static int thread_backtrace_npointers; + +/* SIG has arrived at the current thread. + If we are on the main thread, handle the signal SIG with HANDLER. + Otherwise, this is a fatal error in the handling thread. */ +static void +deliver_thread_signal (int sig, signal_handler_t handler) +{ + int old_errno = errno; + +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD + if (! pthread_equal (pthread_self (), main_thread)) + { + thread_backtrace_npointers + = backtrace (thread_backtrace_buffer, BACKTRACE_LIMIT_MAX); + sigaction (sig, &process_fatal_action, 0); + pthread_kill (main_thread, sig); + + /* Avoid further damage while the main thread is exiting. */ + while (1) + sigsuspend (&empty_mask); + } +#endif + + handler (sig); + errno = old_errno; } + +#if !HAVE_DECL_SYS_SIGLIST +# undef sys_siglist +# ifdef _sys_siglist +# define sys_siglist _sys_siglist +# else +# define sys_siglist my_sys_siglist +static char const *sys_siglist[NSIG]; +# endif #endif -/* I'd like to have these guys return pointers to the mask storage in here, - but there'd be trouble if the code was saving multiple masks. I'll be - safe and pass the structure. It normally won't be more than 2 bytes - anyhow. - DJB */ +#ifdef _sys_nsig +# define sys_siglist_entries _sys_nsig +#else +# define sys_siglist_entries NSIG +#endif + +/* Handle bus errors, invalid instruction, etc. */ +static void +handle_fatal_signal (int sig) +{ + terminate_due_to_signal (sig, 40); +} -sigset_t -sys_sigblock (sigset_t new_mask) +static void +deliver_fatal_signal (int sig) { - sigset_t old_mask; - pthread_sigmask (SIG_BLOCK, &new_mask, &old_mask); - return (old_mask); + deliver_process_signal (sig, handle_fatal_signal); } -sigset_t -sys_sigunblock (sigset_t new_mask) +static void +deliver_fatal_thread_signal (int sig) { - sigset_t old_mask; - pthread_sigmask (SIG_UNBLOCK, &new_mask, &old_mask); - return (old_mask); + deliver_thread_signal (sig, handle_fatal_signal); } -sigset_t -sys_sigsetmask (sigset_t new_mask) +static _Noreturn void +handle_arith_signal (int sig) { - sigset_t old_mask; - pthread_sigmask (SIG_SETMASK, &new_mask, &old_mask); - return (old_mask); + pthread_sigmask (SIG_SETMASK, &empty_mask, 0); + xsignal0 (Qarith_error); } - -#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST -static char *my_sys_siglist[NSIG]; -# ifdef sys_siglist -# undef sys_siglist -# endif -# define sys_siglist my_sys_siglist -#endif +static void +deliver_arith_signal (int sig) +{ + deliver_thread_signal (sig, handle_arith_signal); +} + +/* Treat SIG as a terminating signal, unless it is already ignored and + we are in --batch mode. Among other things, this makes nohup work. */ +static void +maybe_fatal_sig (int sig) +{ + bool catch_sig = !noninteractive; + if (!catch_sig) + { + struct sigaction old_action; + sigaction (sig, 0, &old_action); + catch_sig = old_action.sa_handler != SIG_IGN; + } + if (catch_sig) + sigaction (sig, &process_fatal_action, 0); +} void -init_signals (void) +init_signals (bool dumping) { + struct sigaction thread_fatal_action; + struct sigaction action; + sigemptyset (&empty_mask); -#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST +#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD + main_thread = pthread_self (); +#endif + +#if !HAVE_DECL_SYS_SIGLIST && !defined _sys_siglist if (! initialized) { -# ifdef SIGABRT sys_siglist[SIGABRT] = "Aborted"; -# endif # ifdef SIGAIO sys_siglist[SIGAIO] = "LAN I/O interrupt"; # endif -# ifdef SIGALRM sys_siglist[SIGALRM] = "Alarm clock"; -# endif # ifdef SIGBUS sys_siglist[SIGBUS] = "Bus error"; # endif -# ifdef SIGCLD - sys_siglist[SIGCLD] = "Child status changed"; -# endif -# ifdef SIGCHLD sys_siglist[SIGCHLD] = "Child status changed"; -# endif # ifdef SIGCONT sys_siglist[SIGCONT] = "Continued"; # endif @@ -1597,24 +1710,16 @@ init_signals (void) # ifdef SIGEMT sys_siglist[SIGEMT] = "Emulation trap"; # endif -# ifdef SIGFPE sys_siglist[SIGFPE] = "Arithmetic exception"; -# endif # ifdef SIGFREEZE sys_siglist[SIGFREEZE] = "SIGFREEZE"; # endif # ifdef SIGGRANT sys_siglist[SIGGRANT] = "Monitor mode granted"; # endif -# ifdef SIGHUP sys_siglist[SIGHUP] = "Hangup"; -# endif -# ifdef SIGILL sys_siglist[SIGILL] = "Illegal instruction"; -# endif -# ifdef SIGINT sys_siglist[SIGINT] = "Interrupt"; -# endif # ifdef SIGIO sys_siglist[SIGIO] = "I/O possible"; # endif @@ -1624,9 +1729,7 @@ init_signals (void) # ifdef SIGIOT sys_siglist[SIGIOT] = "IOT trap"; # endif -# ifdef SIGKILL sys_siglist[SIGKILL] = "Killed"; -# endif # ifdef SIGLOST sys_siglist[SIGLOST] = "Resource lost"; # endif @@ -1639,9 +1742,7 @@ init_signals (void) # ifdef SIGPHONE sys_siglist[SIGWIND] = "SIGPHONE"; # endif -# ifdef SIGPIPE sys_siglist[SIGPIPE] = "Broken pipe"; -# endif # ifdef SIGPOLL sys_siglist[SIGPOLL] = "Pollable event occurred"; # endif @@ -1654,18 +1755,14 @@ init_signals (void) # ifdef SIGPWR sys_siglist[SIGPWR] = "Power-fail restart"; # endif -# ifdef SIGQUIT sys_siglist[SIGQUIT] = "Quit"; -# endif # ifdef SIGRETRACT sys_siglist[SIGRETRACT] = "Need to relinquish monitor mode"; # endif # ifdef SIGSAK sys_siglist[SIGSAK] = "Secure attention"; # endif -# ifdef SIGSEGV sys_siglist[SIGSEGV] = "Segmentation violation"; -# endif # ifdef SIGSOUND sys_siglist[SIGSOUND] = "Sound completed"; # endif @@ -1678,9 +1775,7 @@ init_signals (void) # ifdef SIGSYS sys_siglist[SIGSYS] = "Bad argument to system call"; # endif -# ifdef SIGTERM sys_siglist[SIGTERM] = "Terminated"; -# endif # ifdef SIGTHAW sys_siglist[SIGTHAW] = "SIGTHAW"; # endif @@ -1724,7 +1819,130 @@ init_signals (void) sys_siglist[SIGXFSZ] = "File size limit exceeded"; # endif } -#endif /* !defined HAVE_STRSIGNAL && !defined HAVE_DECL_SYS_SIGLIST */ +#endif /* !HAVE_DECL_SYS_SIGLIST && !_sys_siglist */ + + /* Don't alter signal handlers if dumping. On some machines, + changing signal handlers sets static data that would make signals + fail to work right when the dumped Emacs is run. */ + if (dumping) + return; + + sigfillset (&process_fatal_action.sa_mask); + process_fatal_action.sa_handler = deliver_fatal_signal; + process_fatal_action.sa_flags = emacs_sigaction_flags (); + + sigfillset (&thread_fatal_action.sa_mask); + thread_fatal_action.sa_handler = deliver_fatal_thread_signal; + thread_fatal_action.sa_flags = process_fatal_action.sa_flags; + + /* SIGINT may need special treatment on MS-Windows. See + http://lists.gnu.org/archive/html/emacs-devel/2010-09/msg01062.html + Please update the doc of kill-emacs, kill-emacs-hook, and + NEWS if you change this. */ + + maybe_fatal_sig (SIGHUP); + maybe_fatal_sig (SIGINT); + maybe_fatal_sig (SIGTERM); + + /* Emacs checks for write errors, so it can safely ignore SIGPIPE. + However, in batch mode leave SIGPIPE alone, as that causes Emacs + to behave more like typical batch applications do. */ + if (! noninteractive) + signal (SIGPIPE, SIG_IGN); + + sigaction (SIGQUIT, &process_fatal_action, 0); + sigaction (SIGILL, &thread_fatal_action, 0); + sigaction (SIGTRAP, &thread_fatal_action, 0); + + /* Typically SIGFPE is thread-specific and is fatal, like SIGILL. + But on a non-IEEE host SIGFPE can come from a trap in the Lisp + interpreter's floating point operations, so treat SIGFPE as an + arith-error if it arises in the main thread. */ + if (IEEE_FLOATING_POINT) + sigaction (SIGFPE, &thread_fatal_action, 0); + else + { + emacs_sigaction_init (&action, deliver_arith_signal); + sigaction (SIGFPE, &action, 0); + } + +#ifdef SIGUSR1 + add_user_signal (SIGUSR1, "sigusr1"); +#endif +#ifdef SIGUSR2 + add_user_signal (SIGUSR2, "sigusr2"); +#endif + sigaction (SIGABRT, &thread_fatal_action, 0); +#ifdef SIGPRE + sigaction (SIGPRE, &thread_fatal_action, 0); +#endif +#ifdef SIGORE + sigaction (SIGORE, &thread_fatal_action, 0); +#endif +#ifdef SIGUME + sigaction (SIGUME, &thread_fatal_action, 0); +#endif +#ifdef SIGDLK + sigaction (SIGDLK, &process_fatal_action, 0); +#endif +#ifdef SIGCPULIM + sigaction (SIGCPULIM, &process_fatal_action, 0); +#endif +#ifdef SIGIOT + sigaction (SIGIOT, &thread_fatal_action, 0); +#endif +#ifdef SIGEMT + sigaction (SIGEMT, &thread_fatal_action, 0); +#endif +#ifdef SIGBUS + sigaction (SIGBUS, &thread_fatal_action, 0); +#endif + sigaction (SIGSEGV, &thread_fatal_action, 0); +#ifdef SIGSYS + sigaction (SIGSYS, &thread_fatal_action, 0); +#endif + sigaction (SIGTERM, &process_fatal_action, 0); +#ifdef SIGPROF + signal (SIGPROF, SIG_IGN); +#endif +#ifdef SIGVTALRM + sigaction (SIGVTALRM, &process_fatal_action, 0); +#endif +#ifdef SIGXCPU + sigaction (SIGXCPU, &process_fatal_action, 0); +#endif +#ifdef SIGXFSZ + sigaction (SIGXFSZ, &process_fatal_action, 0); +#endif + +#ifdef SIGDANGER + /* This just means available memory is getting low. */ + emacs_sigaction_init (&action, deliver_danger_signal); + sigaction (SIGDANGER, &action, 0); +#endif + + /* AIX-specific signals. */ +#ifdef SIGGRANT + sigaction (SIGGRANT, &process_fatal_action, 0); +#endif +#ifdef SIGMIGRATE + sigaction (SIGMIGRATE, &process_fatal_action, 0); +#endif +#ifdef SIGMSG + sigaction (SIGMSG, &process_fatal_action, 0); +#endif +#ifdef SIGRETRACT + sigaction (SIGRETRACT, &process_fatal_action, 0); +#endif +#ifdef SIGSAK + sigaction (SIGSAK, &process_fatal_action, 0); +#endif +#ifdef SIGSOUND + sigaction (SIGSOUND, &process_fatal_action, 0); +#endif +#ifdef SIGTALRM + sigaction (SIGTALRM, &thread_fatal_action, 0); +#endif } #ifndef HAVE_RANDOM @@ -1765,19 +1983,37 @@ init_signals (void) #endif /* !RAND_BITS */ void -seed_random (long int arg) +seed_random (void *seed, ptrdiff_t seed_size) { +#if defined HAVE_RANDOM || ! defined HAVE_LRAND48 + unsigned int arg = 0; +#else + long int arg = 0; +#endif + unsigned char *argp = (unsigned char *) &arg; + unsigned char *seedp = seed; + ptrdiff_t i; + for (i = 0; i < seed_size; i++) + argp[i % sizeof arg] ^= seedp[i]; #ifdef HAVE_RANDOM - srandom ((unsigned int)arg); + srandom (arg); #else # ifdef HAVE_LRAND48 srand48 (arg); # else - srand ((unsigned int)arg); + srand (arg); # endif #endif } +void +init_random (void) +{ + EMACS_TIME t = current_emacs_time (); + uintmax_t v = getpid () ^ EMACS_SECS (t) ^ EMACS_NSECS (t); + seed_random (&v, sizeof v); +} + /* * Return a nonnegative random integer out of whatever we've got. * It contains enough bits to make a random (signed) Emacs fixnum. @@ -1838,6 +2074,46 @@ snprintf (char *buf, size_t bufsize, char const *format, ...) } #endif +/* If a backtrace is available, output the top lines of it to stderr. + Do not output more than BACKTRACE_LIMIT or BACKTRACE_LIMIT_MAX lines. + This function may be called from a signal handler, so it should + not invoke async-unsafe functions like malloc. */ +void +emacs_backtrace (int backtrace_limit) +{ + void *main_backtrace_buffer[BACKTRACE_LIMIT_MAX + 1]; + int bounded_limit = min (backtrace_limit, BACKTRACE_LIMIT_MAX); + void *buffer; + int npointers; + + if (thread_backtrace_npointers) + { + buffer = thread_backtrace_buffer; + npointers = thread_backtrace_npointers; + } + else + { + buffer = main_backtrace_buffer; + npointers = backtrace (buffer, bounded_limit + 1); + } + + if (npointers) + { + ignore_value (write (STDERR_FILENO, "\nBacktrace:\n", 12)); + backtrace_symbols_fd (buffer, npointers, STDERR_FILENO); + if (bounded_limit < npointers) + ignore_value (write (STDERR_FILENO, "...\n", 4)); + } +} + +#ifndef HAVE_NTGUI +void +emacs_abort (void) +{ + terminate_due_to_signal (SIGABRT, 10); +} +#endif + int emacs_open (const char *path, int oflag, int mode) { @@ -1915,11 +2191,10 @@ emacs_write (int fildes, const char *buf, ptrdiff_t nbyte) { if (errno == EINTR) { -#ifdef SYNC_INPUT /* I originally used `QUIT' but that might causes files to be truncated if you hit C-g in the middle of it. --Stef */ - process_pending_signals (); -#endif + if (pending_signals) + process_pending_signals (); continue; } else @@ -1950,95 +2225,6 @@ emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE]) &emacs_norealloc_allocator, careadlinkatcwd); } -#ifdef USG -/* - * All of the following are for USG. - * - * On USG systems the system calls are INTERRUPTIBLE by signals - * that the user program has elected to catch. Thus the system call - * must be retried in these cases. To handle this without massive - * changes in the source code, we remap the standard system call names - * to names for our own functions in sysdep.c that do the system call - * with retries. Actually, for portability reasons, it is good - * programming practice, as this example shows, to limit all actual - * system calls to a single occurrence in the source. Sure, this - * adds an extra level of function call overhead but it is almost - * always negligible. Fred Fish, Unisoft Systems Inc. - */ - -/* - * Warning, this function may not duplicate 4.2 action properly - * under error conditions. - */ - -#if !defined (HAVE_GETWD) || defined (BROKEN_GETWD) - -#ifndef MAXPATHLEN -/* In 4.1, param.h fails to define this. */ -#define MAXPATHLEN 1024 -#endif - -char * -getwd (char *pathname) -{ - char *npath, *spath; - extern char *getcwd (char *, size_t); - - BLOCK_INPUT; /* getcwd uses malloc */ - spath = npath = getcwd ((char *) 0, MAXPATHLEN); - if (spath == 0) - { - UNBLOCK_INPUT; - return spath; - } - /* On Altos 3068, getcwd can return @hostname/dir, so discard - up to first slash. Should be harmless on other systems. */ - while (*npath && *npath != '/') - npath++; - strcpy (pathname, npath); - free (spath); /* getcwd uses malloc */ - UNBLOCK_INPUT; - return pathname; -} - -#endif /* !defined (HAVE_GETWD) || defined (BROKEN_GETWD) */ - -/* - * This function will go away as soon as all the stubs fixed. (fnf) - */ - -void -croak (char *badfunc) -{ - printf ("%s not yet implemented\r\n", badfunc); - reset_all_sys_modes (); - exit (1); -} - -#endif /* USG */ - -/* Directory routines for systems that don't have them. */ - -#ifdef HAVE_DIRENT_H - -#include - -#if !defined (HAVE_CLOSEDIR) - -int -closedir (DIR *dirp /* stream from opendir */) -{ - int rtnval; - - rtnval = emacs_close (dirp->dd_fd); - xfree (dirp); - - return rtnval; -} -#endif /* not HAVE_CLOSEDIR */ -#endif /* HAVE_DIRENT_H */ - - /* Return a struct timeval that is roughly equivalent to T. Use the least timeval not less than T. Return an extremal value if the result would overflow. */ @@ -2078,40 +2264,27 @@ set_file_times (int fd, const char *filename, return fdutimens (fd, filename, timespec); } -#ifndef HAVE_STRSIGNAL -char * -strsignal (int code) +/* Like strsignal, except async-signal-safe, and this function typically + returns a string in the C locale rather than the current locale. */ +char const * +safe_strsignal (int code) { - char *signame = 0; + char const *signame = 0; - if (0 <= code && code < NSIG) - { - /* Cast to suppress warning if the table has const char *. */ - signame = (char *) sys_siglist[code]; - } + if (0 <= code && code < sys_siglist_entries) + signame = sys_siglist[code]; + if (! signame) + signame = "Unknown signal"; return signame; } -#endif /* HAVE_STRSIGNAL */ #ifndef DOS_NT /* For make-serial-process */ int serial_open (char *port) { - int fd = -1; - - fd = emacs_open ((char*) port, - O_RDWR -#ifdef O_NONBLOCK - | O_NONBLOCK -#else - | O_NDELAY -#endif -#ifdef O_NOCTTY - | O_NOCTTY -#endif - , 0); + int fd = emacs_open (port, O_RDWR | O_NOCTTY | O_NONBLOCK, 0); if (fd < 0) { error ("Could not open %s: %s", @@ -2444,7 +2617,7 @@ get_up_time (void) FILE *fup; EMACS_TIME up = make_emacs_time (0, 0); - BLOCK_INPUT; + block_input (); fup = fopen ("/proc/uptime", "r"); if (fup) @@ -2475,7 +2648,7 @@ get_up_time (void) } fclose (fup); } - UNBLOCK_INPUT; + unblock_input (); return up; } @@ -2489,7 +2662,7 @@ procfs_ttyname (int rdev) FILE *fdev = NULL; char name[PATH_MAX]; - BLOCK_INPUT; + block_input (); fdev = fopen ("/proc/tty/drivers", "r"); if (fdev) @@ -2521,7 +2694,7 @@ procfs_ttyname (int rdev) } fclose (fdev); } - UNBLOCK_INPUT; + unblock_input (); return build_string (name); } @@ -2531,7 +2704,7 @@ procfs_get_total_memory (void) FILE *fmem = NULL; unsigned long retval = 2 * 1024 * 1024; /* default: 2GB */ - BLOCK_INPUT; + block_input (); fmem = fopen ("/proc/meminfo", "r"); if (fmem) @@ -2550,7 +2723,7 @@ procfs_get_total_memory (void) } fclose (fmem); } - UNBLOCK_INPUT; + unblock_input (); return retval; } @@ -2596,17 +2769,17 @@ system_process_attributes (Lisp_Object pid) /* euid egid */ uid = st.st_uid; attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (uid)), attrs); - BLOCK_INPUT; + block_input (); pw = getpwuid (uid); - UNBLOCK_INPUT; + unblock_input (); if (pw) attrs = Fcons (Fcons (Quser, build_string (pw->pw_name)), attrs); gid = st.st_gid; attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (gid)), attrs); - BLOCK_INPUT; + block_input (); gr = getgrgid (gid); - UNBLOCK_INPUT; + unblock_input (); if (gr) attrs = Fcons (Fcons (Qgroup, build_string (gr->gr_name)), attrs); @@ -2834,17 +3007,17 @@ system_process_attributes (Lisp_Object pid) /* euid egid */ uid = st.st_uid; attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (uid)), attrs); - BLOCK_INPUT; + block_input (); pw = getpwuid (uid); - UNBLOCK_INPUT; + unblock_input (); if (pw) attrs = Fcons (Fcons (Quser, build_string (pw->pw_name)), attrs); gid = st.st_gid; attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (gid)), attrs); - BLOCK_INPUT; + block_input (); gr = getgrgid (gid); - UNBLOCK_INPUT; + unblock_input (); if (gr) attrs = Fcons (Fcons (Qgroup, build_string (gr->gr_name)), attrs); @@ -2965,17 +3138,17 @@ system_process_attributes (Lisp_Object pid) attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (proc.ki_uid)), attrs); - BLOCK_INPUT; + block_input (); pw = getpwuid (proc.ki_uid); - UNBLOCK_INPUT; + unblock_input (); if (pw) attrs = Fcons (Fcons (Quser, build_string (pw->pw_name)), attrs); attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (proc.ki_svgid)), attrs); - BLOCK_INPUT; + block_input (); gr = getgrgid (proc.ki_svgid); - UNBLOCK_INPUT; + unblock_input (); if (gr) attrs = Fcons (Fcons (Qgroup, build_string (gr->gr_name)), attrs); @@ -3015,9 +3188,9 @@ system_process_attributes (Lisp_Object pid) attrs = Fcons (Fcons (Qpgrp, make_fixnum_or_float (proc.ki_pgid)), attrs); attrs = Fcons (Fcons (Qsess, make_fixnum_or_float (proc.ki_sid)), attrs); - BLOCK_INPUT; + block_input (); ttyname = proc.ki_tdev == NODEV ? NULL : devname (proc.ki_tdev, S_IFCHR); - UNBLOCK_INPUT; + unblock_input (); if (ttyname) attrs = Fcons (Fcons (Qtty, build_string (ttyname)), attrs);