Various fixes around Bug#16013.
[bpt/emacs.git] / src / w32proc.c
index ca2096a..3e0081c 100644 (file)
@@ -21,6 +21,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
      Adapted from alarm.c by Tim Fleehart
 */
 
+#include <mingw_time.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -230,14 +231,14 @@ sigismember (const sigset_t *set, int signo)
   return (*set & (1U << signo)) != 0;
 }
 
-int
-setpgrp (int pid, int gid)
+pid_t
+getpgrp (void)
 {
-  return 0;
+  return getpid ();
 }
 
 pid_t
-getpgrp (void)
+tcgetpgrp (int fd)
 {
   return getpid ();
 }
@@ -248,6 +249,12 @@ setpgid (pid_t pid, pid_t pgid)
   return 0;
 }
 
+pid_t
+setsid (void)
+{
+  return getpid ();
+}
+
 /* Emulations of interval timers.
 
    Limitations: only ITIMER_REAL and ITIMER_PROF are supported.
@@ -783,7 +790,6 @@ alarm (int seconds)
 /* Child process management list.  */
 int child_proc_count = 0;
 child_process child_procs[ MAX_CHILDREN ];
-child_process *dead_child = NULL;
 
 static DWORD WINAPI reader_thread (void *arg);
 
@@ -844,11 +850,19 @@ new_child (void)
   cp = &child_procs[child_proc_count++];
 
  Initialize:
+  /* Last opportunity to avoid leaking handles before we forget them
+     for good.  */
+  if (cp->procinfo.hProcess)
+    CloseHandle (cp->procinfo.hProcess);
+  if (cp->procinfo.hThread)
+    CloseHandle (cp->procinfo.hThread);
   memset (cp, 0, sizeof (*cp));
   cp->fd = -1;
   cp->pid = -1;
   cp->procinfo.hProcess = NULL;
   cp->status = STATUS_READ_ERROR;
+  cp->input_file = NULL;
+  cp->pending_deletion = 0;
 
   /* use manual reset event so that select() will function properly */
   cp->char_avail = CreateEvent (NULL, TRUE, FALSE, NULL);
@@ -897,6 +911,21 @@ delete_child (child_process *cp)
   if (!CHILD_ACTIVE (cp) && cp->procinfo.hProcess == NULL)
     return;
 
+  /* Delete the child's temporary input file, if any, that is pending
+     deletion.  */
+  if (cp->input_file)
+    {
+      if (cp->pending_deletion)
+       {
+         if (unlink (cp->input_file))
+           DebPrint (("delete_child.unlink (%s) failed, errno: %d\n",
+                      cp->input_file, errno));
+         cp->pending_deletion = 0;
+       }
+      xfree (cp->input_file);
+      cp->input_file = NULL;
+    }
+
   /* reap thread if necessary */
   if (cp->thrd)
     {
@@ -962,6 +991,18 @@ find_child_pid (DWORD pid)
   return NULL;
 }
 
+void
+release_listen_threads (void)
+{
+  int i;
+
+  for (i = child_proc_count - 1; i >= 0; i--)
+    {
+      if (CHILD_ACTIVE (&child_procs[i])
+         && (fd_info[child_procs[i].fd].flags & FILE_LISTEN))
+       child_procs[i].status = STATUS_READ_ERROR;
+    }
+}
 
 /* Thread proc for child process and socket reader threads. Each thread
    is normally blocked until woken by select() to check for input by
@@ -985,21 +1026,15 @@ reader_thread (void *arg)
     {
       int rc;
 
-      if (fd_info[cp->fd].flags & FILE_LISTEN)
+      if (cp->fd >= 0 && fd_info[cp->fd].flags & FILE_LISTEN)
        rc = _sys_wait_accept (cp->fd);
       else
        rc = _sys_read_ahead (cp->fd);
 
-      if (!CHILD_ACTIVE (cp) && cp->procinfo.hProcess && cp->fd >= 0)
-       {
-         /* Somebody already called delete_child on this child, since
-            only delete_child zeroes out cp->char_avail.  This means
-            no one will read from cp->fd and will not set the
-            FILE_AT_EOF flag, therefore preventing sys_select from
-            noticing that the process died.  Set the flag here
-            instead.  */
-         fd_info[cp->fd].flags |= FILE_AT_EOF;
-       }
+      /* Don't bother waiting for the event if we already have been
+        told to exit by delete_child.  */
+      if (cp->status == STATUS_READ_ERROR || !cp->char_avail)
+       break;
 
       /* The name char_avail is a misnomer - it really just means the
         read-ahead has completed, whether successfully or not. */
