Imported Upstream version 4.0
[platform/upstream/make.git] / w32 / subproc / sub_proc.c
index dcb77bf..f790ca3 100644 (file)
@@ -1,6 +1,5 @@
 /* Process handling for Windows.
-Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
-2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+Copyright (C) 1996-2013 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
 GNU Make is free software; you can redistribute it and/or modify it under the
@@ -18,22 +17,24 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <config.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <io.h>                /* for _get_osfhandle */
 #ifdef _MSC_VER
 # include <stddef.h>    /* for intptr_t */
 #else
 # include <stdint.h>
 #endif
+#include <string.h>
 #include <process.h>  /* for msvc _beginthreadex, _endthreadex */
 #include <signal.h>
 #include <windows.h>
 
+#include "makeint.h"
 #include "sub_proc.h"
 #include "proc.h"
 #include "w32err.h"
 #include "debug.h"
 
 static char *make_command_line(char *shell_name, char *exec_path, char **argv);
-extern char *xmalloc (unsigned int);
 
 typedef struct sub_process_t {
        intptr_t sv_stdin[2];
@@ -58,6 +59,126 @@ static sub_process *proc_array[MAXIMUM_WAIT_OBJECTS];
 static int proc_index = 0;
 static int fake_exits_pending = 0;
 
+/* Windows jobserver implementation variables */
+static char jobserver_semaphore_name[MAX_PATH + 1];
+static HANDLE jobserver_semaphore = NULL;
+
+/* Open existing jobserver semaphore */
+int open_jobserver_semaphore(const char* name)
+{
+    jobserver_semaphore = OpenSemaphore(
+        SEMAPHORE_ALL_ACCESS,  // Semaphore access setting
+        FALSE,                 // Child processes DON'T inherit
+        name);                 // Semaphore name
+
+    if (jobserver_semaphore == NULL)
+        return 0;
+
+    return 1;        
+}
+
+/* Create new jobserver semaphore */
+int create_jobserver_semaphore(int tokens)
+{
+    sprintf(jobserver_semaphore_name, "gmake_semaphore_%d", _getpid());
+
+    jobserver_semaphore = CreateSemaphore(
+        NULL,                          // Use default security descriptor
+        tokens,                                // Initial count
+        tokens,                        // Maximum count
+        jobserver_semaphore_name);     // Semaphore name
+
+    if (jobserver_semaphore == NULL)
+        return 0;
+
+    return 1;        
+}
+
+/* Close jobserver semaphore */
+void free_jobserver_semaphore()
+{
+    if (jobserver_semaphore != NULL)
+    {
+        CloseHandle(jobserver_semaphore);
+        jobserver_semaphore = NULL;
+    }
+}
+
+/* Decrement semaphore count */
+int acquire_jobserver_semaphore()
+{
+    DWORD dwEvent = WaitForSingleObject(
+        jobserver_semaphore,   // Handle to semaphore
+        0);                    // DON'T wait on semaphore
+
+    return (dwEvent == WAIT_OBJECT_0);
+}
+
+/* Increment semaphore count */
+int release_jobserver_semaphore()
+{
+    BOOL bResult = ReleaseSemaphore( 
+        jobserver_semaphore,   // handle to semaphore
+        1,                     // increase count by one
+        NULL);                 // not interested in previous count
+
+    return (bResult);
+}
+
+int has_jobserver_semaphore()
+{
+    return (jobserver_semaphore != NULL);
+}
+
+char* get_jobserver_semaphore_name()
+{
+    return (jobserver_semaphore_name);
+}
+
+/* Wait for either the jobserver semaphore to become signalled or one of our
+ * child processes to terminate.
+ */
+int wait_for_semaphore_or_child_process()
+{
+    HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+    DWORD dwHandleCount = 1;
+    DWORD dwEvent;
+    int i;
+
+    /* Add jobserver semaphore to first slot. */
+    handles[0] = jobserver_semaphore;
+
+    /* Build array of handles to wait for */
+    for (i = 0; i < proc_index; i++) 
+    {
+        /* Don't wait on child processes that have already finished */
+        if (fake_exits_pending && proc_array[i]->exit_code)
+            continue;
+
+        handles[dwHandleCount++] = (HANDLE) proc_array[i]->pid;
+    }
+
+    dwEvent = WaitForMultipleObjects( 
+        dwHandleCount, // number of objects in array
+        handles,       // array of objects
+        FALSE,         // wait for any object
+        INFINITE);     // wait until object is signalled
+
+    switch(dwEvent)
+    {
+      case WAIT_FAILED:
+        return -1;
+
+      case WAIT_OBJECT_0:
+        /* Indicate that the semaphore was signalled */
+        return 1;
+
+      default:
+        /* Assume that one or more of the child processes terminated. */
+        return 0;
+    }
+}
+
 /*
  * When a process has been waited for, adjust the wait state
  * array so that we don't wait for it again
@@ -87,7 +208,7 @@ process_adjust_wait_state(sub_process* pproc)
  * Waits for any of the registered child processes to finish.
  */
 static sub_process *
-process_wait_for_any_private(void)
+process_wait_for_any_private(int block, DWORD* pdwWaitStatus)
 {
        HANDLE handles[MAXIMUM_WAIT_OBJECTS];
        DWORD retval, which;
@@ -106,7 +227,7 @@ process_wait_for_any_private(void)
 
        /* wait for someone to exit */
        if (!fake_exits_pending) {
-               retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
+               retval = WaitForMultipleObjects(proc_index, handles, FALSE, (block ? INFINITE : 0));
                which = retval - WAIT_OBJECT_0;
        } else {
                fake_exits_pending--;
@@ -114,13 +235,19 @@ process_wait_for_any_private(void)
                which = i;
        }
 
+        /* If the pointer is not NULL, set the wait status result variable. */
+        if (pdwWaitStatus)
+            *pdwWaitStatus = retval;
+
        /* return pointer to process */
-       if (retval != WAIT_FAILED) {
+        if ((retval == WAIT_TIMEOUT) || (retval == WAIT_FAILED)) {
+               return NULL;
+        }
+       else {
                sub_process* pproc = proc_array[which];
                process_adjust_wait_state(pproc);
                return pproc;
-       } else
-               return NULL;
+       } 
 }
 
 /*
@@ -179,9 +306,9 @@ process_used_slots(void)
  */
 
 HANDLE
-process_wait_for_any(void)
+process_wait_for_any(int block, DWORD* pdwWaitStatus)
 {
-       sub_process* pproc = process_wait_for_any_private();
+       sub_process* pproc = process_wait_for_any_private(block, pdwWaitStatus);
 
        if (!pproc)
                return NULL;
@@ -216,6 +343,15 @@ process_exit_code(HANDLE proc)
        return (((sub_process *)proc)->exit_code);
 }
 
+void
+process_noinherit(int fd)
+{
+  HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+  if (fh && fh != INVALID_HANDLE_VALUE)
+       SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
+}
+
 /*
 2006-02:
 All the following functions are currently unused.
@@ -336,17 +472,19 @@ process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
        sub_process *pproc;
 
        pproc = malloc(sizeof(*pproc));
-       memset(pproc, 0, sizeof(*pproc));
+       if (pproc) {
+               memset(pproc, 0, sizeof(*pproc));
 
-       /*
-        * Just pass the provided file handles to the 'child side' of the
-        * pipe, bypassing pipes altogether.
-        */
-       pproc->sv_stdin[1]  = (intptr_t) stdinh;
-       pproc->sv_stdout[1] = (intptr_t) stdouth;
-       pproc->sv_stderr[1] = (intptr_t) stderrh;
+               /*
+                * Just pass the provided file handles to the 'child
+                * side' of the pipe, bypassing pipes altogether.
+                */
+               pproc->sv_stdin[1]  = (intptr_t) stdinh;
+               pproc->sv_stdout[1] = (intptr_t) stdouth;
+               pproc->sv_stderr[1] = (intptr_t) stderrh;
 
-       pproc->last_err = pproc->lerrno = 0;
+               pproc->last_err = pproc->lerrno = 0;
+       }
 
        return((HANDLE)pproc);
 }
