source: trunk/src/kmk/w32/compat/posixfcn.c

Last change on this file was 3194, checked in by bird, 7 years ago

kmk/win: Windows kmk now defaults to --output-sync=target. Fixed output sync on windows in nested make processes that got busted by winchildren and it's no inheritance policy.

  • Property svn:eol-style set to native
File size: 14.6 KB
Line 
1/* Replacements for Posix functions and Posix functionality for MS-Windows.
2
3Copyright (C) 2013-2016 Free Software Foundation, Inc.
4This file is part of GNU Make.
5
6GNU Make is free software; you can redistribute it and/or modify it under the
7terms of the GNU General Public License as published by the Free Software
8Foundation; either version 3 of the License, or (at your option) any later
9version.
10
11GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16this program. If not, see <http://www.gnu.org/licenses/>. */
17
18#include <string.h>
19#include <io.h>
20#include <stdarg.h>
21#include <errno.h>
22#include <windows.h>
23
24#include "dlfcn.h"
25
26#include "makeint.h"
27#include "job.h"
28
29#ifndef NO_OUTPUT_SYNC
30/* Support for OUTPUT_SYNC and related functionality. */
31
32/* Emulation of fcntl that supports only F_GETFD and F_SETLKW. */
33int
34fcntl (intptr_t fd, int cmd, ...)
35{
36 va_list ap;
37
38 va_start (ap, cmd);
39
40 switch (cmd)
41 {
42 case F_GETFD:
43 va_end (ap);
44 /* Could have used GetHandleInformation, but that isn't
45 supported on Windows 9X. */
46 if (_get_osfhandle (fd) == -1)
47 return -1;
48 return 0;
49 case F_SETLKW:
50 {
51 void *buf = va_arg (ap, void *);
52 struct flock *fl = (struct flock *)buf;
53 HANDLE hmutex = (HANDLE)fd;
54 static struct flock last_fl;
55 short last_type = last_fl.l_type;
56
57 va_end (ap);
58
59 if (hmutex == INVALID_HANDLE_VALUE || !hmutex)
60 return -1;
61
62 last_fl = *fl;
63
64 switch (fl->l_type)
65 {
66
67 case F_WRLCK:
68 {
69 DWORD result;
70
71 if (last_type == F_WRLCK)
72 {
73 /* Don't call WaitForSingleObject if we already
74 own the mutex, because doing so will require
75 us to call ReleaseMutex an equal number of
76 times, before the mutex is actually
77 released. */
78 return 0;
79 }
80
81 result = WaitForSingleObject (hmutex, INFINITE);
82 switch (result)
83 {
84 case WAIT_OBJECT_0:
85 /* We don't care if the mutex owner crashed or
86 exited. */
87 case WAIT_ABANDONED:
88 return 0;
89 case WAIT_FAILED:
90 case WAIT_TIMEOUT: /* cannot happen, really */
91 {
92 DWORD err = GetLastError ();
93
94 /* Invalidate the last command. */
95 memset (&last_fl, 0, sizeof (last_fl));
96
97 switch (err)
98 {
99 case ERROR_INVALID_HANDLE:
100 case ERROR_INVALID_FUNCTION:
101 errno = EINVAL;
102 return -1;
103 default:
104 errno = EDEADLOCK;
105 return -1;
106 }
107 }
108 }
109 }
110 case F_UNLCK:
111 {
112 /* FIXME: Perhaps we should call ReleaseMutex
113 repatedly until it errors out, to make sure the
114 mutext is released even if we somehow managed to
115 to take ownership multiple times? */
116 BOOL status = ReleaseMutex (hmutex);
117
118 if (status)
119 return 0;
120 else
121 {
122 DWORD err = GetLastError ();
123
124 if (err == ERROR_NOT_OWNER)
125 errno = EPERM;
126 else
127 {
128 memset (&last_fl, 0, sizeof (last_fl));
129 errno = EINVAL;
130 }
131 return -1;
132 }
133 }
134 default:
135 errno = ENOSYS;
136 return -1;
137 }
138 }
139 default:
140 errno = ENOSYS;
141 va_end (ap);
142 return -1;
143 }
144}
145
146static intptr_t mutex_handle = -1;
147
148/* Record in a static variable the mutex handle we were requested to
149 use. That nameless mutex was created by the top-level Make, and
150 its handle was passed to us via inheritance. The value of that
151 handle is passed via the command-line arguments, so that we know
152 which handle to use. */
153void
154record_sync_mutex (const char *str)
155{
156#ifdef CONFIG_NEW_WIN_CHILDREN
157 HANDLE hmtx = OpenMutexA(SYNCHRONIZE, FALSE /*fInheritable*/, str);
158 if (hmtx)
159 mutex_handle = (intptr_t)hmtx;
160 else
161 {
162 mutex_handle = -1;
163 errno = ENOENT;
164 }
165#else
166 char *endp;
167 intptr_t hmutex = strtol (str, &endp, 16);
168
169 if (*endp == '\0')
170 mutex_handle = hmutex;
171 else
172 {
173 mutex_handle = -1;
174 errno = EINVAL;
175 }
176#endif
177}
178
179/* Create a new mutex or reuse one created by our parent. */
180intptr_t
181#ifdef CONFIG_NEW_WIN_CHILDREN
182create_mutex (char *mtxname, size_t size)
183#else
184create_mutex (void)
185#endif
186{
187#ifndef CONFIG_NEW_WIN_CHILDREN
188 SECURITY_ATTRIBUTES secattr;
189#endif
190 intptr_t hmutex = -1;
191
192 /* If we have a mutex handle passed from the parent Make, just use
193 that. */
194 if (mutex_handle > 0)
195 {
196#ifdef CONFIG_NEW_WIN_CHILDREN
197 mtxname[0] = '\0';
198#endif
199 return mutex_handle;
200 }
201
202#ifdef CONFIG_NEW_WIN_CHILDREN
203 /* We're the top-level Make. Child Make processes will open our mutex, since
204 children does not inherit any handles other than the three standard ones. */
205 snprintf(mtxname, size, "Make-output-%u-%u-%u", GetCurrentProcessId(),
206 GetCurrentThreadId(), GetTickCount());
207 hmutex = (intptr_t)CreateMutexA (NULL, FALSE /*Locked*/, mtxname);
208#else
209 /* We are the top-level Make, and we want the handle to be inherited
210 by our child processes. */
211 secattr.nLength = sizeof (secattr);
212 secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
213 secattr.bInheritHandle = TRUE;
214
215 hmutex = (intptr_t)CreateMutex (&secattr, FALSE, NULL);
216#endif
217 if (!hmutex)
218 {
219 DWORD err = GetLastError ();
220
221 fprintf (stderr, "CreateMutex: error %lu\n", err);
222 errno = ENOLCK;
223 hmutex = -1;
224 }
225
226 mutex_handle = hmutex;
227 return hmutex;
228}
229
230/* Return non-zero if F1 and F2 are 2 streams representing the same
231 file or pipe or device. */
232int
233same_stream (FILE *f1, FILE *f2)
234{
235 HANDLE fh1 = (HANDLE)_get_osfhandle (fileno (f1));
236 HANDLE fh2 = (HANDLE)_get_osfhandle (fileno (f2));
237
238 /* Invalid file descriptors get treated as different streams. */
239 if (fh1 && fh1 != INVALID_HANDLE_VALUE
240 && fh2 && fh2 != INVALID_HANDLE_VALUE)
241 {
242 if (fh1 == fh2)
243 return 1;
244 else
245 {
246 DWORD ftyp1 = GetFileType (fh1), ftyp2 = GetFileType (fh2);
247
248 if (ftyp1 != ftyp2
249 || ftyp1 == FILE_TYPE_UNKNOWN || ftyp2 == FILE_TYPE_UNKNOWN)
250 return 0;
251 else if (ftyp1 == FILE_TYPE_CHAR)
252 {
253 /* For character devices, check if they both refer to a
254 console. This loses if both handles refer to the
255 null device (FIXME!), but in that case we don't care
256 in the context of Make. */
257 DWORD conmode1, conmode2;
258
259 /* Each process on Windows can have at most 1 console,
260 so if both handles are for the console device, they
261 are the same. We also compare the console mode to
262 distinguish between stdin and stdout/stderr. */
263 if (GetConsoleMode (fh1, &conmode1)
264 && GetConsoleMode (fh2, &conmode2)
265 && conmode1 == conmode2)
266 return 1;
267 }
268 else
269 {
270 /* For disk files and pipes, compare their unique
271 attributes. */
272 BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
273
274 /* Pipes get zero in the volume serial number, but do
275 appear to have meaningful information in file index
276 attributes. We test file attributes as well, for a
277 good measure. */
278 if (GetFileInformationByHandle (fh1, &bhfi1)
279 && GetFileInformationByHandle (fh2, &bhfi2))
280 return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
281 && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow
282 && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
283 && bhfi1.dwFileAttributes == bhfi2.dwFileAttributes);
284 }
285 }
286 }
287 return 0;
288}
289
290/* A replacement for tmpfile, since the MSVCRT implementation creates
291 the file in the root directory of the current drive, which might
292 not be writable by our user. Most of the code borrowed from
293 create_batch_file, see job.c. */
294FILE *
295tmpfile (void)
296{
297 char temp_path[MAXPATHLEN];
298 unsigned path_size = GetTempPath (sizeof temp_path, temp_path);
299 int path_is_dot = 0;
300 /* The following variable is static so we won't try to reuse a name
301 that was generated a little while ago, because that file might
302 not be on disk yet, since we use FILE_ATTRIBUTE_TEMPORARY below,
303 which tells the OS it doesn't need to flush the cache to disk.
304 If the file is not yet on disk, we might think the name is
305 available, while it really isn't. This happens in parallel
306 builds, where Make doesn't wait for one job to finish before it
307 launches the next one. */
308 static unsigned uniq = 0;
309 static int second_loop = 0;
310 const char base[] = "gmake_tmpf";
311 const unsigned sizemax = sizeof base - 1 + 4 + 10 + 10;
312 unsigned pid = GetCurrentProcessId ();
313
314 if (path_size == 0)
315 {
316 path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
317 path_is_dot = 1;
318 }
319
320 ++uniq;
321 if (uniq >= 0x10000 && !second_loop)
322 {
323 /* If we already had 64K batch files in this
324 process, make a second loop through the numbers,
325 looking for free slots, i.e. files that were
326 deleted in the meantime. */
327 second_loop = 1;
328 uniq = 1;
329 }
330 while (path_size > 0 &&
331 path_size + sizemax < sizeof temp_path &&
332 !(uniq >= 0x10000 && second_loop))
333 {
334 HANDLE h;
335
336 sprintf (temp_path + path_size,
337 "%s%s%u-%x.tmp",
338 temp_path[path_size - 1] == '\\' ? "" : "\\",
339 base, pid, uniq);
340 h = CreateFile (temp_path, /* file name */
341 GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */
342 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
343 NULL, /* default security attributes */
344 CREATE_NEW, /* creation disposition */
345 FILE_ATTRIBUTE_NORMAL | /* flags and attributes */
346 FILE_ATTRIBUTE_TEMPORARY |
347 FILE_FLAG_DELETE_ON_CLOSE,
348 NULL); /* no template file */
349
350 if (h == INVALID_HANDLE_VALUE)
351 {
352 const DWORD er = GetLastError ();
353
354 if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
355 {
356 ++uniq;
357 if (uniq == 0x10000 && !second_loop)
358 {
359 second_loop = 1;
360 uniq = 1;
361 }
362 }
363
364 /* The temporary path is not guaranteed to exist, or might
365 not be writable by user. Use the current directory as
366 fallback. */
367 else if (path_is_dot == 0)
368 {
369 path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
370 path_is_dot = 1;
371 }
372
373 else
374 {
375 errno = EACCES;
376 break;
377 }
378 }
379 else
380 {
381 int fd = _open_osfhandle ((intptr_t)h, 0);
382
383 return _fdopen (fd, "w+b");
384 }
385 }
386
387 if (uniq >= 0x10000)
388 errno = EEXIST;
389 return NULL;
390}
391
392#endif /* !NO_OUTPUT_SYNC */
393
394#if MAKE_LOAD
395
396/* Support for dynamic loading of objects. */
397
398
399static DWORD last_err;
400
401void *
402dlopen (const char *file, int mode)
403{
404 char dllfn[MAX_PATH], *p;
405 HANDLE dllhandle;
406
407 if ((mode & ~(RTLD_LAZY | RTLD_NOW | RTLD_GLOBAL)) != 0)
408 {
409 errno = EINVAL;
410 last_err = ERROR_INVALID_PARAMETER;
411 return NULL;
412 }
413
414 if (!file)
415 dllhandle = GetModuleHandle (NULL);
416 else
417 {
418 /* MSDN says to be sure to use backslashes in the DLL file name. */
419 strcpy (dllfn, file);
420 for (p = dllfn; *p; p++)
421 if (*p == '/')
422 *p = '\\';
423
424 dllhandle = LoadLibrary (dllfn);
425 }
426 if (!dllhandle)
427 last_err = GetLastError ();
428
429 return dllhandle;
430}
431
432char *
433dlerror (void)
434{
435 static char errbuf[1024];
436 DWORD ret;
437
438 if (!last_err)
439 return NULL;
440
441 ret = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
442 | FORMAT_MESSAGE_IGNORE_INSERTS,
443 NULL, last_err, 0, errbuf, sizeof (errbuf), NULL);
444 while (ret > 0 && (errbuf[ret - 1] == '\n' || errbuf[ret - 1] == '\r'))
445 --ret;
446
447 errbuf[ret] = '\0';
448 if (!ret)
449 sprintf (errbuf, "Error code %lu", last_err);
450
451 last_err = 0;
452 return errbuf;
453}
454
455void *
456dlsym (void *handle, const char *name)
457{
458 FARPROC addr = NULL;
459
460 if (!handle || handle == INVALID_HANDLE_VALUE)
461 {
462 last_err = ERROR_INVALID_PARAMETER;
463 return NULL;
464 }
465
466 addr = GetProcAddress (handle, name);
467 if (!addr)
468 last_err = GetLastError ();
469
470 return (void *)addr;
471}
472
473int
474dlclose (void *handle)
475{
476 if (!handle || handle == INVALID_HANDLE_VALUE)
477 return -1;
478 if (!FreeLibrary (handle))
479 return -1;
480
481 return 0;
482}
483
484
485#endif /* MAKE_LOAD */
486
487
488/* MS runtime's isatty returns non-zero for any character device,
489 including the null device, which is not what we want. */
490int
491isatty (int fd)
492{
493 HANDLE fh = (HANDLE) _get_osfhandle (fd);
494 DWORD con_mode;
495
496 if (fh == INVALID_HANDLE_VALUE)
497 {
498 errno = EBADF;
499 return 0;
500 }
501 if (GetConsoleMode (fh, &con_mode))
502 return 1;
503
504 errno = ENOTTY;
505 return 0;
506}
507
508char *
509ttyname (int fd)
510{
511 /* This "knows" that Make only asks about stdout and stderr. A more
512 sophisticated implementation should test whether FD is open for
513 input or output. We can do that by looking at the mode returned
514 by GetConsoleMode. */
515 return "CONOUT$";
516}
Note: See TracBrowser for help on using the repository browser.