@@ -1018,6 +1053,11 @@ reader_thread (void *arg)
       if (rc == STATUS_READ_FAILED)
        break;
 
+      /* Don't bother waiting for the acknowledge if we already have
+        been told to exit by delete_child.  */
+      if (cp->status == STATUS_READ_ERROR || !cp->char_consumed)
+       break;
+
       /* Wait until our input is acknowledged before reading again */
       if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
         {
@@ -1025,6 +1065,10 @@ reader_thread (void *arg)
                     "%lu for fd %ld\n", GetLastError (), cp->fd));
          break;
         }
+      /* delete_child sets status to STATUS_READ_ERROR when it wants
+        us to exit.  */
+      if (cp->status == STATUS_READ_ERROR)
+       break;
     }
   return 0;
 }
@@ -1092,9 +1136,6 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app,
   if (cp->pid < 0)
     cp->pid = -cp->pid;
 
-  /* pid must fit in a Lisp_Int */
-  cp->pid = cp->pid & INTMASK;
-
   *pPid = cp->pid;
 
   return TRUE;
@@ -1104,17 +1145,17 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app,
   return FALSE;
 }
 
-/* create_child doesn't know what emacs' file handle will be for waiting
+/* create_child doesn't know what emacs's file handle will be for waiting
    on output from the child, so we need to make this additional call
    to register the handle with the process
    This way the select emulator knows how to match file handles with
    entries in child_procs.  */
 void
-register_child (int pid, int fd)
+register_child (pid_t pid, int fd)
 {
   child_process *cp;
 
-  cp = find_child_pid (pid);
+  cp = find_child_pid ((DWORD)pid);
   if (cp == NULL)
     {
       DebPrint (("register_child unable to find pid %lu\n", pid));
@@ -1141,10 +1182,46 @@ register_child (int pid, int fd)
   fd_info[fd].cp = cp;
 }
 
-/* When a process dies its pipe will break so the reader thread will
-   signal failure to the select emulator.
-   The select emulator then calls this routine to clean up.
-   Since the thread signaled failure we can assume it is exiting.  */
+/* Record INFILE as an input file for process PID.  */
+void
+record_infile (pid_t pid, char *infile)
+{
+  child_process *cp;
+
+  /* INFILE should never be NULL, since xstrdup would have signaled
+     memory full condition in that case, see callproc.c where this
+     function is called.  */
+  eassert (infile);
+
+  cp = find_child_pid ((DWORD)pid);
+  if (cp == NULL)
+    {
+      DebPrint (("record_infile is unable to find pid %lu\n", pid));
+      return;
+    }
+
+  cp->input_file = infile;
+}
+
+/* Mark the input file INFILE of the corresponding subprocess as
+   temporary, to be deleted when the subprocess exits.  */
+void
+record_pending_deletion (char *infile)
+{
+  child_process *cp;
+
+  eassert (infile);
+
+  for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--)
+    if (CHILD_ACTIVE (cp)
+       && cp->input_file && xstrcasecmp (cp->input_file, infile) == 0)
+      {
+       cp->pending_deletion = 1;
+       break;
+      }
+}
+
+/* Called from waitpid when a process exits.  */
 static void
 reap_subprocess (child_process *cp)
 {
@@ -1154,7 +1231,7 @@ reap_subprocess (child_process *cp)
 #ifdef FULL_DEBUG
       /* Process should have already died before we are called.  */
       if (WaitForSingleObject (cp->procinfo.hProcess, 0) != WAIT_OBJECT_0)
-       DebPrint (("reap_subprocess: child fpr fd %d has not died yet!", cp->fd));
+       DebPrint (("reap_subprocess: child for fd %d has not died yet!", cp->fd));
 #endif
       CloseHandle (cp->procinfo.hProcess);
       cp->procinfo.hProcess = NULL;
@@ -1162,75 +1239,134 @@ reap_subprocess (child_process *cp)
       cp->procinfo.hThread = NULL;
     }
 
-  /* For asynchronous children, the child_proc resources will be freed
-     when the last pipe read descriptor is closed; for synchronous
-     children, we must explicitly free the resources now because
-     register_child has not been called. */
-  if (cp->fd == -1)
+  /* If cp->fd was not closed yet, we might be still reading the
+     process output, so don't free its resources just yet.  The call
+     to delete_child on behalf of this subprocess will be made by
+     sys_read when the subprocess output is fully read.  */
+  if (cp->fd < 0)
     delete_child (cp);