@@ -403,6 +541,26 @@ find_file(const char *exec_path, const char *path_var,
        return INVALID_HANDLE_VALUE;
 }
 
+/*
+ * Return non-zero of FNAME specifies a batch file and its name
+ * includes embedded whitespace.
+ */
+
+static int
+batch_file_with_spaces(const char *fname)
+{
+       size_t fnlen = strlen(fname);
+
+       return (fnlen > 4
+               && (_strnicmp(fname + fnlen - 4, ".bat", 4) == 0
+                   || _strnicmp(fname + fnlen - 4, ".cmd", 4) == 0)
+               /* The set of characters in the 2nd arg to strpbrk
+                  should be the same one used by make_command_line
+                  below to decide whether an argv[] element needs
+                  quoting.  */
+               && strpbrk(fname, " \t") != NULL);
+}
+
 
 /*
  * Description:   Create the child process to be helped
@@ -433,6 +591,7 @@ process_begin(
        STARTUPINFO startInfo;
        PROCESS_INFORMATION procInfo;
        char *envblk=NULL;
+       int pass_null_exec_path = 0;
 
        /*
         *  Shell script detection...  if the exec_path starts with #! then
@@ -514,8 +673,28 @@ process_begin(
 
        if (file_not_found)
                command_line = make_command_line( shell_name, exec_path, argv);
-       else
+       else {
+               /* If exec_fname includes whitespace, CreateProcess
+                  behaves erratically and unreliably, and often fails
+                  if argv[0] also includes whitespace (and thus will
+                  be quoted by make_command_line below).  So in that
+                  case, we don't pass exec_fname as the 1st arg to
+                  CreateProcess, but instead replace argv[0] with
+                  exec_fname (to keep its leading directories and
+                  extension as found by find_file), and pass NULL to
+                  CreateProcess as its 1st arg.  This works around
+                  the bugs in CreateProcess, which are probably
+                  caused by its passing the command to cmd.exe with
+                  some incorrect quoting.  */
+               if (!shell_name
+                   && batch_file_with_spaces(exec_fname)
+                   && _stricmp(exec_path, argv[0]) == 0) {
+                       pass_null_exec_path = 1;
+                       free (argv[0]);
+                       argv[0] = xstrdup(exec_fname);
+               }
                command_line = make_command_line( shell_name, exec_fname, argv);
