Imported Upstream version 4.4
[platform/upstream/make.git] / 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 {}