-  else
-    {
-      /* Reset the flag set by reader_thread.  */
-      fd_info[cp->fd].flags &= ~FILE_AT_EOF;
-    }
 }
 
-/* Wait for any of our existing child processes to die
-   When it does, close its handle
-   Return the pid and fill in the status if non-NULL.  */
+/* Wait for a child process specified by PID, or for any of our
+   existing child processes (if PID is nonpositive) to die.  When it
+   does, close its handle.  Return the pid of the process that died
+   and fill in STATUS if non-NULL.  */
 
-int
-sys_wait (int *status)
+pid_t
+waitpid (pid_t pid, int *status, int options)
 {
   DWORD active, retval;
   int nh;
-  int pid;
   child_process *cp, *cps[MAX_CHILDREN];
   HANDLE wait_hnd[MAX_CHILDREN];
+  DWORD timeout_ms;
+  int dont_wait = (options & WNOHANG) != 0;
 
   nh = 0;
-  if (dead_child != NULL)
+  /* According to Posix:
+
+     PID = -1 means status is requested for any child process.
+
+     PID > 0 means status is requested for a single child process
+     whose pid is PID.
+
+     PID = 0 means status is requested for any child process whose
+     process group ID is equal to that of the calling process.  But
+     since Windows has only a limited support for process groups (only
+     for console processes and only for the purposes of passing
+     Ctrl-BREAK signal to them), and since we have no documented way
+     of determining whether a given process belongs to our group, we
+     treat 0 as -1.
+
+     PID < -1 means status is requested for any child process whose
+     process group ID is equal to the absolute value of PID.  Again,
+     since we don't support process groups, we treat that as -1.  */
+  if (pid > 0)
     {
-      /* We want to wait for a specific child */
-      wait_hnd[nh] = dead_child->procinfo.hProcess;
-      cps[nh] = dead_child;
-      if (!wait_hnd[nh]) emacs_abort ();
-      nh++;
-      active = 0;
-      goto get_result;
+      int our_child = 0;
+
+      /* We are requested to wait for a specific child.  */
+      for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--)
+       {
+         /* Some child_procs might be sockets; ignore them.  Also
+            ignore subprocesses whose output is not yet completely
+            read.  */
+         if (CHILD_ACTIVE (cp)
+             && cp->procinfo.hProcess
+             && cp->pid == pid)
+           {
+             our_child = 1;
+             break;
+           }
+       }
+      if (our_child)
+       {
+         if (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0)
+           {
+             wait_hnd[nh] = cp->procinfo.hProcess;
+             cps[nh] = cp;
+             nh++;
+           }
+         else if (dont_wait)
+           {
+             /* PID specifies our subprocess, but its status is not
+                yet available.  */
+             return 0;
+           }
+       }
+      if (nh == 0)
+       {
+         /* No such child process, or nothing to wait for, so fail.  */
+         errno = ECHILD;
+         return -1;
+       }
     }
   else
     {
       for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--)
-       /* some child_procs might be sockets; ignore them */
-       if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess
-           && (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0))
-         {
-           wait_hnd[nh] = cp->procinfo.hProcess;
-           cps[nh] = cp;
-           nh++;
-         }
+       {
+         if (CHILD_ACTIVE (cp)
+             && cp->procinfo.hProcess
+             && (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0))
+           {
+             wait_hnd[nh] = cp->procinfo.hProcess;
+             cps[nh] = cp;
+             nh++;
+           }
+       }
+      if (nh == 0)
+       {
+         /* Nothing to wait on, so fail.  */
+         errno = ECHILD;
+         return -1;
+       }
     }
 
-  if (nh == 0)
-    {
-      /* Nothing to wait on, so fail */
-      errno = ECHILD;
-      return -1;
-    }
+  if (dont_wait)
+    timeout_ms = 0;
+  else
+    timeout_ms = 1000; /* check for quit about once a second. */
 
   do
     {
-      /* Check for quit about once a second. */
       QUIT;
-      active = WaitForMultipleObjects (nh, wait_hnd, FALSE, 1000);
-    } while (active == WAIT_TIMEOUT);
+      active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
+    } while (active == WAIT_TIMEOUT && !dont_wait);
 
   if (active == WAIT_FAILED)
     {
       errno = EBADF;
       return -1;
     }
