From 4ebbdd6712c1966406b40d2673464949775cbd7a Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 9 Jul 2013 00:04:48 -0700 Subject: Handle errno and exit status a bit more carefully. * lib/ignore-value.h: Remove this gnulib-imported file. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * admin/merge-gnulib (GNULIB_MODULES): Remove ignore-value. * src/callproc.c (child_setup) [!DOS_NT]: Don't try to stuff an error number into an exit status. Instead, use EXIT_CANCELED. (child_setup) [!MSDOS]: Avoid possible deadlock with vfork. * src/callproc.c (relocate_fd): * src/emacs.c (close_output_streams, main): * src/process.c (create_process): * src/sysdep.c (sys_subshell) [!DOS_NT || !WINDOWSNT]: Use emacs_perror for simplicity. * src/callproc.c (relocate_fd, main): * src/sysdep.c (sys_subshell): Exit with EXIT_CANCELED etc., not 1, when exec setup fails. (shut_down_emacs): Use emacs_write, not write. * src/emacs.c, src/sysdep.c: Don't include . * src/fileio.c (Fcopy_file, e_write): * src/nsterm.m (ns_select): * src/process.c (send_process): * src/sound.c (vox_write): Use emacs_write_sig, not emacs_write. * src/lisp.h (emacs_write_sig, emacs_perror): New decls. * src/process.h (EXIT_CANCELED), EXIT_CANNOT_INVOKE, EXIT_ENOENT): New constants. * src/sysdep.c (emacs_backtrace): Use emacs_write, not ignore_value of write. (emacs_full_write): New function. (emacs_write): Rewrite to use it. (emacswrite_sig, emacs_perror): New functions. * src/xrdb.c (fatal): Don't invoke perror, since errno might be garbage. --- src/ChangeLog | 31 +++++++++++++++++++++ src/callproc.c | 21 +++++++-------- src/emacs.c | 23 +++++++--------- src/fileio.c | 12 ++++----- src/lisp.h | 3 +++ src/nsterm.m | 4 +-- src/process.c | 10 +++---- src/process.h | 8 ++++++ src/sound.c | 2 +- src/sysdep.c | 85 +++++++++++++++++++++++++++++++++++++++++++--------------- src/xrdb.c | 5 +--- 11 files changed, 138 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/ChangeLog b/src/ChangeLog index 9be0b59ee06..8b152f4a176 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,34 @@ +2013-07-09 Paul Eggert + + Handle errno and exit status a bit more carefully. + * callproc.c (child_setup) [!DOS_NT]: Don't try to stuff an error + number into an exit status. Instead, use EXIT_CANCELED. + (child_setup) [!MSDOS]: Avoid possible deadlock with vfork. + * callproc.c (relocate_fd): + * emacs.c (close_output_streams, main): + * process.c (create_process): + * sysdep.c (sys_subshell) [!DOS_NT || !WINDOWSNT]: + Use emacs_perror for simplicity. + * callproc.c (relocate_fd, main): + * sysdep.c (sys_subshell): + Exit with EXIT_CANCELED etc., not 1, when exec setup fails. + (shut_down_emacs): Use emacs_write, not write. + * emacs.c, sysdep.c: Don't include . + * fileio.c (Fcopy_file, e_write): + * nsterm.m (ns_select): + * process.c (send_process): + * sound.c (vox_write): + Use emacs_write_sig, not emacs_write. + * lisp.h (emacs_write_sig, emacs_perror): New decls. + * process.h (EXIT_CANCELED), EXIT_CANNOT_INVOKE, EXIT_ENOENT): + New constants. + * sysdep.c (emacs_backtrace): Use emacs_write, not ignore_value + of write. + (emacs_full_write): New function. + (emacs_write): Rewrite to use it. + (emacswrite_sig, emacs_perror): New functions. + * xrdb.c (fatal): Don't invoke perror, since errno might be garbage. + 2013-07-08 Magnus Henoch (tiny change). * image.c (imagemagick_load_image): Do not use MagickExportImagePixels diff --git a/src/callproc.c b/src/callproc.c index 369d6eda909..fc274f3d9c0 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -1221,7 +1221,7 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, are changed between the check and this chdir, but we should at least check. */ if (chdir (temp) < 0) - _exit (errno); + _exit (EXIT_CANCELED); #else /* DOS_NT */ /* Get past the drive letter, so that d:/ is left alone. */ if (i > 2 && IS_DEVICE_SEP (temp[1]) && IS_DIRECTORY_SEP (temp[2])) @@ -1366,10 +1366,12 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, execve (new_argv[0], new_argv, env); - emacs_write (1, "Can't exec program: ", 20); - emacs_write (1, new_argv[0], strlen (new_argv[0])); - emacs_write (1, "\n", 1); - _exit (1); + /* Don't output the program name here, as it can be arbitrarily long, + and a long write from a vforked child to its parent can cause a + deadlock. */ + emacs_perror ("child process"); + + _exit (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); #else /* MSDOS */ pid = run_msdos_command (new_argv, pwd_var + 4, in, out, err, env); @@ -1395,13 +1397,8 @@ relocate_fd (int fd, int minfd) int new = fcntl (fd, F_DUPFD_CLOEXEC, minfd); if (new == -1) { - const char *message_1 = "Error while setting up child: "; - const char *errmessage = strerror (errno); - const char *message_2 = "\n"; - emacs_write (2, message_1, strlen (message_1)); - emacs_write (2, errmessage, strlen (errmessage)); - emacs_write (2, message_2, strlen (message_2)); - _exit (1); + emacs_perror ("while setting up child"); + _exit (EXIT_CANCELED); } emacs_close (fd); return new; diff --git a/src/emacs.c b/src/emacs.c index edf98d8cdb5..ee72095f705 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -28,7 +28,6 @@ along with GNU Emacs. If not, see . */ #include #include -#include #include "lisp.h" @@ -646,9 +645,7 @@ close_output_streams (void) { if (close_stream (stdout) != 0) { - fprintf (stderr, "Write error to standard output: %s\n", - strerror (errno)); - fflush (stderr); + emacs_perror ("Write error to standard output"); _exit (EXIT_FAILURE); } @@ -789,7 +786,7 @@ main (int argc, char **argv) execvp (argv[0], argv); /* If the exec fails, try to dump anyway. */ - perror ("execvp"); + emacs_perror (argv[0]); } #endif /* HAVE_PERSONALITY_LINUX32 */ @@ -1020,8 +1017,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem } if (f < 0) { - fprintf (stderr, "Cannot fork!\n"); - exit (1); + emacs_perror ("fork"); + exit (EXIT_CANCELED); } #ifdef DAEMON_MUST_EXEC @@ -1038,14 +1035,14 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem if (! (0 <= fdStrlen && fdStrlen < sizeof fdStr)) { fprintf (stderr, "daemon: child name too long\n"); - exit (1); + exit (EXIT_CANNOT_INVOKE); } argv[skip_args] = fdStr; execvp (argv[0], argv); - fprintf (stderr, "emacs daemon: exec failed: %d\n", errno); - exit (1); + emacs_perror (argv[0]); + exit (errno == ENOENT : EXIT_ENOENT : EXIT_CANNOT_INVOKE); } /* In exec'd: parse special dname into pipe and name info. */ @@ -1053,7 +1050,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem || strlen (dname_arg) < 1 || strlen (dname_arg) > 70) { fprintf (stderr, "emacs daemon: daemon name absent or too long\n"); - exit (1); + exit (EXIT_CANNOT_INVOKE); } dname_arg2[0] = '\0'; sscanf (dname_arg, "\n%d,%d\n%s", &(daemon_pipe[0]), &(daemon_pipe[1]), @@ -1916,8 +1913,8 @@ shut_down_emacs (int sig, Lisp_Object stuff) char buf[sizeof format - 2 + INT_STRLEN_BOUND (int)]; int buflen = sprintf (buf, format, sig); char const *sig_desc = safe_strsignal (sig); - ignore_value (write (STDERR_FILENO, buf, buflen)); - ignore_value (write (STDERR_FILENO, sig_desc, strlen (sig_desc))); + emacs_write (STDERR_FILENO, buf, buflen); + emacs_write (STDERR_FILENO, sig_desc, strlen (sig_desc)); } } } diff --git a/src/fileio.c b/src/fileio.c index 89ae89e1613..d030c78c422 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2122,7 +2122,7 @@ entries (depending on how Emacs was built). */) immediate_quit = 1; QUIT; while ((n = emacs_read (ifd, buf, sizeof buf)) > 0) - if (emacs_write (ofd, buf, n) != n) + if (emacs_write_sig (ofd, buf, n) != n) report_file_error ("I/O error", Fcons (newname, Qnil)); immediate_quit = 0; @@ -5317,12 +5317,10 @@ e_write (int desc, Lisp_Object string, ptrdiff_t start, ptrdiff_t end, if (coding->produced > 0) { - coding->produced - -= emacs_write (desc, - STRINGP (coding->dst_object) - ? SSDATA (coding->dst_object) - : (char *) BYTE_POS_ADDR (coding->dst_pos_byte), - coding->produced); + char *buf = (STRINGP (coding->dst_object) + ? SSDATA (coding->dst_object) + : (char *) BYTE_POS_ADDR (coding->dst_pos_byte)); + coding->produced -= emacs_write_sig (desc, buf, coding->produced); if (coding->produced) return 0; diff --git a/src/lisp.h b/src/lisp.h index c7e36fbf9de..33e9309de34 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3998,6 +3998,7 @@ extern void init_process_emacs (void); extern void syms_of_process (void); extern void setup_process_coding_systems (Lisp_Object); +/* Defined in callproc.c. */ #ifndef DOS_NT _Noreturn #endif @@ -4090,6 +4091,8 @@ extern int emacs_open (const char *, int, int); extern int emacs_close (int); extern ptrdiff_t emacs_read (int, char *, ptrdiff_t); extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t); +extern ptrdiff_t emacs_write_sig (int, char const *, ptrdiff_t); +extern void emacs_perror (char const *); extern void unlock_all_files (void); extern void lock_file (Lisp_Object); diff --git a/src/nsterm.m b/src/nsterm.m index 074a5adf78c..d7cea5c189a 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -3603,7 +3603,7 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds, /* Inform fd_handler that select should be called */ c = 'g'; - emacs_write (selfds[1], &c, 1); + emacs_write_sig (selfds[1], &c, 1); } else if (nr == 0 && timeout) { @@ -3636,7 +3636,7 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds, if (nr > 0 && readfds) { c = 's'; - emacs_write (selfds[1], &c, 1); + emacs_write_sig (selfds[1], &c, 1); } unblock_input (); diff --git a/src/process.c b/src/process.c index 1c210649024..36ca1cf6784 100644 --- a/src/process.c +++ b/src/process.c @@ -1752,7 +1752,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) tcgetattr (xforkin, &t); t.c_lflag = LDISC1; if (tcsetattr (xforkin, TCSANOW, &t) < 0) - emacs_write (1, "create_process/tcsetattr LDISC1 failed\n", 39); + emacs_perror ("create_process/tcsetattr LDISC1"); } #else #if defined (NTTYDISC) && defined (TIOCSETD) @@ -1799,10 +1799,8 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) if (xforkin < 0) { - emacs_write (1, "Couldn't open the pty terminal ", 31); - emacs_write (1, pty_name, strlen (pty_name)); - emacs_write (1, "\n", 1); - _exit (1); + emacs_perror (pty_name); + _exit (EXIT_CANCELED); } } @@ -5503,7 +5501,7 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len, written = emacs_gnutls_write (p, cur_buf, cur_len); else #endif - written = emacs_write (outfd, cur_buf, cur_len); + written = emacs_write_sig (outfd, cur_buf, cur_len); rv = (written ? 0 : -1); #ifdef ADAPTIVE_READ_BUFFERING if (p->read_output_delay > 0 diff --git a/src/process.h b/src/process.h index e7ee5f9adde..8ae33aebf39 100644 --- a/src/process.h +++ b/src/process.h @@ -198,6 +198,14 @@ extern Lisp_Object QCspeed; extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven; extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary; +/* Exit statuses for GNU programs that exec other programs. */ +enum +{ + EXIT_CANCELED = 125, /* Internal error prior to exec attempt. */ + EXIT_CANNOT_INVOKE = 126, /* Program located, but not usable. */ + EXIT_ENOENT = 127 /* Could not find program to exec. */ +}; + /* Defined in callproc.c. */ extern void block_child_signal (void); diff --git a/src/sound.c b/src/sound.c index 7f0ede5b398..5ce185ea60e 100644 --- a/src/sound.c +++ b/src/sound.c @@ -879,7 +879,7 @@ vox_init (struct sound_device *sd) static void vox_write (struct sound_device *sd, const char *buffer, ptrdiff_t nbytes) { - if (emacs_write (sd->fd, buffer, nbytes) != nbytes) + if (emacs_write_sig (sd->fd, buffer, nbytes) != nbytes) sound_perror ("Error writing to sound device"); } diff --git a/src/sysdep.c b/src/sysdep.c index faca7fae461..6346b0bbfcd 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -31,7 +31,6 @@ along with GNU Emacs. If not, see . */ #include #include -#include #include #include "lisp.h" @@ -538,8 +537,8 @@ sys_subshell (void) if (str && chdir ((char *) str) != 0) { #ifndef DOS_NT - ignore_value (write (1, "Can't chdir\n", 12)); - _exit (1); + emacs_perror ((char *) str); + _exit (EXIT_CANCELED); #endif } @@ -570,8 +569,8 @@ sys_subshell (void) write (1, "Can't execute subshell", 22); #else /* not WINDOWSNT */ execlp (sh, sh, (char *) 0); - ignore_value (write (1, "Can't execute subshell", 22)); - _exit (1); + emacs_perror (sh); + _exit (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); #endif /* not WINDOWSNT */ #endif /* not MSDOS */ } @@ -2134,10 +2133,10 @@ emacs_backtrace (int backtrace_limit) if (npointers) { - ignore_value (write (STDERR_FILENO, "\nBacktrace:\n", 12)); + emacs_write (STDERR_FILENO, "\nBacktrace:\n", 12); backtrace_symbols_fd (buffer, npointers, STDERR_FILENO); if (bounded_limit < npointers) - ignore_value (write (STDERR_FILENO, "...\n", 4)); + emacs_write (STDERR_FILENO, "...\n", 4); } } @@ -2246,27 +2245,26 @@ emacs_read (int fildes, char *buf, ptrdiff_t nbyte) } /* Write to FILEDES from a buffer BUF with size NBYTE, retrying if interrupted - or if a partial write occurs. Return the number of bytes written, setting + or if a partial write occurs. If interrupted, process pending + signals if PROCESS SIGNALS. Return the number of bytes written, setting errno if this is less than NBYTE. */ -ptrdiff_t -emacs_write (int fildes, const char *buf, ptrdiff_t nbyte) +static ptrdiff_t +emacs_full_write (int fildes, char const *buf, ptrdiff_t nbyte, + bool process_signals) { - ssize_t rtnval; - ptrdiff_t bytes_written; - - bytes_written = 0; + ptrdiff_t bytes_written = 0; while (nbyte > 0) { - rtnval = write (fildes, buf, min (nbyte, MAX_RW_COUNT)); + ssize_t n = write (fildes, buf, min (nbyte, MAX_RW_COUNT)); - if (rtnval < 0) + if (n < 0) { if (errno == EINTR) { /* I originally used `QUIT' but that might causes files to be truncated if you hit C-g in the middle of it. --Stef */ - if (pending_signals) + if (process_signals && pending_signals) process_pending_signals (); continue; } @@ -2274,12 +2272,57 @@ emacs_write (int fildes, const char *buf, ptrdiff_t nbyte) break; } - buf += rtnval; - nbyte -= rtnval; - bytes_written += rtnval; + buf += n; + nbyte -= n; + bytes_written += n; } - return (bytes_written); + return bytes_written; +} + +/* Write to FILEDES from a buffer BUF with size NBYTE, retrying if + interrupted or if a partial write occurs. Return the number of + bytes written, setting errno if this is less than NBYTE. */ +ptrdiff_t +emacs_write (int fildes, char const *buf, ptrdiff_t nbyte) +{ + return emacs_full_write (fildes, buf, nbyte, 0); +} + +/* Like emacs_write, but also process pending signals if interrupted. */ +ptrdiff_t +emacs_write_sig (int fildes, char const *buf, ptrdiff_t nbyte) +{ + return emacs_full_write (fildes, buf, nbyte, 1); +} + +/* Write a diagnostic to standard error that contains MESSAGE and a + string derived from errno. Preserve errno. Do not buffer stderr. + Do not process pending signals if interrupted. */ +void +emacs_perror (char const *message) +{ + int err = errno; + char const *error_string = strerror (err); + char const *command = (initial_argv && initial_argv[0] + ? initial_argv[0] : "emacs"); + /* Write it out all at once, if it's short; this is less likely to + be interleaved with other output. */ + char buf[BUFSIZ]; + int nbytes = snprintf (buf, sizeof buf, "%s: %s: %s\n", + command, message, error_string); + if (0 <= nbytes && nbytes < BUFSIZ) + emacs_write (STDERR_FILENO, buf, nbytes); + else + { + emacs_write (STDERR_FILENO, command, strlen (command)); + emacs_write (STDERR_FILENO, ": ", 2); + emacs_write (STDERR_FILENO, message, strlen (message)); + emacs_write (STDERR_FILENO, ": ", 2); + emacs_write (STDERR_FILENO, error_string, strlen (error_string)); + emacs_write (STDERR_FILENO, "\n", 1); + } + errno = err; } /* Return a struct timeval that is roughly equivalent to T. diff --git a/src/xrdb.c b/src/xrdb.c index c25c25d6f33..7c9cd53fa8c 100644 --- a/src/xrdb.c +++ b/src/xrdb.c @@ -634,10 +634,7 @@ member (char *elt, List list) static void fatal (char *msg, char *prog) { - if (errno) - perror (prog); - - (void) fprintf (stderr, msg, prog); + fprintf (stderr, msg, prog); exit (1); } -- cgit v1.2.1