* bug#15035: Fix some fd issues when running subprocesses
@ 2013-08-06 17:15 Paul Eggert
2013-08-12 7:14 ` Paul Eggert
0 siblings, 1 reply; 2+ messages in thread
From: Paul Eggert @ 2013-08-06 17:15 UTC (permalink / raw)
To: 15035
Tags: patch
Here's a proposed patch to fix some issues in call-process etc.
that have been discussed on emacs-devel recently.
This is a first cut; I plan to test it further before installing.
I'll publishing it now since it involves changes to the Microsoft
ports, and I'll CC: this to Eli as a heads-up.
This is relative to trunk bzr 113721.
=== modified file 'src/ChangeLog'
--- src/ChangeLog 2013-08-06 16:51:41 +0000
+++ src/ChangeLog 2013-08-06 17:11:59 +0000
@@ -1,3 +1,81 @@
+2013-08-06 Paul Eggert <eggert@cs.ucla.edu>
+
+ Fix some fd issues when running subprocesses.
+ Fix bugs that can leak files or file descriptors on errors.
+ Don't unlink open temp files, as that's hard for users to diagnose
+ when things go awry (e.g., temp disk exhausted).
+ Don't bother to lock temp files. Check for invalid recursion.
+ * callproc.c (synch_process_fd): Remove. All uses removed.
+ (synch_process_tempfile): New var or macro.
+ (CALLPROC_STDOUT, CALLPROC_STDERR, CALLPROC_PIPEREAD, CALLPROC_FDS):
+ New constants.
+ (record_kill_process): New arg, the tempfile name. All callers changed.
+ (delete_temp_file): Now just a simple wrapper around unlink.
+ (call_process_kill): New arg, the call_process_fd array.
+ Close them all. Clear synch_process_pid. Remove the temp file,
+ or arrange for it to be removed.
+ (call_process_cleanup) [MSDOS]: Arg no longer contains file name;
+ that's been moved to synch_process_tempfile. Caller changed.
+ Do not remove the tempfile; that's now call_process_kill's
+ responsibility.
+ (call_process_cleanup) [!MSDOS]: Do not record unwind-protect for
+ call_process_kill; the caller now does that.
+ (call_process_cleanup): Do not close the process fd; that's now
+ call_process_kill's responsibility.
+ (Fcall_process): Implement via new function call_process, which
+ contains most of the old body of Fcall_process, but has a different API.
+ (call_process): New function that does not open or close filefd if
+ it is nonnegative. Record which fds need to be closed, and let
+ call_process_kill close (and remove the tempfile, on MSDOS) on error.
+ Signal an error if invoked recursively (could be done via a hook).
+ Simplify creation of the tempfile in the MSDOS case.
+ Don't create the output file until after checking for the executable.
+ Report any failure to open /dev/null.
+ Don't open /dev/null for writing twice; once is enough.
+ Don't create pipe if all output is being discarded or sent to file.
+ Don't worry about setting up the coding system or reading from the
+ pipe if all output is being discarded.
+ Hoist fd_error local into top level, to lessen block nesting.
+ Don't record deleted pid here; now done by Fcall_process_region.
+ (Fcall_process) [MSDOS]: Report mktemp failure immediately,
+ and note its success in synch_process_tempfile.
+ Do not leak resources when child_setup fails.
+ (Fcall_process) [!MSDOS && !WINDOWSNT]: Remove duplicate assignment
+ to child_errno. Remove unnecessary close of fd0; it's close-on-exec.
+ (create_temp_file): Now returns open fd, with an additional
+ Lisp_Object * argument to return the name. All callers changed.
+ Do not close the file; rewind it instead, and leave it open for
+ the caller. Do not lock the temp file. Unwind-protect the file
+ and the file-descriptor.
+ (Fcall_process_region): If the input is /dev/null, unwind-protect it.
+ If an asynchrounous process, record it here, not in call_process.
+ (syms_of_callproc) [MSDOS]: Initialize synch_process_tempfile.
+ * eval.c (set_unwind_protect): New function.
+ * fileio.c (write_region): New function, generalized from the
+ old Fwrite_region. Do not lock temp files.
+ (Fwrite_region): Use it.
+ * lisp.h (set_unwind_protect, write_region): New decls.
+ * process.c: Include <verify.h>.
+ (make_process): Mark fds as initially closed.
+ (deleted_pid_list): Now a list of pid-filename pairs. All uses changed.
+ (close_process_fd): New function.
+ (SUBPROCESS_STDIN, WRITE_TO_SUBPROCESS, READ_FROM_SUBPROCESS)
+ (SUBPROCESS_STDOUT, READ_FROM_EXEC_MONITOR, EXEC_MONITOR_OUTPUT):
+ New constants. Verify that their number matches PROCESS_OPEN_FDS.
+ (create_process, create_pty, Fmake_serial_process)
+ (server_accept_connection): Record which fds need to be closed,
+ and let deactivate_process close them.
+ (Fmake_network_process): Do not discard the unwind-protect
+ until it's safe to do so.
+ (deactivate_process): Close the fds opened by create_process etc.
+ (Fprocess_send_eof): Adjust to new way of recording open fds.
+ Report an error if /dev/null can't be opened, instead of aborting.
+ * process.h (PROCESS_OPEN_FDS): New constant.
+ (struct Lisp_Process): New member open_fds.
+ (record_kill_process, record_deleted_pid): Adjust signatures.
+ (record_deleted_pid): Move decl here ...
+ * syswait.h (record_deleted_pid): ... from here.
+
2013-08-06 Dmitry Antipov <dmantipov@yandex.ru>
* window.c (window_scroll, window_scroll_pixel_based)
=== modified file 'src/callproc.c'
--- src/callproc.c 2013-08-04 16:56:56 +0000
+++ src/callproc.c 2013-08-06 16:35:16 +0000
@@ -68,9 +68,10 @@
/* Pattern used by call-process-region to make temp files. */
static Lisp_Object Vtemp_file_name_pattern;
-/* The next two variables are valid only while record-unwind-protect
- is in place during call-process for a synchronous subprocess. At
- other times, their contents are irrelevant. Doing this via static
+/* The next two variables are used while record-unwind-protect is in place
+ during call-process for a subprocess for which record_deleted_pid has
+ not yet been called. At other times, synch_process_pid is zero and
+ synch_process_tempfile's contents are irrelevant. Doing this via static
C variables is more convenient than putting them into the arguments
of record-unwind-protect, as they need to be updated at randomish
times in the code, and Lisp cannot always store these values as
@@ -80,8 +81,28 @@
/* If nonzero, a process-ID that has not been reaped. */
static pid_t synch_process_pid;
-/* If nonnegative, a file descriptor that has not been closed. */
-static int synch_process_fd;
+/* If a string, the name of a temp file that has not been removed. */
+#ifdef MSDOS
+static Lisp_Object synch_process_tempfile;
+#else
+# define synch_process_tempfile make_number (0)
+#endif
+
+/* Indexes of file descriptors that need closing on call_process_kill. */
+enum
+ {
+ /* The subsidiary process's stdout and stderr. stdin is handled
+ separately, in either Fcall_process_region or create_temp_file. */
+ CALLPROC_STDOUT, CALLPROC_STDERR,
+
+ /* How to read from a pipe (or substitute) from the subsidiary process. */
+ CALLPROC_PIPEREAD,
+
+ /* A bound on the number of file descriptors. */
+ CALLPROC_FDS
+ };
+
+static Lisp_Object call_process (ptrdiff_t, Lisp_Object *, int);
\f
/* Block SIGCHLD. */
@@ -107,80 +128,68 @@
reaped on receipt of the first SIGCHLD after the critical section. */
void
-record_kill_process (struct Lisp_Process *p)
+record_kill_process (struct Lisp_Process *p, Lisp_Object tempfile)
{
block_child_signal ();
if (p->alive)
{
+ record_deleted_pid (p->pid, tempfile);
p->alive = 0;
- record_deleted_pid (p->pid);
kill (- p->pid, SIGKILL);
}
unblock_child_signal ();
}
-/* Clean up when exiting call_process_cleanup. */
-
-static void
-call_process_kill (void)
-{
- if (synch_process_fd >= 0)
- emacs_close (synch_process_fd);
+/* Clean up files, file descriptors and processes created by Fcall_process. */
+
+static void
+delete_temp_file (Lisp_Object name)
+{
+ unlink (SSDATA (name));
+}
+
+static void
+call_process_kill (void *ptr)
+{
+ int *callproc_fd = ptr;
+ int i;
+ for (i = 0; i < CALLPROC_FDS; i++)
+ if (0 <= callproc_fd[i])
+ emacs_close (callproc_fd[i]);
if (synch_process_pid)
{
struct Lisp_Process proc;
proc.alive = 1;
proc.pid = synch_process_pid;
- record_kill_process (&proc);
+ record_kill_process (&proc, synch_process_tempfile);
+ synch_process_pid = 0;
}
+ else if (STRINGP (synch_process_tempfile))
+ delete_temp_file (synch_process_tempfile);
}
-/* Clean up when exiting Fcall_process.
- On MSDOS, delete the temporary file on any kind of termination.
- On Unix, kill the process and any children on termination by signal. */
+/* Clean up when exiting Fcall_process: restore the buffer, and
+ kill the subsidiary process group if the process still exists. */
static void
-call_process_cleanup (Lisp_Object arg)
+call_process_cleanup (Lisp_Object buffer)
{
-#ifdef MSDOS
- Lisp_Object buffer = Fcar (arg);
- Lisp_Object file = Fcdr (arg);
-#else
- Lisp_Object buffer = arg;
-#endif
-
Fset_buffer (buffer);
-#ifndef MSDOS
- /* If the process still exists, kill its process group. */
if (synch_process_pid)
{
- ptrdiff_t count = SPECPDL_INDEX ();
kill (-synch_process_pid, SIGINT);
- record_unwind_protect_void (call_process_kill);
message1 ("Waiting for process to die...(type C-g again to kill it instantly)");
immediate_quit = 1;
QUIT;
wait_for_termination (synch_process_pid, 0, 1);
synch_process_pid = 0;
immediate_quit = 0;
- specpdl_ptr = specpdl + count; /* Discard the unwind protect. */
message1 ("Waiting for process to die...done");
}
-#endif
-
- if (synch_process_fd >= 0)
- emacs_close (synch_process_fd);
-
-#ifdef MSDOS
- /* FILE is "" when we didn't actually create a temporary file in
- call-process. */
- if (!(strcmp (SDATA (file), NULL_DEVICE) == 0 || SREF (file, 0) == '\0'))
- unlink (SDATA (file));
-#endif
}
#ifdef DOS_NT
@@ -218,10 +227,42 @@
usage: (call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS) */)
(ptrdiff_t nargs, Lisp_Object *args)
{
- Lisp_Object infile, buffer, current_dir, path;
+ Lisp_Object infile, encoded_infile;
+ int filefd;
+ struct gcpro gcpro1;
+ ptrdiff_t count = SPECPDL_INDEX ();
+
+ if (nargs >= 2 && ! NILP (args[1]))
+ {
+ infile = Fexpand_file_name (args[1], BVAR (current_buffer, directory));
+ CHECK_STRING (infile);
+ }
+ else
+ infile = build_string (NULL_DEVICE);
+
+ GCPRO1 (infile);
+ encoded_infile = STRING_MULTIBYTE (infile) ? ENCODE_FILE (infile) : infile;
+
+ filefd = emacs_open (SSDATA (encoded_infile), O_RDONLY, 0);
+ if (filefd < 0)
+ report_file_error ("Opening process input file", infile);
+ record_unwind_protect_int (close_file_unwind, filefd);
+ UNGCPRO;
+ return unbind_to (count, call_process (nargs, args, filefd));
+}
+
+/* Like Fcall_process (NARGS, ARGS), except use FILEFD as the input file.
+ At entry, the specpdl stack top entry must be close_file_unwind (FILEFD). */
+
+static Lisp_Object
+call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd)
+{
+ Lisp_Object buffer, current_dir, path;
bool display_p;
- int fd0, fd1, filefd;
+ int fd0;
+ int callproc_fd[CALLPROC_FDS];
int status;
+ ptrdiff_t i;
ptrdiff_t count = SPECPDL_INDEX ();
USE_SAFE_ALLOCA;
@@ -231,19 +272,21 @@
Lisp_Object error_file;
Lisp_Object output_file = Qnil;
#ifdef MSDOS /* Demacs 1.1.1 91/10/16 HIRANO Satoshi */
- char *outf, *tempfile = NULL;
- int outfilefd;
+ char *tempfile = NULL;
int pid;
#else
pid_t pid;
#endif
int child_errno;
- int fd_output = -1;
+ int fd_output, fd_error;
struct coding_system process_coding; /* coding-system of process output */
struct coding_system argument_coding; /* coding-system of arguments */
/* Set to the return value of Ffind_operation_coding_system. */
Lisp_Object coding_systems;
- bool output_to_buffer = 1;
+ bool discard_output;
+
+ if (synch_process_pid)
+ error ("call-process invoked recursively");
/* Qt denotes that Ffind_operation_coding_system is not yet called. */
coding_systems = Qt;
@@ -262,7 +305,6 @@
/* Decide the coding-system for giving arguments. */
{
Lisp_Object val, *args2;
- ptrdiff_t i;
/* If arguments are supplied, we may have to encode them. */
if (nargs >= 5)
@@ -301,24 +343,16 @@
}
}
- if (nargs >= 2 && ! NILP (args[1]))
- {
- infile = Fexpand_file_name (args[1], BVAR (current_buffer, directory));
- CHECK_STRING (infile);
- }
+ if (nargs < 3)
+ buffer = Qnil;
else
- infile = build_string (NULL_DEVICE);
-
- if (nargs >= 3)
{
buffer = args[2];
/* If BUFFER is a list, its meaning is (BUFFER-FOR-STDOUT
FILE-FOR-STDERR), unless the first element is :file, in which case see
the next paragraph. */
- if (CONSP (buffer)
- && (! SYMBOLP (XCAR (buffer))
- || strcmp (SSDATA (SYMBOL_NAME (XCAR (buffer))), ":file")))
+ if (CONSP (buffer) && !EQ (XCAR (buffer), QCfile))
{
if (CONSP (XCDR (buffer)))
{
@@ -335,9 +369,7 @@
}
/* If the buffer is (still) a list, it might be a (:file "file") spec. */
- if (CONSP (buffer)
- && SYMBOLP (XCAR (buffer))
- && ! strcmp (SSDATA (SYMBOL_NAME (XCAR (buffer))), ":file"))
+ if (CONSP (buffer) && EQ (XCAR (buffer), QCfile))
{
output_file = Fexpand_file_name (XCAR (XCDR (buffer)),
BVAR (current_buffer, directory));
@@ -345,9 +377,7 @@
buffer = Qnil;
}
- if (!(EQ (buffer, Qnil)
- || EQ (buffer, Qt)
- || INTEGERP (buffer)))
+ if (! (NILP (buffer) || EQ (buffer, Qt) || INTEGERP (buffer)))
{
Lisp_Object spec_buffer;
spec_buffer = buffer;
@@ -358,8 +388,6 @@
CHECK_BUFFER (buffer);
}
}
- else
- buffer = Qnil;
/* Make sure that the child will be able to chdir to the current
buffer's current directory, or its unhandled equivalent. We
@@ -372,11 +400,11 @@
protected by the caller, so all we really have to worry about is
buffer. */
{
- struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
+ struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
current_dir = BVAR (current_buffer, directory);
- GCPRO5 (infile, buffer, current_dir, error_file, output_file);
+ GCPRO4 (buffer, current_dir, error_file, output_file);
current_dir = Funhandled_file_name_directory (current_dir);
if (NILP (current_dir))
@@ -390,8 +418,6 @@
report_file_error ("Setting current directory",
BVAR (current_buffer, directory));
- if (STRING_MULTIBYTE (infile))
- infile = ENCODE_FILE (infile);
if (STRING_MULTIBYTE (current_dir))
current_dir = ENCODE_FILE (current_dir);
if (STRINGP (error_file) && STRING_MULTIBYTE (error_file))
@@ -403,44 +429,23 @@
display_p = INTERACTIVE && nargs >= 4 && !NILP (args[3]);
- filefd = emacs_open (SSDATA (infile), O_RDONLY, 0);
- if (filefd < 0)
- {
- int open_errno = errno;
- report_file_errno ("Opening process input file", DECODE_FILE (infile),
- open_errno);
- }
-
- if (STRINGP (output_file))
- {
- fd_output = emacs_open (SSDATA (output_file),
- O_WRONLY | O_CREAT | O_TRUNC | O_TEXT,
- default_output_mode);
- if (fd_output < 0)
- {
- int open_errno = errno;
- output_file = DECODE_FILE (output_file);
- report_file_errno ("Opening process output file",
- output_file, open_errno);
- }
- if (STRINGP (error_file) || NILP (error_file))
- output_to_buffer = 0;
- }
+ for (i = 0; i < CALLPROC_FDS; i++)
+ callproc_fd[i] = -1;
+#ifdef MSDOS
+ synch_process_tempfile = make_number (0);
+#endif
+ record_unwind_protect_ptr (call_process_kill, callproc_fd);
/* Search for program; barf if not found. */
{
- struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+ struct gcpro gcpro1, gcpro2, gcpro3;
int ok;
- GCPRO4 (infile, buffer, current_dir, error_file);
+ GCPRO3 (buffer, current_dir, error_file);
ok = openp (Vexec_path, args[0], Vexec_suffixes, &path, make_number (X_OK));
UNGCPRO;
if (ok < 0)
- {
- int openp_errno = errno;
- emacs_close (filefd);
- report_file_errno ("Searching for program", args[0], openp_errno);
- }
+ report_file_error ("Searching for program", args[0]);
}
/* If program file name starts with /: for quoting a magic name,
@@ -452,9 +457,9 @@
new_argv = SAFE_ALLOCA ((nargs > 4 ? nargs - 2 : 2) * sizeof *new_argv);
{
- struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
+ struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
- GCPRO5 (infile, buffer, current_dir, path, error_file);
+ GCPRO4 (buffer, current_dir, path, error_file);
if (nargs > 4)
{
ptrdiff_t i;
@@ -479,254 +484,213 @@
UNGCPRO;
}
-#ifdef MSDOS /* MW, July 1993 */
+ discard_output = INTEGERP (buffer) || (NILP (buffer) && NILP (output_file));
- /* If we're redirecting STDOUT to a file, that file is already open
- on fd_output. */
- if (fd_output < 0)
+#ifdef MSDOS
+ if (! discard_output && ! STRINGP (output_file))
{
- if ((outf = egetenv ("TMPDIR")))
- strcpy (tempfile = alloca (strlen (outf) + 20), outf);
- else
- {
- tempfile = alloca (20);
- *tempfile = '\0';
- }
+ char const *tmpdir = egetenv ("TMPDIR");
+ char const *outf = tmpdir ? tmpdir : "";
+ tempfile = alloca (strlen (outf) + 20);
+ strcpy (tempfile, outf);
dostounix_filename (tempfile, 0);
if (*tempfile == '\0' || tempfile[strlen (tempfile) - 1] != '/')
strcat (tempfile, "/");
strcat (tempfile, "detmp.XXX");
mktemp (tempfile);
- outfilefd = emacs_open (tempfile, O_WRONLY | O_CREAT | O_TRUNC,
- S_IREAD | S_IWRITE);
- if (outfilefd < 0)
+ if (!*tempfile)
+ report_file_error ("Opening process output file", Qnil);
+ output_file = build_string (tempfile);
+ synch_process_tempfile = output_file;
+ }
+#endif
+
+ if (discard_output)
+ {
+ fd_output = emacs_open (NULL_DEVICE, O_WRONLY, 0);
+ if (fd_output < 0)
+ report_file_error ("Opening null device", Qnil);
+ }
+ else if (STRINGP (output_file))
+ {
+ fd_output = emacs_open (SSDATA (output_file),
+ O_WRONLY | O_CREAT | O_TRUNC | O_TEXT,
+ default_output_mode);
+ if (fd_output < 0)
{
int open_errno = errno;
- emacs_close (filefd);
+ output_file = DECODE_FILE (output_file);
report_file_errno ("Opening process output file",
- build_string (tempfile), open_errno);
+ output_file, open_errno);
}
}
else
- outfilefd = fd_output;
- fd0 = filefd;
- fd1 = outfilefd;
-#endif /* MSDOS */
-
- if (INTEGERP (buffer))
- {
- fd0 = -1;
- fd1 = emacs_open (NULL_DEVICE, O_WRONLY, 0);
- }
- else
- {
-#ifndef MSDOS
+ {
int fd[2];
if (emacs_pipe (fd) != 0)
- {
- int pipe_errno = errno;
- emacs_close (filefd);
- report_file_errno ("Creating process pipe", Qnil, pipe_errno);
- }
- fd0 = fd[0];
- fd1 = fd[1];
-#endif
+ report_file_error ("Creating process pipe", Qnil);
+ callproc_fd[CALLPROC_PIPEREAD] = fd[0];
+ fd_output = fd[1];
}
-
- {
- int fd_error = fd1;
-
- if (fd_output >= 0)
- fd1 = fd_output;
-
- if (NILP (error_file))
- fd_error = emacs_open (NULL_DEVICE, O_WRONLY, 0);
- else if (STRINGP (error_file))
- fd_error = emacs_open (SSDATA (error_file),
+ callproc_fd[CALLPROC_STDOUT] = fd_output;
+
+ fd_error = fd_output;
+
+ if (STRINGP (error_file) || (NILP (error_file) && !discard_output))
+ {
+ fd_error = emacs_open ((STRINGP (error_file)
+ ? SSDATA (error_file)
+ : NULL_DEVICE),
O_WRONLY | O_CREAT | O_TRUNC | O_TEXT,
default_output_mode);
-
- if (fd_error < 0)
- {
- int open_errno = errno;
- emacs_close (filefd);
- if (fd0 != filefd)
- emacs_close (fd0);
- if (fd1 >= 0)
- emacs_close (fd1);
-#ifdef MSDOS
- unlink (tempfile);
-#endif
- if (NILP (error_file))
- error_file = build_string (NULL_DEVICE);
- else if (STRINGP (error_file))
- error_file = DECODE_FILE (error_file);
- report_file_errno ("Cannot redirect stderr", error_file, open_errno);
- }
+ if (fd_error < 0)
+ {
+ int open_errno = errno;
+ report_file_errno ("Cannot redirect stderr",
+ (STRINGP (error_file)
+ ? DECODE_FILE (error_file)
+ : build_string (NULL_DEVICE)),
+ open_errno);
+ }
+ callproc_fd[CALLPROC_STDERR] = fd_error;
+ }
#ifdef MSDOS /* MW, July 1993 */
- /* Note that on MSDOS `child_setup' actually returns the child process
- exit status, not its PID, so assign it to status below. */
- pid = child_setup (filefd, outfilefd, fd_error, new_argv, 0, current_dir);
- child_errno = errno;
-
- emacs_close (outfilefd);
- if (fd_error != outfilefd)
- emacs_close (fd_error);
- if (pid < 0)
- {
- synchronize_system_messages_locale ();
- return
- code_convert_string_norecord (build_string (strerror (child_errno)),
- Vlocale_coding_system, 0);
- }
- status = pid;
- fd1 = -1; /* No harm in closing that one! */
- if (tempfile)
- {
- /* Since CRLF is converted to LF within `decode_coding', we
- can always open a file with binary mode. */
- fd0 = emacs_open (tempfile, O_RDONLY | O_BINARY, 0);
- if (fd0 < 0)
- {
- int open_errno = errno;
- unlink (tempfile);
- emacs_close (filefd);
- report_file_errno ("Cannot re-open temporary file",
- build_string (tempfile), open_errno);
- }
- }
- else
- fd0 = -1; /* We are not going to read from tempfile. */
-#endif /* MSDOS */
-
- /* Do the unwind-protect now, even though the pid is not known, so
- that no storage allocation is done in the critical section.
- The actual PID will be filled in during the critical section. */
- synch_process_pid = 0;
- synch_process_fd = fd0;
-
-#ifdef MSDOS
- /* MSDOS needs different cleanup information. */
- record_unwind_protect (call_process_cleanup,
- Fcons (Fcurrent_buffer (),
- build_string (tempfile ? tempfile : "")));
-#else
- record_unwind_protect (call_process_cleanup, Fcurrent_buffer ());
-
- block_input ();
- block_child_signal ();
-
-#ifdef WINDOWSNT
- pid = child_setup (filefd, fd1, fd_error, new_argv, 0, current_dir);
- /* We need to record the input file of this child, for when we are
- called from call-process-region to create an async subprocess.
- That's because call-process-region's unwind procedure will
- attempt to delete the temporary input file, which will fail
- because that file is still in use. Recording it with the child
- will allow us to delete the file when the subprocess exits.
- The second part of this is in delete_temp_file, q.v. */
- if (pid > 0 && INTEGERP (buffer) && nargs >= 2 && !NILP (args[1]))
- record_infile (pid, xstrdup (SSDATA (infile)));
-#else /* not WINDOWSNT */
-
- /* vfork, and prevent local vars from being clobbered by the vfork. */
+ /* Note that on MSDOS `child_setup' actually returns the child process
+ exit status, not its PID, so assign it to status below. */
+ pid = child_setup (filefd, fd_output, fd_error, new_argv, 0, current_dir);
+
+ if (pid < 0)
{
- Lisp_Object volatile buffer_volatile = buffer;
- Lisp_Object volatile coding_systems_volatile = coding_systems;
- Lisp_Object volatile current_dir_volatile = current_dir;
- bool volatile display_p_volatile = display_p;
- bool volatile output_to_buffer_volatile = output_to_buffer;
- bool volatile sa_must_free_volatile = sa_must_free;
- int volatile fd1_volatile = fd1;
- int volatile fd_error_volatile = fd_error;
- int volatile fd_output_volatile = fd_output;
- int volatile filefd_volatile = filefd;
- ptrdiff_t volatile count_volatile = count;
- ptrdiff_t volatile sa_count_volatile = sa_count;
- char **volatile new_argv_volatile = new_argv;
-
- pid = vfork ();
child_errno = errno;
-
- buffer = buffer_volatile;
- coding_systems = coding_systems_volatile;
- current_dir = current_dir_volatile;
- display_p = display_p_volatile;
- output_to_buffer = output_to_buffer_volatile;
- sa_must_free = sa_must_free_volatile;
- fd1 = fd1_volatile;
- fd_error = fd_error_volatile;
- fd_output = fd_output_volatile;
- filefd = filefd_volatile;
- count = count_volatile;
- sa_count = sa_count_volatile;
- new_argv = new_argv_volatile;
-
- fd0 = synch_process_fd;
+ unbind_to (count, Qnil);
+ synchronize_system_messages_locale ();
+ return
+ code_convert_string_norecord (build_string (strerror (child_errno)),
+ Vlocale_coding_system, 0);
}
+ status = pid;
- if (pid == 0)
+ for (i = 0; i < CALLPROC_FDS; i++)
+ if (0 <= callproc_fd[i])
{
- unblock_child_signal ();
-
- if (fd0 >= 0)
- emacs_close (fd0);
-
- setsid ();
-
- /* Emacs ignores SIGPIPE, but the child should not. */
- signal (SIGPIPE, SIG_DFL);
-
- child_setup (filefd, fd1, fd_error, new_argv, 0, current_dir);
+ emacs_close (callproc_fd[i]);
+ callproc_fd[i] = -1;
}
+ emacs_close (filefd);
+ clear_unwind_protect (count - 1);
+
+ if (tempfile)
+ {
+ /* Since CRLF is converted to LF within `decode_coding', we
+ can always open a file with binary mode. */
+ callproc_fd[CALLPROC_PIPEREAD] = emacs_open (tempfile,
+ O_RDONLY | O_BINARY, 0);
+ if (callproc_fd[CALLPROC_PIPEREAD] < 0)
+ {
+ int open_errno = errno;
+ report_file_errno ("Cannot re-open temporary file",
+ build_string (tempfile), open_errno);
+ }
+ }
+
+#endif /* MSDOS */
+
+ /* Do the unwind-protect now, even though the pid is not known, so
+ that no storage allocation is done in the critical section.
+ The actual PID will be filled in during the critical section. */
+ record_unwind_protect (call_process_cleanup, Fcurrent_buffer ());
+
+#ifndef MSDOS
+
+ block_input ();
+ block_child_signal ();
+
+#ifdef WINDOWSNT
+ pid = child_setup (filefd, fd_output, fd_error, new_argv, 0, current_dir);
+#else /* not WINDOWSNT */
+
+ /* vfork, and prevent local vars from being clobbered by the vfork. */
+ {
+ Lisp_Object volatile buffer_volatile = buffer;
+ Lisp_Object volatile coding_systems_volatile = coding_systems;
+ Lisp_Object volatile current_dir_volatile = current_dir;
+ bool volatile display_p_volatile = display_p;
+ bool volatile sa_must_free_volatile = sa_must_free;
+ int volatile fd_error_volatile = fd_error;
+ int volatile filefd_volatile = filefd;
+ ptrdiff_t volatile count_volatile = count;
+ ptrdiff_t volatile sa_count_volatile = sa_count;
+ char **volatile new_argv_volatile = new_argv;
+ int volatile callproc_fd_volatile[CALLPROC_FDS];
+ for (i = 0; i < CALLPROC_FDS; i++)
+ callproc_fd_volatile[i] = callproc_fd[i];
+
+ pid = vfork ();
+
+ buffer = buffer_volatile;
+ coding_systems = coding_systems_volatile;
+ current_dir = current_dir_volatile;
+ display_p = display_p_volatile;
+ sa_must_free = sa_must_free_volatile;
+ fd_error = fd_error_volatile;
+ filefd = filefd_volatile;
+ count = count_volatile;
+ sa_count = sa_count_volatile;
+ new_argv = new_argv_volatile;
+
+ for (i = 0; i < CALLPROC_FDS; i++)
+ callproc_fd[i] = callproc_fd_volatile[i];
+ fd_output = callproc_fd[CALLPROC_STDOUT];
+ }
+
+ if (pid == 0)
+ {
+ unblock_child_signal ();
+
+ setsid ();
+
+ /* Emacs ignores SIGPIPE, but the child should not. */
+ signal (SIGPIPE, SIG_DFL);
+
+ child_setup (filefd, fd_output, fd_error, new_argv, 0, current_dir);
+ }
#endif /* not WINDOWSNT */
- child_errno = errno;
-
- if (pid > 0)
- {
- if (INTEGERP (buffer))
- record_deleted_pid (pid);
- else
- synch_process_pid = pid;
- }
-
- unblock_child_signal ();
- unblock_input ();
-
- /* The MSDOS case did this already. */
- if (fd_error >= 0)
- emacs_close (fd_error);
+ child_errno = errno;
+
+ if (pid > 0)
+ synch_process_pid = pid;
+
+ unblock_child_signal ();
+ unblock_input ();
+
#endif /* not MSDOS */
- /* Close most of our file descriptors, but not fd0
- since we will use that to read input from. */
- emacs_close (filefd);
- if (fd_output >= 0)
- emacs_close (fd_output);
- if (fd1 >= 0 && fd1 != fd_error)
- emacs_close (fd1);
- }
-
if (pid < 0)
report_file_errno ("Doing vfork", Qnil, child_errno);
+ /* Close our file descriptors, except for callproc_fd[CALLPROC_PIPEREAD]
+ since we will use that to read input from. */
+ for (i = 0; i < CALLPROC_FDS; i++)
+ if (i != CALLPROC_PIPEREAD && 0 <= callproc_fd[i])
+ {
+ emacs_close (callproc_fd[i]);
+ callproc_fd[i] = -1;
+ }
+ emacs_close (filefd);
+ clear_unwind_protect (count - 1);
+
if (INTEGERP (buffer))
return unbind_to (count, Qnil);
if (BUFFERP (buffer))
Fset_buffer (buffer);
- if (NILP (buffer))
- {
- /* If BUFFER is nil, we must read process output once and then
- discard it, so setup coding system but with nil. */
- setup_coding_system (Qnil, &process_coding);
- process_coding.dst_multibyte = 0;
- }
- else
+ fd0 = callproc_fd[CALLPROC_PIPEREAD];
+
+ if (0 <= fd0)
{
Lisp_Object val, *args2;
@@ -762,13 +726,13 @@
setup_coding_system (val, &process_coding);
process_coding.dst_multibyte
= ! NILP (BVAR (current_buffer, enable_multibyte_characters));
+ process_coding.src_multibyte = 0;
}
- process_coding.src_multibyte = 0;
immediate_quit = 1;
QUIT;
- if (output_to_buffer)
+ if (0 <= fd0)
{
enum { CALLPROC_BUFFER_SIZE_MIN = 16 * 1024 };
enum { CALLPROC_BUFFER_SIZE_MAX = 4 * CALLPROC_BUFFER_SIZE_MIN };
@@ -779,9 +743,8 @@
EMACS_INT total_read = 0;
int carryover = 0;
bool display_on_the_fly = display_p;
- struct coding_system saved_coding;
+ struct coding_system saved_coding = process_coding;
- saved_coding = process_coding;
while (1)
{
/* Repeatedly read until we've filled as much as possible
@@ -812,58 +775,55 @@
/* Now NREAD is the total amount of data in the buffer. */
immediate_quit = 0;
- if (!NILP (buffer))
- {
- if (NILP (BVAR (current_buffer, enable_multibyte_characters))
- && ! CODING_MAY_REQUIRE_DECODING (&process_coding))
- insert_1_both (buf, nread, nread, 0, 1, 0);
- else
- { /* We have to decode the input. */
- Lisp_Object curbuf;
- ptrdiff_t count1 = SPECPDL_INDEX ();
-
- XSETBUFFER (curbuf, current_buffer);
- /* We cannot allow after-change-functions be run
- during decoding, because that might modify the
- buffer, while we rely on process_coding.produced to
- faithfully reflect inserted text until we
- TEMP_SET_PT_BOTH below. */
- specbind (Qinhibit_modification_hooks, Qt);
- decode_coding_c_string (&process_coding,
- (unsigned char *) buf, nread, curbuf);
- unbind_to (count1, Qnil);
- if (display_on_the_fly
- && CODING_REQUIRE_DETECTION (&saved_coding)
- && ! CODING_REQUIRE_DETECTION (&process_coding))
- {
- /* We have detected some coding system. But,
- there's a possibility that the detection was
- done by insufficient data. So, we give up
- displaying on the fly. */
- if (process_coding.produced > 0)
- del_range_2 (process_coding.dst_pos,
- process_coding.dst_pos_byte,
- process_coding.dst_pos
- + process_coding.produced_char,
- process_coding.dst_pos_byte
- + process_coding.produced, 0);
- display_on_the_fly = 0;
- process_coding = saved_coding;
- carryover = nread;
- /* This is to make the above condition always
- fails in the future. */
- saved_coding.common_flags
- &= ~CODING_REQUIRE_DETECTION_MASK;
- continue;
- }
-
- TEMP_SET_PT_BOTH (PT + process_coding.produced_char,
- PT_BYTE + process_coding.produced);
- carryover = process_coding.carryover_bytes;
- if (carryover > 0)
- memcpy (buf, process_coding.carryover,
- process_coding.carryover_bytes);
+ if (NILP (BVAR (current_buffer, enable_multibyte_characters))
+ && ! CODING_MAY_REQUIRE_DECODING (&process_coding))
+ insert_1_both (buf, nread, nread, 0, 1, 0);
+ else
+ { /* We have to decode the input. */
+ Lisp_Object curbuf;
+ ptrdiff_t count1 = SPECPDL_INDEX ();
+
+ XSETBUFFER (curbuf, current_buffer);
+ /* We cannot allow after-change-functions be run
+ during decoding, because that might modify the
+ buffer, while we rely on process_coding.produced to
+ faithfully reflect inserted text until we
+ TEMP_SET_PT_BOTH below. */
+ specbind (Qinhibit_modification_hooks, Qt);
+ decode_coding_c_string (&process_coding,
+ (unsigned char *) buf, nread, curbuf);
+ unbind_to (count1, Qnil);
+ if (display_on_the_fly
+ && CODING_REQUIRE_DETECTION (&saved_coding)
+ && ! CODING_REQUIRE_DETECTION (&process_coding))
+ {
+ /* We have detected some coding system. But,
+ there's a possibility that the detection was
+ done by insufficient data. So, we give up
+ displaying on the fly. */
+ if (process_coding.produced > 0)
+ del_range_2 (process_coding.dst_pos,
+ process_coding.dst_pos_byte,
+ process_coding.dst_pos
+ + process_coding.produced_char,
+ process_coding.dst_pos_byte
+ + process_coding.produced, 0);
+ display_on_the_fly = 0;
+ process_coding = saved_coding;
+ carryover = nread;
+ /* This is to make the above condition always
+ fails in the future. */
+ saved_coding.common_flags
+ &= ~CODING_REQUIRE_DETECTION_MASK;
+ continue;
}
+
+ TEMP_SET_PT_BOTH (PT + process_coding.produced_char,
+ PT_BYTE + process_coding.produced);
+ carryover = process_coding.carryover_bytes;
+ if (carryover > 0)
+ memcpy (buf, process_coding.carryover,
+ process_coding.carryover_bytes);
}
if (process_coding.mode & CODING_MODE_LAST_BLOCK)
@@ -901,7 +861,7 @@
#ifndef MSDOS
/* Wait for it to terminate, unless it already has. */
- wait_for_termination (pid, &status, !output_to_buffer);
+ wait_for_termination (pid, &status, fd0 < 0);
#endif
immediate_quit = 0;
@@ -931,37 +891,19 @@
return make_number (WEXITSTATUS (status));
}
\f
-static void
-delete_temp_file (Lisp_Object name)
-{
- /* Suppress jka-compr handling, etc. */
- ptrdiff_t count = SPECPDL_INDEX ();
- specbind (intern ("file-name-handler-alist"), Qnil);
-#ifdef WINDOWSNT
- /* If this is called when the subprocess didn't exit yet, the
- attempt to delete its input file will fail. In that case, we
- schedule the file for deletion when the subprocess exits. This
- is the 2nd part of handling this situation; see the call to
- record_infile in call-process above, for the first part. */
- if (!internal_delete_file (name))
- {
- Lisp_Object encoded_file = ENCODE_FILE (name);
-
- record_pending_deletion (SSDATA (encoded_file));
- }
-#else
- internal_delete_file (name);
-#endif
- unbind_to (count, Qnil);
-}
-
/* Create a temporary file suitable for storing the input data of
call-process-region. NARGS and ARGS are the same as for
- call-process-region. */
+ call-process-region. Store into *FILENAME_STRING_PTR a Lisp string
+ naming the file, and return a read-write file desciptor
+ for the file. Unwind-protect the file, so that the file descriptor
+ will be closed and the file removed when the caller unwinds the
+ specpdl stack. */
-static Lisp_Object
-create_temp_file (ptrdiff_t nargs, Lisp_Object *args)
+static int
+create_temp_file (ptrdiff_t nargs, Lisp_Object *args,
+ Lisp_Object *filename_string_ptr)
{
+ int fd;
struct gcpro gcpro1;
Lisp_Object filename_string;
Lisp_Object val, start, end;
@@ -988,6 +930,7 @@
{
Lisp_Object pattern = Fexpand_file_name (Vtemp_file_name_pattern, tmpdir);
char *tempfile;
+ ptrdiff_t count;
#ifdef WINDOWSNT
/* Cannot use the result of Fexpand_file_name, because it
@@ -1008,15 +951,14 @@
GCPRO1 (filename_string);
tempfile = SSDATA (filename_string);
- {
- int fd = mkostemp (tempfile, O_CLOEXEC);
- if (fd < 0)
- report_file_error ("Failed to open temporary file using pattern",
- pattern);
- emacs_close (fd);
- }
-
- record_unwind_protect (delete_temp_file, filename_string);
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_nothing ();
+ fd = mkostemp (tempfile, O_CLOEXEC);
+ if (fd < 0)
+ report_file_error ("Failed to open temporary file using pattern",
+ pattern);
+ set_unwind_protect (count, delete_temp_file, filename_string);
+ record_unwind_protect_int (close_file_unwind, fd);
}
start = args[0];
@@ -1047,15 +989,19 @@
/* POSIX lets mk[s]temp use "."; don't invoke jka-compr if we
happen to get a ".Z" suffix. */
specbind (intern ("file-name-handler-alist"), Qnil);
- Fwrite_region (start, end, filename_string, Qnil, Qlambda, Qnil, Qnil);
+ write_region (start, end, filename_string, Qnil, Qlambda, Qnil, Qnil, fd);
unbind_to (count1, Qnil);
}
+ if (lseek (fd, 0, SEEK_SET) < 0)
+ report_file_error ("Setting file position", filename_string);
+
/* Note that Fcall_process takes care of binding
coding-system-for-read. */
- RETURN_UNGCPRO (filename_string);
+ *filename_string_ptr = filename_string;
+ RETURN_UNGCPRO (fd);
}
DEFUN ("call-process-region", Fcall_process_region, Scall_process_region,
@@ -1085,12 +1031,13 @@
usage: (call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &rest ARGS) */)
(ptrdiff_t nargs, Lisp_Object *args)
{
- struct gcpro gcpro1;
- Lisp_Object infile;
+ struct gcpro gcpro1, gcpro2;
+ Lisp_Object infile, val;
ptrdiff_t count = SPECPDL_INDEX ();
Lisp_Object start = args[0];
Lisp_Object end = args[1];
bool empty_input;
+ int fd;
if (STRINGP (start))
empty_input = SCHARS (start) == 0;
@@ -1104,8 +1051,19 @@
empty_input = XINT (start) == XINT (end);
}
- infile = empty_input ? Qnil : create_temp_file (nargs, args);
- GCPRO1 (infile);
+ if (empty_input)
+ {
+ infile = Qnil;
+ fd = emacs_open (NULL_DEVICE, O_RDONLY, 0);
+ if (fd < 0)
+ report_file_error ("Opening null device", Qnil);
+ record_unwind_protect_int (close_file_unwind, fd);
+ }
+ else
+ fd = create_temp_file (nargs, args, &infile);
+
+ val = infile;
+ GCPRO2 (infile, val);
if (nargs > 3 && !NILP (args[3]))
Fdelete_region (start, end);
@@ -1122,7 +1080,17 @@
}
args[1] = infile;
- RETURN_UNGCPRO (unbind_to (count, Fcall_process (nargs, args)));
+ val = call_process (nargs, args, fd);
+
+ if (!empty_input && 4 < nargs
+ && (INTEGERP (CONSP (args[4]) ? XCAR (args[4]) : args[4])))
+ {
+ record_deleted_pid (synch_process_pid, infile);
+ synch_process_pid = 0;
+ clear_unwind_protect (count);
+ }
+
+ RETURN_UNGCPRO (unbind_to (count, val));
}
\f
#ifndef WINDOWSNT
@@ -1683,6 +1651,11 @@
#endif
staticpro (&Vtemp_file_name_pattern);
+#ifdef MSDOS
+ synch_process_tempfile = make_number (0);
+ staticpro (&synch_process_tempfile);
+#endif
+
DEFVAR_LISP ("shell-file-name", Vshell_file_name,
doc: /* File name to load inferior shells from.
Initialized from the SHELL environment variable, or to a system-dependent
=== modified file 'src/eval.c'
--- src/eval.c 2013-08-02 21:16:33 +0000
+++ src/eval.c 2013-08-06 11:47:10 +0000
@@ -3302,6 +3302,16 @@
previous value without invoking it. */
void
+set_unwind_protect (ptrdiff_t count, void (*func) (Lisp_Object),
+ Lisp_Object arg)
+{
+ union specbinding *p = specpdl + count;
+ p->unwind.kind = SPECPDL_UNWIND;
+ p->unwind.func = func;
+ p->unwind.arg = arg;
+}
+
+void
set_unwind_protect_ptr (ptrdiff_t count, void (*func) (void *), void *arg)
{
union specbinding *p = specpdl + count;
=== modified file 'src/fileio.c'
--- src/fileio.c 2013-08-05 04:14:43 +0000
+++ src/fileio.c 2013-08-06 11:47:10 +0000
@@ -4746,25 +4746,39 @@
This calls `write-region-annotate-functions' at the start, and
`write-region-post-annotation-function' at the end. */)
- (Lisp_Object start, Lisp_Object end, Lisp_Object filename, Lisp_Object append, Lisp_Object visit, Lisp_Object lockname, Lisp_Object mustbenew)
-{
- int desc;
+ (Lisp_Object start, Lisp_Object end, Lisp_Object filename, Lisp_Object append,
+ Lisp_Object visit, Lisp_Object lockname, Lisp_Object mustbenew)
+{
+ return write_region (start, end, filename, append, visit, lockname, mustbenew,
+ -1);
+}
+
+/* Like Fwrite_region, except that if DESC is nonnegative, it is a file
+ descriptor for FILENAME, so do not open or close FILENAME. */
+
+Lisp_Object
+write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
+ Lisp_Object append, Lisp_Object visit, Lisp_Object lockname,
+ Lisp_Object mustbenew, int desc)
+{
int open_flags;
int mode;
off_t offset IF_LINT (= 0);
+ bool open_and_close_file = desc < 0;
bool ok;
int save_errno = 0;
const char *fn;
struct stat st;
EMACS_TIME modtime;
ptrdiff_t count = SPECPDL_INDEX ();
- ptrdiff_t count1;
+ ptrdiff_t count1 IF_LINT (= 0);
Lisp_Object handler;
Lisp_Object visit_file;
Lisp_Object annotations;
Lisp_Object encoded_filename;
bool visiting = (EQ (visit, Qt) || STRINGP (visit));
bool quietly = !NILP (visit);
+ bool file_locked = 0;
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
struct buffer *given_buffer;
struct coding_system coding;
@@ -4832,7 +4846,6 @@
record_unwind_protect (build_annotations_unwind,
Vwrite_region_annotation_buffers);
Vwrite_region_annotation_buffers = list1 (Fcurrent_buffer ());
- count1 = SPECPDL_INDEX ();
given_buffer = current_buffer;
@@ -4871,8 +4884,11 @@
coding.mode |= CODING_MODE_SELECTIVE_DISPLAY;
#ifdef CLASH_DETECTION
- if (!auto_saving)
- lock_file (lockname);
+ if (open_and_close_file && !auto_saving)
+ {
+ lock_file (lockname);
+ file_locked = 1;
+ }
#endif /* CLASH_DETECTION */
encoded_filename = ENCODE_FILE (filename);
@@ -4889,20 +4905,24 @@
mode = auto_saving ? auto_save_mode_bits : 0666;
#endif
- desc = emacs_open (fn, open_flags, mode);
-
- if (desc < 0)
+ if (open_and_close_file)
{
- int open_errno = errno;
+ desc = emacs_open (fn, open_flags, mode);
+ if (desc < 0)
+ {
+ int open_errno = errno;
#ifdef CLASH_DETECTION
- if (!auto_saving) unlock_file (lockname);
+ if (file_locked)
+ unlock_file (lockname);
#endif /* CLASH_DETECTION */
- UNGCPRO;
- report_file_errno ("Opening output file", filename, open_errno);
+ UNGCPRO;
+ report_file_errno ("Opening output file", filename, open_errno);
+ }
+
+ count1 = SPECPDL_INDEX ();
+ record_unwind_protect_int (close_file_unwind, desc);
}
- record_unwind_protect_int (close_file_unwind, desc);
-
if (NUMBERP (append))
{
off_t ret = lseek (desc, offset, SEEK_SET);
@@ -4910,7 +4930,8 @@
{
int lseek_errno = errno;
#ifdef CLASH_DETECTION
- if (!auto_saving) unlock_file (lockname);
+ if (file_locked)
+ unlock_file (lockname);
#endif /* CLASH_DETECTION */
UNGCPRO;
report_file_errno ("Lseek error", filename, lseek_errno);
@@ -4945,9 +4966,9 @@
immediate_quit = 0;
- /* fsync is not crucial for auto-save files, since they might lose
- some work anyway. */
- if (!auto_saving && !write_region_inhibit_fsync)
+ /* fsync is not crucial for temporary files. Nor for auto-save
+ files, since they might lose some work anyway. */
+ if (open_and_close_file && !auto_saving && !write_region_inhibit_fsync)
{
/* Transfer data and metadata to disk, retrying if interrupted.
fsync can report a write failure here, e.g., due to disk full
@@ -4971,12 +4992,15 @@
ok = 0, save_errno = errno;
}
- /* NFS can report a write failure now. */
- if (emacs_close (desc) < 0)
- ok = 0, save_errno = errno;
+ if (open_and_close_file)
+ {
+ /* NFS can report a write failure now. */
+ if (emacs_close (desc) < 0)
+ ok = 0, save_errno = errno;
- /* Discard the unwind protect for close_file_unwind. */
- specpdl_ptr = specpdl + count1;
+ /* Discard the unwind protect for close_file_unwind. */
+ specpdl_ptr = specpdl + count1;
+ }
/* Some file systems have a bug where st_mtime is not updated
properly after a write. For example, CIFS might not see the
@@ -5052,7 +5076,7 @@
unbind_to (count, Qnil);
#ifdef CLASH_DETECTION
- if (!auto_saving)
+ if (file_locked)
unlock_file (lockname);
#endif /* CLASH_DETECTION */
=== modified file 'src/lisp.h'
--- src/lisp.h 2013-08-06 05:30:18 +0000
+++ src/lisp.h 2013-08-06 11:47:10 +0000
@@ -3746,11 +3746,12 @@
Lisp_Object, Lisp_Object (*) (Lisp_Object, ptrdiff_t, Lisp_Object *));
extern void specbind (Lisp_Object, Lisp_Object);
extern void record_unwind_protect (void (*) (Lisp_Object), Lisp_Object);
+extern void record_unwind_protect_ptr (void (*) (void *), void *);
extern void record_unwind_protect_int (void (*) (int), int);
-extern void record_unwind_protect_ptr (void (*) (void *), void *);
extern void record_unwind_protect_void (void (*) (void));
extern void record_unwind_protect_nothing (void);
extern void clear_unwind_protect (ptrdiff_t);
+extern void set_unwind_protect (ptrdiff_t, void (*) (Lisp_Object), Lisp_Object);
extern void set_unwind_protect_ptr (ptrdiff_t, void (*) (void *), void *);
extern Lisp_Object unbind_to (ptrdiff_t, Lisp_Object);
extern _Noreturn void error (const char *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
@@ -3830,6 +3831,9 @@
extern Lisp_Object Qinsert_file_contents;
extern Lisp_Object Qfile_name_history;
extern Lisp_Object expand_and_dir_to_file (Lisp_Object, Lisp_Object);
+extern Lisp_Object write_region (Lisp_Object, Lisp_Object, Lisp_Object,
+ Lisp_Object, Lisp_Object, Lisp_Object,
+ Lisp_Object, int);
EXFUN (Fread_file_name, 6); /* Not a normal DEFUN. */
extern void close_file_unwind (int);
extern void fclose_unwind (void *);
=== modified file 'src/process.c'
--- src/process.c 2013-08-06 14:17:25 +0000
+++ src/process.c 2013-08-06 16:32:10 +0000
@@ -92,6 +92,7 @@
#include <c-ctype.h>
#include <sig2str.h>
+#include <verify.h>
#endif /* subprocesses */
@@ -722,6 +723,8 @@
non-Lisp data, so do it only for slots which should not be zero. */
p->infd = -1;
p->outfd = -1;
+ for (i = 0; i < PROCESS_OPEN_FDS; i++)
+ p->open_fd[i] = -1;
#ifdef HAVE_GNUTLS
p->gnutls_initstage = GNUTLS_STAGE_EMPTY;
@@ -818,13 +821,17 @@
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. */
+ Some processes created by call-process are also put onto this list.
+
+ Members of this list are (process-ID . filename) pairs. The
+ process-ID is a number; the filename, if a string, is a file that
+ needs to be removed after the process exits. */
static Lisp_Object deleted_pid_list;
void
-record_deleted_pid (pid_t pid)
+record_deleted_pid (pid_t pid, Lisp_Object filename)
{
- deleted_pid_list = Fcons (make_fixnum_or_float (pid),
+ deleted_pid_list = Fcons (Fcons (make_fixnum_or_float (pid), filename),
/* GC treated elements set to nil. */
Fdelq (Qnil, deleted_pid_list));
@@ -852,7 +859,7 @@
else
{
if (p->alive)
- record_kill_process (p);
+ record_kill_process (p, Qnil);
if (p->infd >= 0)
{
@@ -1605,17 +1612,45 @@
remove_process (proc);
}
+/* If *FD_ADDR is nonnegative, close it, and mark it as closed. */
+
+static void
+close_process_fd (int *fd_addr)
+{
+ int fd = *fd_addr;
+ if (0 <= fd)
+ {
+ *fd_addr = -1;
+ emacs_close (fd);
+ }
+}
+
+/* Indexes of file descriptors in open_fds. */
+enum
+ {
+ /* The pipe from Emacs to its subprocess. */
+ SUBPROCESS_STDIN,
+ WRITE_TO_SUBPROCESS,
+
+ /* The main pipe from the subprocess to Emacs. */
+ READ_FROM_SUBPROCESS,
+ SUBPROCESS_STDOUT,
+
+ /* The pipe from the subprocess to Emacs that is closed when the
+ subprocess execs. */
+ READ_FROM_EXEC_MONITOR,
+ EXEC_MONITOR_OUTPUT
+ };
+
+verify (PROCESS_OPEN_FDS == EXEC_MONITOR_OUTPUT + 1);
static void
create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
{
+ struct Lisp_Process *p = XPROCESS (process);
int inchannel, outchannel;
pid_t pid;
int vfork_errno;
- int sv[2];
-#ifndef WINDOWSNT
- int wait_child_setup[2];
-#endif
int forkin, forkout;
bool pty_flag = 0;
char pty_name[PTY_NAME_SIZE];
@@ -1629,6 +1664,7 @@
if (inchannel >= 0)
{
+ p->open_fd[READ_FROM_SUBPROCESS] = inchannel;
#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. */
@@ -1637,6 +1673,7 @@
forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
if (forkin < 0)
report_file_error ("Opening pty", Qnil);
+ p->open_fd[SUBPROCESS_STDIN] = forkin;
#else
forkin = forkout = -1;
#endif /* not USG, or USG_SUBTTY_WORKS */
@@ -1645,23 +1682,17 @@
}
else
{
- if (emacs_pipe (sv) != 0)
+ if (emacs_pipe (p->open_fd + SUBPROCESS_STDIN) != 0
+ || emacs_pipe (p->open_fd + READ_FROM_SUBPROCESS) != 0)
report_file_error ("Creating pipe", Qnil);
- inchannel = sv[0];
- forkout = sv[1];
- if (emacs_pipe (sv) != 0)
- {
- int pipe_errno = errno;
- emacs_close (inchannel);
- emacs_close (forkout);
- report_file_errno ("Creating pipe", Qnil, pipe_errno);
- }
- outchannel = sv[1];
- forkin = sv[0];
+ forkin = p->open_fd[SUBPROCESS_STDIN];
+ outchannel = p->open_fd[WRITE_TO_SUBPROCESS];
+ inchannel = p->open_fd[READ_FROM_SUBPROCESS];
+ forkout = p->open_fd[SUBPROCESS_STDOUT];
}
#ifndef WINDOWSNT
- if (emacs_pipe (wait_child_setup) != 0)
+ if (emacs_pipe (p->open_fd + READ_FROM_EXEC_MONITOR) != 0)
report_file_error ("Creating pipe", Qnil);
#endif
@@ -1670,16 +1701,16 @@
/* Record this as an active process, with its channels. */
chan_process[inchannel] = process;
- XPROCESS (process)->infd = inchannel;
- XPROCESS (process)->outfd = outchannel;
+ p->infd = inchannel;
+ p->outfd = outchannel;
/* Previously we recorded the tty descriptor used in the subprocess.
It was only used for getting the foreground tty process, so now
we just reopen the device (see emacs_get_tty_pgrp) as this is
more portable (see USG_SUBTTY_WORKS above). */
- XPROCESS (process)->pty_flag = pty_flag;
- pset_status (XPROCESS (process), Qrun);
+ p->pty_flag = pty_flag;
+ pset_status (p, Qrun);
FD_SET (inchannel, &input_wait_mask);
FD_SET (inchannel, &non_keyboard_wait_mask);
@@ -1699,25 +1730,21 @@
{
Lisp_Object volatile encoded_current_dir_volatile = encoded_current_dir;
Lisp_Object volatile lisp_pty_name_volatile = lisp_pty_name;
- Lisp_Object volatile process_volatile = process;
char **volatile new_argv_volatile = new_argv;
int volatile forkin_volatile = forkin;
int volatile forkout_volatile = forkout;
- int volatile wait_child_setup_0_volatile = wait_child_setup[0];
- int volatile wait_child_setup_1_volatile = wait_child_setup[1];
+ struct Lisp_Process *p_volatile = p;
pid = vfork ();
encoded_current_dir = encoded_current_dir_volatile;
lisp_pty_name = lisp_pty_name_volatile;
- process = process_volatile;
new_argv = new_argv_volatile;
forkin = forkin_volatile;
forkout = forkout_volatile;
- wait_child_setup[0] = wait_child_setup_0_volatile;
- wait_child_setup[1] = wait_child_setup_1_volatile;
+ p = p_volatile;
- pty_flag = XPROCESS (process)->pty_flag;
+ pty_flag = p->pty_flag;
}
if (pid == 0)
@@ -1833,42 +1860,42 @@
/* Back in the parent process. */
vfork_errno = errno;
- XPROCESS (process)->pid = pid;
+ p->pid = pid;
if (pid >= 0)
- XPROCESS (process)->alive = 1;
+ p->alive = 1;
/* Stop blocking in the parent. */
unblock_child_signal ();
unblock_input ();
- if (forkin >= 0)
- emacs_close (forkin);
- if (forkin != forkout && forkout >= 0)
- emacs_close (forkout);
-
if (pid < 0)
report_file_errno ("Doing vfork", Qnil, vfork_errno);
else
{
/* vfork succeeded. */
+ /* Close the pipe ends that the child uses, or the child's pty. */
+ close_process_fd (&p->open_fd[SUBPROCESS_STDIN]);
+ close_process_fd (&p->open_fd[SUBPROCESS_STDOUT]);
+
#ifdef WINDOWSNT
register_child (pid, inchannel);
#endif /* WINDOWSNT */
- pset_tty_name (XPROCESS (process), lisp_pty_name);
+ pset_tty_name (p, lisp_pty_name);
#ifndef WINDOWSNT
/* Wait for child_setup to complete in case that vfork is
- actually defined as fork. The descriptor wait_child_setup[1]
+ actually defined as fork. The descriptor
+ XPROCESS (proc)->open_fd[EXEC_MOINTOR_OUTPUT]
of a pipe is closed at the child side either by close-on-exec
on successful execve or the _exit call in child_setup. */
{
char dummy;
- emacs_close (wait_child_setup[1]);
- emacs_read (wait_child_setup[0], &dummy, 1);
- emacs_close (wait_child_setup[0]);
+ close_process_fd (&p->open_fd[EXEC_MONITOR_OUTPUT]);
+ emacs_read (p->open_fd[READ_FROM_EXEC_MONITOR], &dummy, 1);
+ close_process_fd (&p->open_fd[READ_FROM_EXEC_MONITOR]);
}
#endif
}
@@ -1877,16 +1904,13 @@
static void
create_pty (Lisp_Object process)
{
+ struct Lisp_Process *p = XPROCESS (process);
char pty_name[PTY_NAME_SIZE];
- int inchannel, outchannel;
-
- inchannel = outchannel = -1;
-
- if (!NILP (Vprocess_connection_type))
- outchannel = inchannel = allocate_pty (pty_name);
-
- if (inchannel >= 0)
+ int pty_fd = NILP (Vprocess_connection_type) ? -1 : allocate_pty (pty_name);
+
+ if (pty_fd >= 0)
{
+ p->open_fd[SUBPROCESS_STDIN] = pty_fd;
#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. */
@@ -1895,6 +1919,7 @@
int forkout = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
if (forkout < 0)
report_file_error ("Opening pty", Qnil);
+ p->open_fd[WRITE_TO_SUBPROCESS] = forkout;
#if defined (DONT_REOPEN_PTY)
/* In the case that vfork is defined as fork, the parent process
(Emacs) may send some data before the child process completes
@@ -1903,33 +1928,32 @@
#endif /* DONT_REOPEN_PTY */
#endif /* not USG, or USG_SUBTTY_WORKS */
- fcntl (inchannel, F_SETFL, O_NONBLOCK);
- fcntl (outchannel, F_SETFL, O_NONBLOCK);
+ fcntl (pty_fd, F_SETFL, O_NONBLOCK);
/* Record this as an active process, with its channels.
As a result, child_setup will close Emacs's side of the pipes. */
- chan_process[inchannel] = process;
- XPROCESS (process)->infd = inchannel;
- XPROCESS (process)->outfd = outchannel;
+ chan_process[pty_fd] = process;
+ p->infd = pty_fd;
+ p->outfd = pty_fd;
/* Previously we recorded the tty descriptor used in the subprocess.
It was only used for getting the foreground tty process, so now
we just reopen the device (see emacs_get_tty_pgrp) as this is
more portable (see USG_SUBTTY_WORKS above). */
- XPROCESS (process)->pty_flag = 1;
- pset_status (XPROCESS (process), Qrun);
+ p->pty_flag = 1;
+ pset_status (p, Qrun);
setup_process_coding_systems (process);
- FD_SET (inchannel, &input_wait_mask);
- FD_SET (inchannel, &non_keyboard_wait_mask);
- if (inchannel > max_process_desc)
- max_process_desc = inchannel;
+ FD_SET (pty_fd, &input_wait_mask);
+ FD_SET (pty_fd, &non_keyboard_wait_mask);
+ if (pty_fd > max_process_desc)
+ max_process_desc = pty_fd;
- pset_tty_name (XPROCESS (process), build_string (pty_name));
+ pset_tty_name (p, build_string (pty_name));
}
- XPROCESS (process)->pid = -2;
+ p->pid = -2;
}
\f
@@ -2535,6 +2559,7 @@
p = XPROCESS (proc);
fd = serial_open (port);
+ p->open_fd[SUBPROCESS_STDIN] = fd;
p->infd = fd;
p->outfd = fd;
if (fd > max_process_desc)
@@ -3297,12 +3322,6 @@
}
#endif
- /* Discard the unwind protect for closing S, if any. */
- specpdl_ptr = specpdl + count1;
-
- /* Unwind bind_polling_period and request_sigio. */
- unbind_to (count, Qnil);
-
if (s < 0)
{
/* If non-blocking got this far - and failed - assume non-blocking is
@@ -3344,8 +3363,17 @@
if ((tem = Fplist_get (contact, QCstop), !NILP (tem)))
pset_command (p, Qt);
p->pid = 0;
+
+ p->open_fd[SUBPROCESS_STDIN] = inch;
p->infd = inch;
p->outfd = outch;
+
+ /* Discard the unwind protect for closing S, if any. */
+ specpdl_ptr = specpdl + count1;
+
+ /* Unwind bind_polling_period and request_sigio. */
+ unbind_to (count, Qnil);
+
if (is_server && socktype != SOCK_DGRAM)
pset_status (p, Qlisten);
@@ -3784,17 +3812,15 @@
static void
deactivate_process (Lisp_Object proc)
{
- register int inchannel, outchannel;
- register struct Lisp_Process *p = XPROCESS (proc);
+ int inchannel;
+ struct Lisp_Process *p = XPROCESS (proc);
+ int i;
#ifdef HAVE_GNUTLS
/* Delete GnuTLS structures in PROC, if any. */
emacs_gnutls_deinit (proc);
#endif /* HAVE_GNUTLS */
- inchannel = p->infd;
- outchannel = p->outfd;
-
#ifdef ADAPTIVE_READ_BUFFERING
if (p->read_output_delay > 0)
{
@@ -3805,16 +3831,17 @@
}
#endif
+ inchannel = p->infd;
+
+ /* Beware SIGCHLD hereabouts. */
+ if (inchannel >= 0)
+ flush_pending_output (inchannel);
+
+ for (i = 0; i < PROCESS_OPEN_FDS; i++)
+ close_process_fd (&p->open_fd[i]);
+
if (inchannel >= 0)
{
- /* Beware SIGCHLD hereabouts. */
- flush_pending_output (inchannel);
- emacs_close (inchannel);
- if (outchannel >= 0 && outchannel != inchannel)
- emacs_close (outchannel);
-
- p->infd = -1;
- p->outfd = -1;
#ifdef DATAGRAM_SOCKETS
if (DATAGRAM_CHAN_P (inchannel))
{
@@ -4095,6 +4122,7 @@
/* Discard the unwind protect for closing S. */
specpdl_ptr = specpdl + count;
+ p->open_fd[SUBPROCESS_STDIN] = s;
p->infd = s;
p->outfd = s;
pset_status (p, Qrun);
@@ -6014,7 +6042,8 @@
}
else
{
- int old_outfd, new_outfd;
+ int old_outfd = XPROCESS (proc)->outfd;
+ int new_outfd;
#ifdef HAVE_SHUTDOWN
/* If this is a network connection, or socketpair is used
@@ -6022,18 +6051,15 @@
(In some old system, shutdown to socketpair doesn't work.
Then we just can't win.) */
if (EQ (XPROCESS (proc)->type, Qnetwork)
- || XPROCESS (proc)->outfd == XPROCESS (proc)->infd)
- shutdown (XPROCESS (proc)->outfd, 1);
- /* In case of socketpair, outfd == infd, so don't close it. */
- if (XPROCESS (proc)->outfd != XPROCESS (proc)->infd)
- emacs_close (XPROCESS (proc)->outfd);
-#else /* not HAVE_SHUTDOWN */
- emacs_close (XPROCESS (proc)->outfd);
-#endif /* not HAVE_SHUTDOWN */
+ || XPROCESS (proc)->infd == old_outfd)
+ shutdown (old_outfd, 1);
+#endif
+ close_process_fd (&XPROCESS (proc)->open_fd[WRITE_TO_SUBPROCESS]);
new_outfd = emacs_open (NULL_DEVICE, O_WRONLY, 0);
if (new_outfd < 0)
- emacs_abort ();
- old_outfd = XPROCESS (proc)->outfd;
+ report_file_error ("Opening null device", Qnil);
+ XPROCESS (proc)->open_fd[WRITE_TO_SUBPROCESS] = new_outfd;
+ XPROCESS (proc)->outfd = new_outfd;
if (!proc_encode_coding_system[new_outfd])
proc_encode_coding_system[new_outfd]
@@ -6042,8 +6068,6 @@
= *proc_encode_coding_system[old_outfd];
memset (proc_encode_coding_system[old_outfd], 0,
sizeof (struct coding_system));
-
- XPROCESS (proc)->outfd = new_outfd;
}
return process;
}
@@ -6120,7 +6144,8 @@
bool all_pids_are_fixnums
= (MOST_NEGATIVE_FIXNUM <= TYPE_MINIMUM (pid_t)
&& TYPE_MAXIMUM (pid_t) <= MOST_POSITIVE_FIXNUM);
- Lisp_Object xpid = XCAR (tail);
+ Lisp_Object head = XCAR (tail);
+ Lisp_Object xpid = XCAR (head);
if (all_pids_are_fixnums ? INTEGERP (xpid) : NUMBERP (xpid))
{
pid_t deleted_pid;
@@ -6129,7 +6154,11 @@
else
deleted_pid = XFLOAT_DATA (xpid);
if (child_status_changed (deleted_pid, 0, 0))
- XSETCAR (tail, Qnil);
+ {
+ if (STRINGP (XCDR (head)))
+ unlink (SSDATA (XCDR (head)));
+ XSETCAR (tail, Qnil);
+ }
}
}
=== modified file 'src/process.h'
--- src/process.h 2013-07-09 07:04:48 +0000
+++ src/process.h 2013-08-06 11:47:10 +0000
@@ -31,6 +31,11 @@
# define PROCESS_INLINE INLINE
#endif
+/* Bound on number of file descriptors opened on behalf of a process,
+ that need to be closed. */
+
+enum { PROCESS_OPEN_FDS = 6 };
+
/* This structure records information about a subprocess
or network connection. */
@@ -115,6 +120,9 @@
int infd;
/* Descriptor by which we write to this process */
int outfd;
+ /* Descriptors that were created for this process and that need
+ closing. Unused entries are negative. */
+ int open_fd[PROCESS_OPEN_FDS];
/* Event-count of last event in which this process changed status. */
EMACS_INT tick;
/* Event-count of last such event reported. */
@@ -210,13 +218,16 @@
extern void block_child_signal (void);
extern void unblock_child_signal (void);
-extern void record_kill_process (struct Lisp_Process *);
+extern void record_kill_process (struct Lisp_Process *, Lisp_Object);
-/* Defined in process.c. */
+/* Defined in sysdep.c. */
extern Lisp_Object list_system_processes (void);
extern Lisp_Object system_process_attributes (Lisp_Object);
+/* Defined in process.c. */
+
+extern void record_deleted_pid (pid_t, Lisp_Object);
extern void hold_keyboard_input (void);
extern void unhold_keyboard_input (void);
extern bool kbd_on_hold_p (void);
=== modified file 'src/syswait.h'
--- src/syswait.h 2013-01-02 16:13:04 +0000
+++ src/syswait.h 2013-08-06 11:47:10 +0000
@@ -52,9 +52,6 @@
#define WTERMSIG(status) ((status) & 0x7f)
#endif
-/* Defined in process.c. */
-extern void record_deleted_pid (pid_t);
-
/* Defined in sysdep.c. */
extern void wait_for_termination (pid_t, int *, bool);
extern pid_t child_status_changed (pid_t, int *, int);
^ permalink raw reply [flat|nested] 2+ messages in thread
* bug#15035: Fix some fd issues when running subprocesses
2013-08-06 17:15 bug#15035: Fix some fd issues when running subprocesses Paul Eggert
@ 2013-08-12 7:14 ` Paul Eggert
0 siblings, 0 replies; 2+ messages in thread
From: Paul Eggert @ 2013-08-12 7:14 UTC (permalink / raw)
To: 15035-done
Installed as trunk bzr 113813, and marking this as done.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2013-08-12 7:14 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-08-06 17:15 bug#15035: Fix some fd issues when running subprocesses Paul Eggert
2013-08-12 7:14 ` Paul Eggert
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.