+  else if (active == WAIT_TIMEOUT && dont_wait)
+    {
+      /* PID specifies our subprocess, but it didn't exit yet, so its
+        status is not yet available.  */
+#ifdef FULL_DEBUG
+      DebPrint (("Wait: PID %d not reap yet\n", cp->pid));
+#endif
+      return 0;
+    }
   else if (active >= WAIT_OBJECT_0
           && active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
     {
@@ -1244,7 +1380,6 @@ sys_wait (int *status)
   else
     emacs_abort ();
 
-get_result:
   if (!GetExitCodeProcess (wait_hnd[active], &retval))
     {
       DebPrint (("Wait.GetExitCodeProcess failed with %lu\n",
@@ -1253,8 +1388,10 @@ get_result:
     }
   if (retval == STILL_ACTIVE)
     {
-      /* Should never happen */
+      /* Should never happen */
       DebPrint (("Wait.WaitForMultipleObjects returned an active process\n"));
+      if (pid > 0 && dont_wait)
+       return 0;
       errno = EINVAL;
       return -1;
     }
@@ -1268,6 +1405,8 @@ get_result:
   else
     retval <<= 8;
 
+  if (pid > 0 && active != 0)
+    emacs_abort ();
   cp = cps[active];
   pid = cp->pid;
 #ifdef FULL_DEBUG
@@ -1275,33 +1414,7 @@ get_result:
 #endif
 
   if (status)
-    {
-      *status = retval;
-    }
-  else if (synch_process_alive)
-    {
-      synch_process_alive = 0;
-
-      /* Report the status of the synchronous process.  */
-      if (WIFEXITED (retval))
-       synch_process_retcode = WEXITSTATUS (retval);
-      else if (WIFSIGNALED (retval))
-       {
-         int code = WTERMSIG (retval);
-         const char *signame;
-
-         synchronize_system_messages_locale ();
-         signame = strsignal (code);
-
-         if (signame == 0)
-           signame = "unknown";
-
-         synch_process_death = signame;
-       }
-
-      reap_subprocess (cp);
-    }
-
+    *status = retval;
   reap_subprocess (cp);
 
   return pid;
@@ -1486,7 +1599,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
   Lisp_Object program, full;
   char *cmdline, *env, *parg, **targ;
   int arglen, numenv;
-  int pid;
+  pid_t pid;
   child_process *cp;
   int is_dos_app, is_cygnus_app, is_gui_app;
   int do_quoting = 0;
@@ -1816,7 +1929,7 @@ extern int proc_buffered_char[];
 
 int
 sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
-           EMACS_TIME *timeout, void *ignored)
+           struct timespec *timeout, void *ignored)
 {
   SELECT_TYPE orfds;
   DWORD timeout_ms, start_time;
@@ -1847,12 +1960,17 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
   FD_ZERO (rfds);
   nr = 0;
 
-  /* Always wait on interrupt_handle, to detect C-g (quit).  */
-  wait_hnd[0] = interrupt_handle;
-  fdindex[0] = -1;
+  /* If interrupt_handle is available and valid, always wait on it, to
+     detect C-g (quit).  */
+  nh = 0;
+  if (interrupt_handle && interrupt_handle != INVALID_HANDLE_VALUE)
+    {
+      wait_hnd[0] = interrupt_handle;
+      fdindex[0] = -1;
+      nh++;
+    }
 
   /* Build a list of pipe handles to wait on.  */
-  nh = 1;
   for (i = 0; i < nfds; i++)
     if (FD_ISSET (i, &orfds))
       {
@@ -1873,10 +1991,15 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
                FD_SET (i, rfds);
                return 1;
              }
+           else if (noninteractive)
+             {
+               if (handle_file_notifications (NULL))
+                 return 1;
+             }
          }
        else
          {
-           /* Child process and socket input */
+           /* Child process and socket/comm port input.  */
            cp = fd_info[i].cp;
            if (cp)
              {
@@ -1889,7 +2012,7 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
                    /* Wake up the reader thread for this process */
                    cp->status = STATUS_READ_READY;
                    if (!SetEvent (cp->char_consumed))
-                     DebPrint (("nt_select.SetEvent failed with "
+                     DebPrint (("sys_select.SetEvent failed with "
                                 "%lu for fd %ld\n", GetLastError (), i));
                  }
 
@@ -1957,7 +2080,7 @@ count_children:
     /* Some child_procs might be sockets; ignore them.  Also some
        children may have died already, but we haven't finished reading
        the process output; ignore them too.  */
-    if ((CHILD_ACTIVE (cp) || cp->procinfo.hProcess)
+    if ((CHILD_ACTIVE (cp) && cp->procinfo.hProcess)
        && (cp->fd < 0
            || (fd_info[cp->fd].flags & FILE_SEND_SIGCHLD) == 0
            || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0)
@@ -1973,6 +2096,11 @@ count_children:
     {
       if (timeout)
        Sleep (timeout_ms);
+      if (noninteractive)
+       {
+         if (handle_file_notifications (NULL))
+           return 1;
+       }
       return 0;
     }
 
@@ -1999,6 +2127,11 @@ count_children:
     }
   else if (active == WAIT_TIMEOUT)
     {
+      if (noninteractive)
+       {
+         if (handle_file_notifications (NULL))
+           return 1;
+       }
       return 0;
     }
   else if (active >= WAIT_OBJECT_0
@@ -2037,7 +2170,24 @@ count_children:
             (*) Note that MsgWaitForMultipleObjects above is an
             internal dispatch point for messages that are sent to
             windows created by this thread.  */
-         drain_message_queue ();
+         if (drain_message_queue ()
+             /* If drain_message_queue returns non-zero, that means
+                we received a WM_EMACS_FILENOTIFY message.  If this
+                is a TTY frame, we must signal the caller that keyboard
+                input is available, so that w32_console_read_socket
+                will be called to pick up the notifications.  If we
+                don't do that, file notifications will only work when
+                the Emacs TTY frame has focus.  */
+             && FRAME_TERMCAP_P (SELECTED_FRAME ())
+             /* they asked for stdin reads */
+             && FD_ISSET (0, &orfds)
+             /* the stdin handle is valid */
+             && keyboard_handle)
+           {
+             FD_SET (0, rfds);
+             if (nr == 0)
+               nr = 1;
+           }
        }
       else if (active >= nh)
        {
@@ -2057,9 +2207,7 @@ count_children:
              DebPrint (("select calling SIGCHLD handler for pid %d\n",
                         cp->pid));
 #endif
-             dead_child = cp;
              sig_handlers[SIGCHLD] (SIGCHLD);
-             dead_child = NULL;
            }
        }
       else if (fdindex[active] == -1)
@@ -2090,6 +2238,12 @@ count_children:
          break;
     } while (active < nh + nc);
 
+  if (noninteractive)
+    {
+      if (handle_file_notifications (NULL))
+       nr++;
+    }
+
   /* If no input has arrived and timeout hasn't expired, wait again.  */
   if (nr == 0)
     {
@@ -2136,20 +2290,54 @@ find_child_console (HWND hwnd, LPARAM arg)
 
 /* Emulate 'kill', but only for other processes.  */
 int
-sys_kill (int pid, int sig)
+sys_kill (pid_t pid, int sig)
 {
   child_process *cp;
   HANDLE proc_hand;
   int need_to_free = 0;
   int rc = 0;
 
+  /* Each process is in its own process group.  */
+  if (pid < 0)
+    pid = -pid;
+
   /* Only handle signals that will result in the process dying */
-  if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
+  if (sig != 0
+      && sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
     {
       errno = EINVAL;
       return -1;
     }
 
+  if (sig == 0)
+    {
+      /* It will take _some_ time before PID 4 or less on Windows will
+        be Emacs...  */
+      if (pid <= 4)
+       {
+         errno = EPERM;
+         return -1;
+       }
+      proc_hand = OpenProcess (PROCESS_QUERY_INFORMATION, 0, pid);
+      if (proc_hand == NULL)
+        {
+         DWORD err = GetLastError ();
+
+         switch (err)
+           {
+           case ERROR_ACCESS_DENIED: /* existing process, but access denied */
+             errno = EPERM;
+             return -1;
+           case ERROR_INVALID_PARAMETER: /* process PID does not exist */
+             errno = ESRCH;
+             return -1;
+           }
+       }
+      else
+       CloseHandle (proc_hand);
+      return 0;
+    }
+
   cp = find_child_pid (pid);
   if (cp == NULL)
     {
@@ -2488,8 +2676,9 @@ All path elements in FILENAME are converted to their short names.  */)
   if (GetShortPathName (SDATA (ENCODE_FILE (filename)), shortname, MAX_PATH) == 0)
     return Qnil;
 
-  dostounix_filename (shortname);
+  dostounix_filename (shortname, 0);
 
+  /* No need to DECODE_FILE, because 8.3 names are pure ASCII.   */
   return build_string (shortname);
 }
 
@@ -2516,7 +2705,7 @@ All path elements in FILENAME are converted to their long names.  */)
   if (!w32_get_long_filename (SDATA (ENCODE_FILE (filename)), longname, MAX_PATH))
     return Qnil;
 
-  dostounix_filename (longname);
+  dostounix_filename (longname, 0);
 
   /* If we were passed only a drive, make sure that a slash is not appended
      for consistency with directories.  Allow for drive mapping via SUBST