(root)/
make-4.4/
src/
w32/
w32os.c
       1  /* Windows32-based operating system interface for GNU Make.
       2  Copyright (C) 2016-2022 Free Software Foundation, Inc.
       3  This file is part of GNU Make.
       4  
       5  GNU Make is free software; you can redistribute it and/or modify it under the
       6  terms of the GNU General Public License as published by the Free Software
       7  Foundation; either version 3 of the License, or (at your option) any later
       8  version.
       9  
      10  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      11  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      13  
      14  You should have received a copy of the GNU General Public License along with
      15  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #include "makeint.h"
      18  
      19  #include <stdio.h>
      20  #include <string.h>
      21  
      22  #include <windows.h>
      23  #include <process.h>
      24  #include <io.h>
      25  #if _WIN32_WINNT > 0x0601
      26  #include <synchapi.h>
      27  #endif
      28  #include "pathstuff.h"
      29  #include "sub_proc.h"
      30  #include "w32err.h"
      31  #include "os.h"
      32  #include "debug.h"
      33  
      34  unsigned int
      35  check_io_state ()
      36  {
      37    static unsigned int state = IO_UNKNOWN;
      38  
      39    /* We only need to compute this once per process.  */
      40    if (state != IO_UNKNOWN)
      41      return state;
      42  
      43    /* Could have used GetHandleInformation, but that isn't supported
      44       on Windows 9X.  */
      45    HANDLE outfd = (HANDLE)_get_osfhandle (fileno (stdout));
      46    HANDLE errfd = (HANDLE)_get_osfhandle (fileno (stderr));
      47  
      48    if ((HANDLE)_get_osfhandle (fileno (stdin)) != INVALID_HANDLE_VALUE)
      49      state |= IO_STDIN_OK;
      50    if (outfd != INVALID_HANDLE_VALUE)
      51      state |= IO_STDOUT_OK;
      52    if (errfd != INVALID_HANDLE_VALUE)
      53      state |= IO_STDERR_OK;
      54  
      55    if (ALL_SET (state, IO_STDOUT_OK|IO_STDERR_OK))
      56      {
      57        unsigned int combined = 0;
      58  
      59        if (outfd == errfd)
      60          combined = IO_COMBINED_OUTERR;
      61        else
      62          {
      63            DWORD outtype = GetFileType (outfd), errtype = GetFileType (errfd);
      64  
      65            if (outtype == errtype
      66                && outtype != FILE_TYPE_UNKNOWN && errtype != FILE_TYPE_UNKNOWN)
      67              {
      68                if (outtype == FILE_TYPE_CHAR)
      69                  {
      70                    /* For character devices, check if they both refer to a
      71                       console.  This loses if both handles refer to the
      72                       null device (FIXME!), but in that case we don't care
      73                       in the context of Make.  */
      74                    DWORD outmode, errmode;
      75  
      76                    /* Each process on Windows can have at most 1 console,
      77                       so if both handles are for the console device, they
      78                       are the same.  We also compare the console mode to
      79                       distinguish between stdin and stdout/stderr.  */
      80                    if (GetConsoleMode (outfd, &outmode)
      81                        && GetConsoleMode (errfd, &errmode)
      82                        && outmode == errmode)
      83                      combined = IO_COMBINED_OUTERR;
      84                  }
      85                else
      86                  {
      87                    /* For disk files and pipes, compare their unique
      88                       attributes.  */
      89                    BY_HANDLE_FILE_INFORMATION outfi, errfi;
      90  
      91                    /* Pipes get zero in the volume serial number, but do
      92                       appear to have meaningful information in file index
      93                       attributes.  We test file attributes as well, for a
      94                       good measure.  */
      95                    if (GetFileInformationByHandle (outfd, &outfi)
      96                        && GetFileInformationByHandle (errfd, &errfi)
      97                        && outfi.dwVolumeSerialNumber == errfi.dwVolumeSerialNumber
      98                        && outfi.nFileIndexLow == errfi.nFileIndexLow
      99                        && outfi.nFileIndexHigh == errfi.nFileIndexHigh
     100                        && outfi.dwFileAttributes == errfi.dwFileAttributes)
     101                      combined = IO_COMBINED_OUTERR;
     102                  }
     103              }
     104          }
     105        state |= combined;
     106      }
     107  
     108    return state;
     109  }
     110  
     111  /* A replacement for tmpfile, since the MSVCRT implementation creates
     112     the file in the root directory of the current drive, which might
     113     not be writable by our user, and also it returns a FILE* and we want a file
     114     descriptor.  Mostly borrowed from create_batch_file, see job.c.  */
     115  int
     116  os_anontmp ()
     117  {
     118    char temp_path[MAX_PATH+1];
     119    unsigned path_size = GetTempPath (sizeof (temp_path), temp_path);
     120    int using_cwd = 0;
     121  
     122    /* These variables are static so we won't try to reuse a name that was
     123       generated a little while ago, because that file might not be on disk yet,
     124       since we use FILE_ATTRIBUTE_TEMPORARY below, which tells the OS it
     125       doesn't need to flush the cache to disk.  If the file is not yet on disk,
     126       we might think the name is available, while it really isn't.  This
     127       happens in parallel builds.  */
     128    static unsigned uniq = 0;
     129    static int second_loop = 0;
     130  
     131    const char base[] = "gmake_tmpf";
     132    const unsigned sizemax = sizeof (base) - 1 + 4 + 10 + 10;
     133    unsigned pid = GetCurrentProcessId ();
     134  
     135    if (path_size == 0)
     136      {
     137        path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
     138        using_cwd = 1;
     139      }
     140  
     141    ++uniq;
     142    if (uniq >= 0x10000 && !second_loop)
     143      {
     144        /* If we already had 64K batch files in this
     145           process, make a second loop through the numbers,
     146           looking for free slots, i.e. files that were
     147           deleted in the meantime.  */
     148        second_loop = 1;
     149        uniq = 1;
     150      }
     151  
     152    while (path_size > 0 && path_size + sizemax < sizeof (temp_path)
     153           && (uniq < 0x10000 || !second_loop))
     154      {
     155        HANDLE h;
     156  
     157        sprintf (temp_path + path_size,
     158                 "%s%s%u-%x.tmp",
     159                 temp_path[path_size - 1] == '\\' ? "" : "\\",
     160                 base, pid, uniq);
     161        h = CreateFile (temp_path,  /* file name */
     162                        GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */
     163                        FILE_SHARE_READ | FILE_SHARE_WRITE,    /* share mode */
     164                        NULL,                                  /* default security attributes */
     165                        CREATE_NEW,                            /* creation disposition */
     166                        FILE_ATTRIBUTE_NORMAL |                /* flags and attributes */
     167                        FILE_ATTRIBUTE_TEMPORARY |
     168                        FILE_FLAG_DELETE_ON_CLOSE,
     169                        NULL);                                 /* no template file */
     170  
     171        if (h != INVALID_HANDLE_VALUE)
     172          return _open_osfhandle ((intptr_t)h, 0);
     173  
     174        {
     175          const DWORD er = GetLastError ();
     176  
     177          if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
     178            {
     179              ++uniq;
     180              if (uniq == 0x10000 && !second_loop)
     181                {
     182                  second_loop = 1;
     183                  uniq = 1;
     184                }
     185            }
     186          /* The temporary path is not guaranteed to exist, or might not be
     187             writable by user.  Use the current directory as fallback.  */
     188          else if (!using_cwd)
     189            {
     190              path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
     191              using_cwd = 1;
     192            }
     193          else
     194            {
     195              errno = EACCES;
     196              return -1;
     197            }
     198        }
     199      }
     200  
     201    if (uniq >= 0x10000)
     202      errno = EEXIST;
     203    return -1;
     204  }
     205  
     206  #if defined(MAKE_JOBSERVER)
     207  
     208  /* This section provides OS-specific functions to support the jobserver.  */
     209  
     210  static char jobserver_semaphore_name[MAX_PATH + 1];
     211  static HANDLE jobserver_semaphore = NULL;
     212  
     213  unsigned int
     214  jobserver_setup (int slots, const char *style)
     215  {
     216    /* sub_proc.c is limited in the number of objects it can wait for. */
     217  
     218    if (style && strcmp (style, "sem") != 0)
     219      OS (fatal, NILF, _("Unknown jobserver auth style '%s'"), style);
     220  
     221    if (slots > process_table_usable_size())
     222      {
     223        slots = process_table_usable_size();
     224        DB (DB_JOBS, (_("Jobserver slots limited to %d\n"), slots));
     225      }
     226  
     227    sprintf (jobserver_semaphore_name, "gmake_semaphore_%d", _getpid ());
     228  
     229    jobserver_semaphore = CreateSemaphore (
     230        NULL,                           /* Use default security descriptor */
     231        slots,                          /* Initial count */
     232        slots,                          /* Maximum count */
     233        jobserver_semaphore_name);      /* Semaphore name */
     234  
     235    if (jobserver_semaphore == NULL)
     236      {
     237        DWORD err = GetLastError ();
     238        const char *estr = map_windows32_error_to_string (err);
     239        ONS (fatal, NILF,
     240             _("creating jobserver semaphore: (Error %ld: %s)"), err, estr);
     241      }
     242  
     243    return 1;
     244  }
     245  
     246  unsigned int
     247  jobserver_parse_auth (const char *auth)
     248  {
     249    jobserver_semaphore = OpenSemaphore (
     250        SEMAPHORE_ALL_ACCESS,   /* Semaphore access setting */
     251        FALSE,                  /* Child processes DON'T inherit */
     252        auth);                  /* Semaphore name */
     253  
     254    if (jobserver_semaphore == NULL)
     255      {
     256        DWORD err = GetLastError ();
     257        const char *estr = map_windows32_error_to_string (err);
     258        fatal (NILF, strlen (auth) + INTSTR_LENGTH + strlen (estr),
     259               _("internal error: unable to open jobserver semaphore '%s': (Error %ld: %s)"),
     260               auth, err, estr);
     261      }
     262    DB (DB_JOBS, (_("Jobserver client (semaphore %s)\n"), auth));
     263  
     264    return 1;
     265  }
     266  
     267  char *
     268  jobserver_get_auth ()
     269  {
     270    return xstrdup (jobserver_semaphore_name);
     271  }
     272  
     273  const char *
     274  jobserver_get_invalid_auth ()
     275  {
     276    /* Because we're using a semaphore we don't need to invalidate.  */
     277    return NULL;
     278  }
     279  
     280  unsigned int
     281  jobserver_enabled ()
     282  {
     283    return jobserver_semaphore != NULL;
     284  }
     285  
     286  /* Close jobserver semaphore */
     287  void
     288  jobserver_clear ()
     289  {
     290    if (jobserver_semaphore != NULL)
     291      {
     292        CloseHandle (jobserver_semaphore);
     293        jobserver_semaphore = NULL;
     294      }
     295  }
     296  
     297  void
     298  jobserver_release (int is_fatal)
     299  {
     300    if (! ReleaseSemaphore (
     301            jobserver_semaphore,    /* handle to semaphore */
     302            1,                      /* increase count by one */
     303            NULL))                  /* not interested in previous count */
     304      {
     305        if (is_fatal)
     306          {
     307            DWORD err = GetLastError ();
     308            const char *estr = map_windows32_error_to_string (err);
     309            ONS (fatal, NILF,
     310                 _("release jobserver semaphore: (Error %ld: %s)"), err, estr);
     311          }
     312        perror_with_name ("release_jobserver_semaphore", "");
     313      }
     314  }
     315  
     316  unsigned int
     317  jobserver_acquire_all ()
     318  {
     319    unsigned int tokens = 0;
     320    while (1)
     321      {
     322        DWORD dwEvent = WaitForSingleObject (
     323            jobserver_semaphore,    /* Handle to semaphore */
     324            0);                     /* DON'T wait on semaphore */
     325  
     326        if (dwEvent != WAIT_OBJECT_0)
     327          return tokens;
     328  
     329        ++tokens;
     330      }
     331  }
     332  
     333  void
     334  jobserver_signal ()
     335  {
     336  }
     337  
     338  void jobserver_pre_child (int recursive)
     339  {
     340  }
     341  
     342  void jobserver_post_child (int recursive)
     343  {
     344  }
     345  
     346  void
     347  jobserver_pre_acquire ()
     348  {
     349  }
     350  
     351  /* Returns 1 if we got a token, or 0 if a child has completed.
     352     The Windows implementation doesn't support load detection.  */
     353  unsigned int
     354  jobserver_acquire (int timeout)
     355  {
     356      HANDLE *handles;
     357      DWORD dwHandleCount;
     358      DWORD dwEvent;
     359  
     360      handles = xmalloc(process_table_actual_size() * sizeof(HANDLE));
     361  
     362      /* Add jobserver semaphore to first slot. */
     363      handles[0] = jobserver_semaphore;
     364  
     365      /* Build array of handles to wait for.  */
     366      dwHandleCount = 1 + process_set_handles (&handles[1]);
     367  
     368      dwEvent = process_wait_for_multiple_objects (
     369          dwHandleCount,  /* number of objects in array */
     370          handles,        /* array of objects */
     371          FALSE,          /* wait for any object */
     372          INFINITE);      /* wait until object is signalled */
     373  
     374      free(handles);
     375  
     376      if (dwEvent == WAIT_FAILED)
     377        {
     378          DWORD err = GetLastError ();
     379          const char *estr = map_windows32_error_to_string (err);
     380          ONS (fatal, NILF,
     381               _("semaphore or child process wait: (Error %ld: %s)"),
     382               err, estr);
     383        }
     384  
     385      /* WAIT_OBJECT_0 indicates that the semaphore was signalled.  */
     386      return dwEvent == WAIT_OBJECT_0;
     387  }
     388  
     389  #endif /* MAKE_JOBSERVER */
     390  
     391  #if !defined(NO_OUTPUT_SYNC)
     392  
     393  #define MUTEX_PREFIX    "fnm:"
     394  
     395  /* Since we're using this with CreateMutex, NULL is invalid.  */
     396  static HANDLE osync_handle = NULL;
     397  
     398  unsigned int
     399  osync_enabled ()
     400  {
     401    return osync_handle != NULL;
     402  }
     403  
     404  void
     405  osync_setup ()
     406  {
     407    SECURITY_ATTRIBUTES secattr;
     408  
     409    /* We are the top-level make, and we want the handle to be inherited
     410       by our child processes.  */
     411    secattr.nLength = sizeof (secattr);
     412    secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
     413    secattr.bInheritHandle = TRUE;
     414  
     415    osync_handle = CreateMutex (&secattr, FALSE, NULL);
     416    if (!osync_handle)
     417      {
     418        DWORD err = GetLastError ();
     419        fprintf (stderr, "CreateMutex: error %lu\n", err);
     420        errno = ENOLCK;
     421      }
     422  }
     423  
     424  char *
     425  osync_get_mutex ()
     426  {
     427    char *mutex = NULL;
     428  
     429    if (osync_enabled ())
     430      {
     431        /* Prepare the mutex handle string for our children.
     432           2 hex digits per byte + 2 characters for "0x" + null.  */
     433        mutex = xmalloc ((2 * sizeof (osync_handle)) + 2 + 1);
     434        sprintf (mutex, "0x%Ix", (unsigned long long)(DWORD_PTR)osync_handle);
     435      }
     436  
     437    return mutex;
     438  }
     439  
     440  unsigned int
     441  osync_parse_mutex (const char *mutex)
     442  {
     443    char *endp;
     444    unsigned long long i;
     445  
     446    errno = 0;
     447    i = strtoull (mutex, &endp, 16);
     448    if (errno != 0)
     449      OSS (fatal, NILF, _("cannot parse output sync mutex %s: %s"),
     450           mutex, strerror (errno));
     451    if (endp[0] != '\0')
     452      OS (fatal, NILF, _("invalid output sync mutex: %s"), mutex);
     453  
     454    osync_handle = (HANDLE) (DWORD_PTR) i;
     455  
     456    return 1;
     457  }
     458  
     459  void
     460  osync_clear ()
     461  {
     462    if (osync_handle)
     463      {
     464        CloseHandle (osync_handle);
     465        osync_handle = NULL;
     466      }
     467  }
     468  
     469  unsigned int
     470  osync_acquire ()
     471  {
     472    if (osync_enabled())
     473      {
     474        DWORD result = WaitForSingleObject (osync_handle, INFINITE);
     475        if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
     476          return 0;
     477      }
     478  
     479    return 1;
     480  }
     481  
     482  void
     483  osync_release ()
     484  {
     485    if (osync_enabled())
     486      /* FIXME: Perhaps we should call ReleaseMutex repatedly until it errors
     487         out, to make sure the mutext is released even if we somehow managed to
     488         to take ownership multiple times?  */
     489      ReleaseMutex (osync_handle);
     490  }
     491  
     492  #endif /* NO_OUTPUT_SYNC */
     493  
     494  void
     495  fd_inherit(int fd)
     496  {
     497    HANDLE fh = (HANDLE)_get_osfhandle(fd);
     498  
     499    if (fh && fh != INVALID_HANDLE_VALUE)
     500          SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 1);
     501  }
     502  
     503  void
     504  fd_noinherit(int fd)
     505  {
     506    HANDLE fh = (HANDLE)_get_osfhandle(fd);
     507  
     508    if (fh && fh != INVALID_HANDLE_VALUE)
     509          SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
     510  }
     511  
     512  void
     513  fd_set_append (int fd)
     514  {}