+       }
 
        if ( command_line == NULL ) {
                pproc->last_err = 0;
@@ -532,7 +711,7 @@ process_begin(
                }
        }
 
-       if ((shell_name) || (file_not_found)) {
+       if (shell_name || file_not_found || pass_null_exec_path) {
                exec_path = 0;  /* Search for the program in %Path% */
        } else {
                exec_path = exec_fname;
@@ -585,9 +764,12 @@ process_begin(
        CloseHandle(procInfo.hThread);
 
        /* Close the halves of the pipes we don't need */
-        CloseHandle((HANDLE)pproc->sv_stdin[1]);
-        CloseHandle((HANDLE)pproc->sv_stdout[1]);
-        CloseHandle((HANDLE)pproc->sv_stderr[1]);
+       if ((HANDLE)pproc->sv_stdin[1] != INVALID_HANDLE_VALUE)
+         CloseHandle((HANDLE)pproc->sv_stdin[1]);
+       if ((HANDLE)pproc->sv_stdout[1] != INVALID_HANDLE_VALUE)
+         CloseHandle((HANDLE)pproc->sv_stdout[1]);
+       if ((HANDLE)pproc->sv_stderr[1] != INVALID_HANDLE_VALUE)
+         CloseHandle((HANDLE)pproc->sv_stderr[1]);
         pproc->sv_stdin[1] = 0;
         pproc->sv_stdout[1] = 0;
         pproc->sv_stderr[1] = 0;
@@ -600,6 +782,7 @@ process_begin(
 
 
 
+#if 0  /* unused */
 static DWORD
 proc_stdin_thread(sub_process *pproc)
 {
@@ -844,6 +1027,7 @@ process_pipe_io(
                return(0);
 
 }
+#endif /* unused */
 
 /*
  * Purpose: collects output from child process and returns results
@@ -865,7 +1049,7 @@ process_file_io(
         DWORD ierr;
 
        if (proc == NULL)
-               pproc = process_wait_for_any_private();
+               pproc = process_wait_for_any_private(1, 0);
        else
                pproc = (sub_process *)proc;
 
@@ -927,7 +1111,7 @@ done2:
 
 /*
  * Description:  Clean up any leftover handles, etc.  It is up to the
- * caller to manage and free the input, ouput, and stderr buffers.
+ * caller to manage and free the input, output, and stderr buffers.
  */
        void
 process_cleanup(
@@ -938,11 +1122,14 @@ process_cleanup(
 
        if (pproc->using_pipes) {
                for (i= 0; i <= 1; i++) {
-                       if ((HANDLE)pproc->sv_stdin[i])
+                       if ((HANDLE)pproc->sv_stdin[i]
+                           && (HANDLE)pproc->sv_stdin[i] != INVALID_HANDLE_VALUE)
                                CloseHandle((HANDLE)pproc->sv_stdin[i]);
-                       if ((HANDLE)pproc->sv_stdout[i])
+                       if ((HANDLE)pproc->sv_stdout[i]
+                           && (HANDLE)pproc->sv_stdout[i] != INVALID_HANDLE_VALUE)
                                CloseHandle((HANDLE)pproc->sv_stdout[i]);
-                       if ((HANDLE)pproc->sv_stderr[i])
+                       if ((HANDLE)pproc->sv_stderr[i]
+                           && (HANDLE)pproc->sv_stderr[i] != INVALID_HANDLE_VALUE)
                                CloseHandle((HANDLE)pproc->sv_stderr[i]);
                }
        }
@@ -1205,52 +1392,110 @@ make_command_line( char *shell_name, char *full_exec_path, char **argv)
 HANDLE
 process_easy(
        char **argv,
-       char **envp)
+       char **envp,
+       int outfd,
+       int errfd)
 {
-  HANDLE hIn;
-  HANDLE hOut;
-  HANDLE hErr;
-  HANDLE hProcess;
+  HANDLE hIn = INVALID_HANDLE_VALUE;
+  HANDLE hOut = INVALID_HANDLE_VALUE;
+  HANDLE hErr = INVALID_HANDLE_VALUE;
+  HANDLE hProcess, tmpIn, tmpOut, tmpErr;
+  DWORD e;
 
   if (proc_index >= MAXIMUM_WAIT_OBJECTS) {
        DB (DB_JOBS, ("process_easy: All process slots used up\n"));
        return INVALID_HANDLE_VALUE;
   }
+  /* Standard handles returned by GetStdHandle can be NULL or
+     INVALID_HANDLE_VALUE if the parent process closed them.  If that
+     happens, we open the null device and pass its handle to
+     CreateProcess as the corresponding handle to inherit.  */
+  tmpIn = GetStdHandle(STD_INPUT_HANDLE);
   if (DuplicateHandle(GetCurrentProcess(),
-                      GetStdHandle(STD_INPUT_HANDLE),
-                      GetCurrentProcess(),
-                      &hIn,
-                      0,
-                      TRUE,
-                      DUPLICATE_SAME_ACCESS) == FALSE) {
-    fprintf(stderr,
-            "process_easy: DuplicateHandle(In) failed (e=%ld)\n",
-            GetLastError());
-    return INVALID_HANDLE_VALUE;
+                     tmpIn,
+                     GetCurrentProcess(),
+                     &hIn,
+                     0,
+                     TRUE,
+                     DUPLICATE_SAME_ACCESS) == FALSE) {
+    if ((e = GetLastError()) == ERROR_INVALID_HANDLE) {
+      tmpIn = CreateFile("NUL", GENERIC_READ,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (tmpIn != INVALID_HANDLE_VALUE
+         && DuplicateHandle(GetCurrentProcess(),
+                            tmpIn,
+                            GetCurrentProcess(),
+                            &hIn,
+                            0,
+                            TRUE,
+                            DUPLICATE_SAME_ACCESS) == FALSE)
+       CloseHandle(tmpIn);
+    }
+    if (hIn == INVALID_HANDLE_VALUE) {
+      fprintf(stderr, "process_easy: DuplicateHandle(In) failed (e=%ld)\n", e);
+      return INVALID_HANDLE_VALUE;
+    }
   }
+  if (outfd >= 0)
+    tmpOut = (HANDLE)_get_osfhandle (outfd);
+  else
+    tmpOut = GetStdHandle (STD_OUTPUT_HANDLE);
   if (DuplicateHandle(GetCurrentProcess(),
-                      GetStdHandle(STD_OUTPUT_HANDLE),
-                      GetCurrentProcess(),
-                      &hOut,
-                      0,
-                      TRUE,
-                      DUPLICATE_SAME_ACCESS) == FALSE) {
-    fprintf(stderr,
-           "process_easy: DuplicateHandle(Out) failed (e=%ld)\n",
-           GetLastError());
-    return INVALID_HANDLE_VALUE;
+                     tmpOut,
+                     GetCurrentProcess(),
+                     &hOut,
+                     0,
+                     TRUE,
+                     DUPLICATE_SAME_ACCESS) == FALSE) {
+    if ((e = GetLastError()) == ERROR_INVALID_HANDLE) {
+      tmpOut = CreateFile("NUL", GENERIC_WRITE,
+                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (tmpOut != INVALID_HANDLE_VALUE
+         && DuplicateHandle(GetCurrentProcess(),
+                            tmpOut,
+                            GetCurrentProcess(),
+                            &hOut,
+                            0,
+                            TRUE,
+                            DUPLICATE_SAME_ACCESS) == FALSE)
+       CloseHandle(tmpOut);
+    }
+    if (hOut == INVALID_HANDLE_VALUE) {
+      fprintf(stderr, "process_easy: DuplicateHandle(Out) failed (e=%ld)\n", e);
+      return INVALID_HANDLE_VALUE;
+    }
   }
+  if (errfd >= 0)
+    tmpErr = (HANDLE)_get_osfhandle (errfd);
+  else
+    tmpErr = GetStdHandle(STD_ERROR_HANDLE);
   if (DuplicateHandle(GetCurrentProcess(),
-                      GetStdHandle(STD_ERROR_HANDLE),
-                      GetCurrentProcess(),
-                      &hErr,
-                      0,
-                      TRUE,
-                      DUPLICATE_SAME_ACCESS) == FALSE) {
-    fprintf(stderr,
-            "process_easy: DuplicateHandle(Err) failed (e=%ld)\n",
-            GetLastError());
-    return INVALID_HANDLE_VALUE;
+                     tmpErr,
+                     GetCurrentProcess(),
+                     &hErr,
+                     0,
+                     TRUE,
+                     DUPLICATE_SAME_ACCESS) == FALSE) {
+    if ((e = GetLastError()) == ERROR_INVALID_HANDLE) {
+      tmpErr = CreateFile("NUL", GENERIC_WRITE,
+                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (tmpErr != INVALID_HANDLE_VALUE
+         && DuplicateHandle(GetCurrentProcess(),
+                            tmpErr,
+                            GetCurrentProcess(),
+                            &hErr,
+                            0,
+                            TRUE,
+                            DUPLICATE_SAME_ACCESS) == FALSE)
+       CloseHandle(tmpErr);
+    }
+    if (hErr == INVALID_HANDLE_VALUE) {
+      fprintf(stderr, "process_easy: DuplicateHandle(Err) failed (e=%ld)\n", e);
+      return INVALID_HANDLE_VALUE;
+    }
   }
 
   hProcess = process_init_fd(hIn, hOut, hErr);
@@ -1263,9 +1508,12 @@ process_easy(
     ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
 
     /* close up unused handles */
-    CloseHandle(hIn);
-    CloseHandle(hOut);
-    CloseHandle(hErr);
+    if (hIn != INVALID_HANDLE_VALUE)
+      CloseHandle(hIn);
+    if (hOut != INVALID_HANDLE_VALUE)
+      CloseHandle(hOut);
+    if (hErr != INVALID_HANDLE_VALUE)
+      CloseHandle(hErr);
   }
 
   process_register(hProcess);