VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/redirect.c@ 3095

Last change on this file since 3095 was 3085, checked in by bird, 7 years ago

Config.kmk: Minimum 32-bit OS X version is now 10.5 because we want posix_spawn.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.5 KB
Line 
1/* $Id: redirect.c 3085 2017-10-03 11:01:54Z bird $ */
2/** @file
3 * kmk_redirect - Do simple program <-> file redirection (++).
4 */
5
6/*
7 * Copyright (c) 2007-2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#if defined(__APPLE__)
30/*# define _POSIX_C_SOURCE 1 / * 10.4 sdk and unsetenv * / - breaks O_CLOEXEC on 10.8 */
31#endif
32#include "make.h"
33#include <assert.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <errno.h>
38#include <fcntl.h>
39#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
40# include <process.h>
41#endif
42#if defined(_MSC_VER)
43# include <ctype.h>
44# include <io.h>
45# include "quote_argv.h"
46#else
47# ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
48# if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
49# define USE_POSIX_SPAWN
50# endif
51# elif !defined(KBUILD_OS_WINDOWS) && !defined(KBUILD_OS_OS2)
52# define USE_POSIX_SPAWN
53# endif
54# include <unistd.h>
55# ifdef USE_POSIX_SPAWN
56# include <spawn.h>
57# endif
58# include <sys/wait.h>
59#endif
60
61#include <k/kDefs.h>
62#include <k/kTypes.h>
63#include "err.h"
64#include "kbuild_version.h"
65#include "kmkbuiltin.h"
66#ifdef KMK
67# ifdef KBUILD_OS_WINDOWS
68# include "sub_proc.h"
69# include "pathstuff.h"
70# endif
71# include "job.h"
72# include "variable.h"
73#endif
74
75#ifdef __OS2__
76# define INCL_BASE
77# include <os2.h>
78# ifndef LIBPATHSTRICT
79# define LIBPATHSTRICT 3
80# endif
81#endif
82
83
84/* String + strlen tuple. */
85#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
86
87
88#if defined(_MSC_VER)
89
90
91/** Used by safeCloseFd. */
92static void __cdecl ignore_invalid_parameter(const wchar_t *a, const wchar_t *b, const wchar_t *c, unsigned d, uintptr_t e)
93{
94}
95
96#endif /* _MSC_VER */
97
98#if 0 /* unused */
99/**
100 * Safely works around MS CRT's pedantic close() function.
101 *
102 * @param fd The file handle.
103 */
104static void safeCloseFd(int fd)
105{
106#ifdef _MSC_VER
107 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
108 _set_invalid_parameter_handler(ignore_invalid_parameter);
109 close(fd);
110 _set_invalid_parameter_handler(pfnOld);
111#else
112 close(fd);
113#endif
114}
115#endif /* unused */
116
117
118static const char *name(const char *pszName)
119{
120 const char *psz = strrchr(pszName, '/');
121#if defined(_MSC_VER) || defined(__OS2__)
122 const char *psz2 = strrchr(pszName, '\\');
123 if (!psz2)
124 psz2 = strrchr(pszName, ':');
125 if (psz2 && (!psz || psz2 > psz))
126 psz = psz2;
127#endif
128 return psz ? psz + 1 : pszName;
129}
130
131
132static int usage(FILE *pOut, const char *argv0)
133{
134 argv0 = name(argv0);
135 fprintf(pOut,
136 "usage: %s [-[rwa+tb]<fd> <file>] [-d<fd>=<src-fd>] [-c<fd>]\n"
137 " [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage]\n"
138 " [-v] -- <program> [args]\n"
139 " or: %s --help\n"
140 " or: %s --version\n"
141 "\n"
142 "The rwa+tb is like for fopen, if not specified it defaults to w+.\n"
143 "The <fd> is either a number or an alias for the standard handles:\n"
144 " i = stdin\n"
145 " o = stdout\n"
146 " e = stderr\n"
147 "\n"
148 "The -d switch duplicate the right hand file descriptor (src-fd) to the left\n"
149 "hand side one (fd).\n"
150 "\n"
151 "The -c switch will close the specified file descriptor.\n"
152 "\n"
153 "The -Z switch zaps the environment.\n"
154 "\n"
155 "The -E switch is for making changes to the environment in a putenv\n"
156 "fashion.\n"
157 "\n"
158 "The -C switch is for changing the current directory. This takes immediate\n"
159 "effect, so be careful where you put it.\n"
160 "\n"
161 "The --wcc-brain-damage switch is to work around wcc and wcc386 (Open Watcom)\n"
162 "not following normal quoting conventions on Windows, OS/2, and DOS.\n"
163 "\n"
164 "The -v switch is for making the thing more verbose.\n"
165 "\n"
166 "This command was originally just a quick hack to avoid invoking the shell\n"
167 "on Windows (cygwin) where forking is very expensive and has exhibited\n"
168 "stability issues on SMP machines. It has since grown into something like\n"
169 "/usr/bin/env on steroids.\n"
170 ,
171 argv0, argv0, argv0);
172 return 2;
173}
174
175
176/**
177 * Decoded file descriptor operations.
178 */
179typedef struct REDIRECTORDERS
180{
181 enum {
182 kRedirectOrder_Invalid = 0,
183 kRedirectOrder_Close,
184 kRedirectOrder_Open,
185 kRedirectOrder_Dup
186 } enmOrder;
187 /** The target file handle. */
188 int fdTarget;
189 /** The source file name, -1 on close only.
190 * This is an opened file if pszFilename is set. */
191 int fdSource;
192 /** Whether to remove the file on failure cleanup. */
193 int fRemoveOnFailure;
194 /** The open flags (for O_TEXT/O_BINARY) on windows. */
195 int fOpen;
196 /** The filename - NULL if close only. */
197 const char *pszFilename;
198#ifndef USE_POSIX_SPAWN
199 /** Saved file descriptor. */
200 int fdSaved;
201 /** Saved flags. */
202 int fSaved;
203#endif
204} REDIRECTORDERS;
205
206
207#ifdef _MSC_VER
208
209/**
210 * Safe way of getting the OS handle of a file descriptor without triggering
211 * invalid parameter handling.
212 *
213 * @returns The handle value if open, INVALID_HANDLE_VALUE if not.
214 * @param fd The file descriptor in question.
215 */
216static HANDLE mscGetOsHandle(int fd)
217{
218 intptr_t hHandle;
219 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
220 _set_invalid_parameter_handler(ignore_invalid_parameter);
221 hHandle = _get_osfhandle(fd);
222 _set_invalid_parameter_handler(pfnOld);
223 return hHandle != -1 ? (HANDLE)hHandle : INVALID_HANDLE_VALUE;
224}
225
226/**
227 * Checks if the specified file descriptor is open.
228 *
229 * @returns K_TRUE if open, K_FALSE if not.
230 * @param fd The file descriptor in question.
231 */
232static KBOOL mscIsOpenFile(int fd)
233{
234 return mscGetOsHandle(fd) != INVALID_HANDLE_VALUE;
235}
236
237/**
238 * Checks if the native handle is inheritable.
239 *
240 * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid handle.
241 * @param hHandle The native handle.
242 */
243static KBOOL mscIsNativeHandleInheritable(HANDLE hHandle)
244{
245 DWORD fFlags = 0;
246 if (GetHandleInformation(hHandle, &fFlags))
247 return (fFlags & HANDLE_FLAG_INHERIT) != 0;
248 return K_FALSE;
249}
250
251/**
252 * Checks if the file descriptor is inheritable or not.
253 *
254 * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid descriptor.
255 * @param fd The file descriptor in question.
256 */
257static KBOOL mscIsInheritable(int fd)
258{
259 HANDLE hHandle = mscGetOsHandle(fd);
260 if (hHandle != INVALID_HANDLE_VALUE)
261 return mscIsNativeHandleInheritable(hHandle);
262 return K_FALSE;
263}
264
265/**
266 * A dup3 like function.
267 *
268 * @returns fdNew on success, -1 on failure w/ error details written to pStdErr.
269 * @param fdSource The source descriptor.
270 * @param fdNew The new descriptor.
271 * @param fFlags The inherit and text/binary mode flag.
272 * @param pStdErr Working stderr to write error details to.
273 */
274static int mscDup3(int fdSource, int fdNew, int fFlags, FILE *pStdErr)
275{
276 if (!fFlags & _O_NOINHERIT)
277 {
278 /* ASSUMES fFlags doesn't include any changing _O_TEXT/_O_BINARY. */
279 int fdDup = _dup2(fdSource, fdNew);
280 if (fdDup != -1)
281 return fdDup;
282 fprintf(pStdErr, "%s: _dup2(%d,%d) failed: %s\n", g_progname, fdSource, fdNew, strerror(errno));
283 }
284 else
285 {
286 HANDLE hSource = mscGetOsHandle(fdSource);
287 unsigned cTries = 0;
288 int aFdTries[48];
289
290 if (hSource != INVALID_HANDLE_VALUE)
291 {
292 HANDLE hCurProc = GetCurrentProcess();
293 BOOL fInherit = !(fFlags & _O_NOINHERIT);
294
295 /*
296 * Make sure the old descriptor is closed and can be used again.
297 */
298 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
299 _set_invalid_parameter_handler(ignore_invalid_parameter);
300 close(fdNew);
301 _set_invalid_parameter_handler(pfnOld);
302
303 /*
304 * Duplicate the source handle till we've got a match.
305 */
306 for (;;)
307 {
308 HANDLE hDup = INVALID_HANDLE_VALUE;
309 if (DuplicateHandle(hCurProc, hSource, hCurProc, &hDup, 0 /* DesiredAccess */,
310 fInherit, DUPLICATE_SAME_ACCESS))
311 {
312 int fdDup = _open_osfhandle((intptr_t)hDup, fFlags);
313 if (fdDup != -1)
314 {
315 if (fdDup == fdNew)
316 {
317 while (cTries-- > 0)
318 close(aFdTries[cTries]);
319 return fdDup;
320 }
321
322 aFdTries[cTries++] = fdDup;
323 if ( fdDup < fdNew
324 && cTries < K_ELEMENTS(aFdTries))
325 continue;
326 fprintf(pStdErr, "%s: mscDup3(%d,%d): giving up! (last fdDup=%d)\n",
327 g_progname, fdSource, fdNew, fdDup);
328 }
329 else
330 {
331 fprintf(pStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno));
332 CloseHandle(hDup);
333 }
334 }
335 else
336 fprintf(pStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hSource, GetLastError());
337 break;
338 }
339
340 while (cTries-- > 0)
341 close(aFdTries[cTries]);
342 }
343 else
344 fprintf(pStdErr, "%s: mscDup3(%d,%d): source descriptor is invalid!\n", g_progname, fdSource, fdNew);
345 }
346 return -1;
347}
348
349#endif /* _MSC_VER */
350
351static KBOOL kRedirectHasConflict(int fd, unsigned cOrders, REDIRECTORDERS *paOrders)
352{
353 while (cOrders-- > 0)
354 if (paOrders[cOrders].fdTarget == fd)
355 return K_TRUE;
356 return K_FALSE;
357}
358
359
360/**
361 * Creates a file descriptor for @a pszFilename that does not conflict with any
362 * previous orders.
363 *
364 * We need to be careful that there isn't a close or dup targetting the
365 * temporary file descriptor we return. Also, we need to take care with the
366 * descriptor's inheritability. It should only be inheritable if the returned
367 * descriptor matches the target descriptor (@a fdTarget).
368 *
369 * @returns File descriptor on success, -1 & err/errx on failure.
370 *
371 * The returned file descriptor is not inherited (i.e. close-on-exec),
372 * unless it matches @a fdTarget
373 *
374 * @param pszFilename The filename to open.
375 * @param fOpen The open flags.
376 * @param fMode The file creation mode (if applicable).
377 * @param cOrders The number of orders.
378 * @param paOrders The order array.
379 * @param fRemoveOnFailure Whether to remove the file on failure.
380 * @param fdTarget The target descriptor.
381 */
382static int kRedirectOpenWithoutConflict(const char *pszFilename, int fOpen, mode_t fMode,
383 unsigned cOrders, REDIRECTORDERS *paOrders, int fRemoveOnFailure, int fdTarget)
384{
385#ifdef _O_NOINHERIT
386 int const fNoInherit = _O_NOINHERIT;
387#elif defined(O_NOINHERIT)
388 int const fNoInherit = O_NOINHERIT;
389#elif defined(O_CLOEXEC)
390 int const fNoInherit = O_CLOEXEC;
391#else
392 int const fNoInherit = 0;
393# define USE_FD_CLOEXEC
394#endif
395 int aFdTries[32];
396 unsigned cTries;
397 int fdOpened;
398
399#ifdef KBUILD_OS_WINDOWS
400 if (strcmp(pszFilename, "/dev/null") == 0)
401 pszFilename = "nul";
402#endif
403
404 /* Open it first. */
405 fdOpened = open(pszFilename, fOpen | fNoInherit, fMode);
406 if (fdOpened < 0)
407 return err(-1, "open(%s,%#x,) failed", pszFilename, fOpen);
408
409 /* Check for conflicts. */
410 if (!kRedirectHasConflict(fdOpened, cOrders, paOrders))
411 {
412 if (fdOpened != fdTarget)
413 return fdOpened;
414#ifndef _MSC_VER /* Stupid, stupid MSVCRT! No friggin way of making a handle inheritable (or not). */
415# ifndef USE_FD_CLOEXEC
416 if (fcntl(fdOpened, F_SETFD, 0) != -1)
417# endif
418 return fdOpened;
419#endif
420 }
421
422 /*
423 * Do conflict resolving.
424 */
425 cTries = 1;
426 aFdTries[cTries++] = fdOpened;
427 while (cTries < K_ELEMENTS(aFdTries))
428 {
429 fdOpened = open(pszFilename, fOpen | fNoInherit, fMode);
430 if (fdOpened >= 0)
431 {
432 if ( !kRedirectHasConflict(fdOpened, cOrders, paOrders)
433#ifdef _MSC_VER
434 && fdOpened != fdTarget
435#endif
436 )
437 {
438#ifndef _MSC_VER
439# ifdef USE_FD_CLOEXEC
440 if ( fdOpened == fdTarget
441 || fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1)
442# else
443 if ( fdOpened != fdTarget
444 || fcntl(fdOpened, F_SETFD, 0) != -1)
445# endif
446#endif
447 {
448 while (cTries-- > 0)
449 close(aFdTries[cTries]);
450 return fdOpened;
451 }
452 }
453
454 }
455 else
456 {
457 err(-1, "open(%s,%#x,) #%u failed", pszFilename, cTries + 1, fOpen);
458 break;
459 }
460 aFdTries[cTries++] = fdOpened;
461 }
462
463 /*
464 * Give up.
465 */
466 if (fdOpened >= 0)
467 errx(-1, "failed to find a conflict free file descriptor for '%s'!", pszFilename);
468
469 while (cTries-- > 0)
470 close(aFdTries[cTries]);
471 return -1;
472}
473
474
475/**
476 * Cleans up the file operation orders.
477 *
478 * This does not restore stuff, just closes handles we've opened for the child.
479 *
480 * @param cOrders Number of file operation orders.
481 * @param paOrders The file operation orders.
482 * @param fFailed Set if it's a failure.
483 */
484static void kRedirectCleanupFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, KBOOL fFailure)
485{
486 unsigned i = cOrders;
487 while (i-- > 0)
488 {
489 if ( paOrders[i].enmOrder == kRedirectOrder_Open
490 && paOrders[i].fdSource != -1)
491 {
492 close(paOrders[i].fdSource);
493 paOrders[i].fdSource = -1;
494 if ( fFailure
495 && paOrders[i].fRemoveOnFailure
496 && paOrders[i].pszFilename)
497 remove(paOrders[i].pszFilename);
498 }
499 }
500}
501
502
503#ifndef USE_POSIX_SPAWN
504
505/**
506 * Saves a file handle to one which isn't inherited and isn't affected by the
507 * file orders.
508 *
509 * @returns 0 on success, non-zero exit code on failure.
510 * @param pToSave Pointer to the file order to save the target
511 * descriptor of.
512 * @param cOrders Number of file orders.
513 * @param paOrders The array of file orders.
514 * @param ppWorkingStdErr Pointer to a pointer to a working stderr. This will
515 * get replaced if we're saving stderr, so that we'll
516 * keep having a working one to report failures to.
517 */
518static int kRedirectSaveHandle(REDIRECTORDERS *pToSave, unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
519{
520 int fdToSave = pToSave->fdTarget;
521 int rcRet = 10;
522
523 /*
524 * First, check if there's actually handle here that needs saving.
525 */
526# ifdef KBUILD_OS_WINDOWS
527 HANDLE hToSave = mscGetOsHandle(fdToSave);
528 if (hToSave != INVALID_HANDLE_VALUE)
529 {
530 pToSave->fSaved = _setmode(fdToSave, _O_BINARY);
531 if (pToSave->fSaved != _O_BINARY)
532 _setmode(fdToSave, pToSave->fSaved);
533 if (!mscIsNativeHandleInheritable(hToSave))
534 pToSave->fSaved |= _O_NOINHERIT;
535 }
536 if (hToSave != INVALID_HANDLE_VALUE)
537# else
538 pToSave->fSaved = fcntl(pToSave->fdTarget, F_GETFD, 0);
539 if (pToSave->fSaved != -1)
540# endif
541 {
542 /*
543 * Try up to 32 times to get a duplicate descriptor that doesn't conflict.
544 */
545# ifdef KBUILD_OS_WINDOWS
546 HANDLE hCurProc = GetCurrentProcess();
547# endif
548 int aFdTries[32];
549 int cTries = 0;
550 do
551 {
552 /* Duplicate the handle (windows makes this complicated). */
553 int fdDup;
554# ifdef KBUILD_OS_WINDOWS
555 HANDLE hDup = INVALID_HANDLE_VALUE;
556 if (!DuplicateHandle(hCurProc, hToSave, hCurProc, &hDup, 0 /* DesiredAccess */,
557 FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
558 {
559 fprintf(*ppWorkingStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hToSave, GetLastError());
560 break;
561 }
562 fdDup = _open_osfhandle((intptr_t)hDup, pToSave->fSaved | _O_NOINHERIT);
563 if (fdDup == -1)
564 {
565 fprintf(*ppWorkingStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno));
566 CloseHandle(hDup);
567 break;
568 }
569# else
570 fdDup = dup(fdToSave);
571 if (fdDup == -1)
572 {
573 fprintf(*ppWorkingStdErr, "%s: dup(%#x) failed: %u\n", g_progname, fdToSave, strerror(errno));
574 break;
575 }
576#endif
577 /* Is the duplicate usable? */
578 if (!kRedirectHasConflict(fdDup, cOrders, paOrders))
579 {
580 pToSave->fdSaved = fdDup;
581 if ( *ppWorkingStdErr == stderr
582 && fdToSave == fileno(*ppWorkingStdErr))
583 {
584 *ppWorkingStdErr = fdopen(fdDup, "wt");
585 if (*ppWorkingStdErr == NULL)
586 {
587 fprintf(stderr, "%s: fdopen(%d,\"wt\") failed: %s\n", g_progname, fdDup, strerror(errno));
588 *ppWorkingStdErr = stderr;
589 close(fdDup);
590 break;
591 }
592 }
593 rcRet = 0;
594 break;
595 }
596
597 /* Not usuable, stash it and try again. */
598 aFdTries[cTries++] = fdDup;
599 } while (cTries < K_ELEMENTS(aFdTries));
600
601 /*
602 * Clean up unused duplicates.
603 */
604 while (cTries-- > 0)
605 close(aFdTries[cTries]);
606 }
607 else
608 {
609 /*
610 * Nothing to save.
611 */
612 pToSave->fdSaved = -1;
613 rcRet = 0;
614 }
615 return rcRet;
616}
617
618
619/**
620 * Restores the target file descriptors affected by the file operation orders.
621 *
622 * @param cOrders Number of file operation orders.
623 * @param paOrders The file operation orders.
624 * @param ppWorkingStdErr Pointer to a pointer to the working stderr. If this
625 * is one of the saved file descriptors, we'll restore
626 * it to stderr.
627 */
628static void kRedirectRestoreFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
629{
630 int iSavedErrno = errno;
631 unsigned i = cOrders;
632 while (i-- > 0)
633 {
634 if (paOrders[i].fdSaved != -1)
635 {
636 KBOOL fRestoreStdErr = *ppWorkingStdErr != stderr
637 && paOrders[i].fdSaved == fileno(*ppWorkingStdErr);
638
639#ifdef KBUILD_OS_WINDOWS
640 if (mscDup3(paOrders[i].fdSaved, paOrders[i].fdTarget, paOrders[i].fSaved, *ppWorkingStdErr) != -1)
641#else
642 if (dup2(paOrders[i].fdSaved, paOrders[i].fdTarget) != -1)
643#endif
644 {
645 close(paOrders[i].fdSaved);
646 paOrders[i].fdSaved = -1;
647
648 if (fRestoreStdErr)
649 {
650 *ppWorkingStdErr = stderr;
651 assert(fileno(stderr) == paOrders[i].fdTarget);
652 }
653 }
654#ifndef KBUILD_OS_WINDOWS
655 else
656 fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s\n",
657 g_progname, paOrders[i].fdSaved, paOrders[i].fdTarget, strerror(errno));
658#endif
659 }
660
661#ifndef KBUILD_OS_WINDOWS
662 if (paOrders[i].fSaved != -1)
663 {
664 if (fcntl(paOrders[i].fdTarget, F_SETFD, paOrders[i].fSaved & FD_CLOEXEC) != -1)
665 paOrders[i].fSaved = -1;
666 else
667 fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,%s) failed: %s\n",
668 g_progname, paOrders[i].fdTarget, paOrders[i].fSaved & FD_CLOEXEC ? "FD_CLOEXEC" : "0", strerror(errno));
669 }
670#endif
671 }
672 errno = iSavedErrno;
673}
674
675
676/**
677 * Executes the file operation orders.
678 *
679 * @returns 0 on success, exit code on failure.
680 * @param cOrders Number of file operation orders.
681 * @param paOrders File operation orders to execute.
682 * @param ppWorkingStdErr Where to return a working stderr (mainly for
683 * kRedirectRestoreFdOrders).
684 */
685static int kRedirectExecFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
686{
687 unsigned i;
688
689 *ppWorkingStdErr = stderr;
690 for (i = 0; i < cOrders; i++)
691 {
692 int rcExit = 10;
693 switch (paOrders[i].enmOrder)
694 {
695 case kRedirectOrder_Close:
696 {
697 /* If the handle isn't used by any of the following operation,
698 just mark it as non-inheritable if necessary. */
699 int const fdTarget = paOrders[i].fdTarget;
700 unsigned j;
701 for (j = i + 1; j < cOrders; j++)
702 if (paOrders[j].fdTarget == fdTarget)
703 break;
704# ifdef _MSC_VER
705 if (j >= cOrders && !mscIsInheritable(fdTarget))
706 rcExit = 0;
707# else
708 if (j >= cOrders)
709 {
710 paOrders[j].fSaved = fcntl(fdTarget, F_GETFD, 0);
711 if (paOrders[j].fSaved != -1)
712 {
713 if (paOrders[j].fSaved & FD_CLOEXEC)
714 rcExit = 0;
715 else if ( fcntl(fdTarget, F_SETFD, FD_CLOEXEC) != -1
716 || errno == EBADF)
717 rcExit = 0;
718 else
719 fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,FD_CLOEXEC) failed: %s\n",
720 g_progname, fdTarget, strerror(errno));
721 }
722 else if (errno == EBADF)
723 rcExit = 0;
724 else
725 fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_GETFD,0) failed: %s\n", g_progname, fdTarget, strerror(errno));
726 }
727# endif
728 else
729 rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr);
730 break;
731 }
732
733 case kRedirectOrder_Dup:
734 case kRedirectOrder_Open:
735 rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr);
736 if (rcExit == 0)
737 {
738 if (dup2(paOrders[i].fdSource, paOrders[i].fdTarget) != -1)
739 rcExit = 0;
740 else
741 {
742 if (paOrders[i].enmOrder == kRedirectOrder_Open)
743 fprintf(*ppWorkingStdErr, "%s: dup2(%d [%s],%d) failed: %s\n", g_progname, paOrders[i].fdSource,
744 paOrders[i].pszFilename, paOrders[i].fdTarget, strerror(errno));
745 else
746 fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s\n",
747 g_progname, paOrders[i].fdSource, paOrders[i].fdTarget, strerror(errno));
748 rcExit = 10;
749 }
750 }
751 break;
752
753 default:
754 fprintf(*ppWorkingStdErr, "%s: error! invalid enmOrder=%d\n", g_progname, paOrders[i].enmOrder);
755 rcExit = 99;
756 break;
757 }
758
759 if (rcExit != 0)
760 {
761 kRedirectRestoreFdOrders(i, paOrders, ppWorkingStdErr);
762 return rcExit;
763 }
764 }
765
766 return 0;
767}
768
769#endif /* !USE_POSIX_SPAWN */
770
771
772/**
773 * Does the child spawning .
774 *
775 * @returns Exit code.
776 * @param pszExecutable The child process executable.
777 * @param cArgs Number of arguments.
778 * @param papszArgs The child argument vector.
779 * @param fWatcomBrainDamage Whether MSC need to do quoting according to
780 * weird Watcom WCC rules.
781 * @param papszEnvVars The child environment vector.
782 * @param pszCwd The current working directory of the child.
783 * @param pszSavedCwd The saved current working directory. This is
784 * NULL if the CWD doesn't need changing.
785 * @param cOrders Number of file operation orders.
786 * @param paOrders The file operation orders.
787 * @param pFileActions The posix_spawn file actions.
788 * @param cVerbosity The verbosity level.
789 * @param pPidSpawned Where to return the PID of the spawned child
790 * when we're inside KMK and we're return without
791 * waiting.
792 * @param pfIsChildExitCode Where to indicate whether the return exit code
793 * is from the child or from our setup efforts.
794 */
795static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage, char **papszEnvVars,
796 const char *pszCwd, const char *pszSavedCwd, unsigned cOrders, REDIRECTORDERS *paOrders,
797#ifdef USE_POSIX_SPAWN
798 posix_spawn_file_actions_t *pFileActions,
799#endif
800 unsigned cVerbosity,
801#ifdef KMK
802 pid_t *pPidSpawned,
803#endif
804 KBOOL *pfIsChildExitCode)
805{
806 int rcExit = 0;
807 int i;
808#ifdef _MSC_VER
809 char **papszArgsOriginal = papszArgs;
810#endif
811 *pfIsChildExitCode = K_FALSE;
812
813#ifdef _MSC_VER
814 /*
815 * Do MSC parameter quoting.
816 */
817 papszArgs = malloc((cArgs + 1) * sizeof(papszArgs[0]));
818 if (papszArgs)
819 memcpy(papszArgs, papszArgsOriginal, (cArgs + 1) * sizeof(papszArgs[0]));
820 else
821 return errx(9, "out of memory!");
822
823 rcExit = quote_argv(cArgs, papszArgs, fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
824 if (rcExit == 0)
825#endif
826 {
827 /*
828 * Display what we're about to execute if we're in verbose mode.
829 */
830 if (cVerbosity > 0)
831 {
832 for (i = 0; i < cArgs; i++)
833 warnx("debug: argv[%i]=%s<eos>", i, papszArgs[i]);
834 for (i = 0; i < (int)cOrders; i++)
835 switch (paOrders[i].enmOrder)
836 {
837 case kRedirectOrder_Close:
838 warnx("debug: close %d\n", paOrders[i].fdTarget);
839 break;
840 case kRedirectOrder_Dup:
841 warnx("debug: dup %d to %d\n", paOrders[i].fdSource, paOrders[i].fdTarget);
842 break;
843 case kRedirectOrder_Open:
844 warnx("debug: open '%s' (%#x) as [%d ->] %d\n",
845 paOrders[i].pszFilename, paOrders[i].fOpen, paOrders[i].fdSource, paOrders[i].fdTarget);
846 break;
847 default:
848 warnx("error! invalid enmOrder=%d", paOrders[i].enmOrder);
849 assert(0);
850 break;
851 }
852 if (pszSavedCwd)
853 warnx("debug: chdir %s\n", pszCwd);
854 }
855
856 /*
857 * Change working directory if so requested.
858 */
859 if (pszSavedCwd)
860 {
861 if (chdir(pszCwd) < 0)
862 rcExit = errx(10, "Failed to change directory to '%s'", pszCwd);
863 }
864 if (rcExit == 0)
865 {
866#ifndef USE_POSIX_SPAWN
867 /*
868 * Execute the file orders.
869 */
870 FILE *pWorkingStdErr = NULL;
871 rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr);
872 if (rcExit == 0)
873#endif
874 {
875#ifdef KMK
876 /*
877 * We're spawning from within kmk.
878 */
879#if defined(KBUILD_OS_WINDOWS)
880 /* Windows is slightly complicated due to handles and sub_proc.c. */
881 HANDLE hProcess = (HANDLE)_spawnvpe(_P_NOWAIT, pszExecutable, papszArgs, papszEnvVars);
882 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
883 if ((intptr_t)hProcess != -1)
884 {
885 if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
886 {
887 if (cVerbosity > 0)
888 warnx("debug: spawned %d", *pPidSpawned);
889 }
890 else
891 {
892 DWORD dwTmp;
893 warn("sub_proc is out of slots, waiting for child...");
894 dwTmp = WaitForSingleObject(hProcess, INFINITE);
895 if (dwTmp != WAIT_OBJECT_0)
896 warn("WaitForSingleObject failed: %#x\n", dwTmp);
897
898 if (GetExitCodeProcess(hProcess, &dwTmp))
899 rcExit = (int)dwTmp;
900 else
901 {
902 warn("GetExitCodeProcess failed: %u\n", GetLastError());
903 TerminateProcess(hProcess, 127);
904 rcExit = 127;
905 }
906
907 CloseHandle(hProcess);
908 *pPidSpawned = 0;
909 *pfIsChildExitCode = K_TRUE;
910 }
911 }
912 else
913 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
914
915# elif defined(KBUILD_OS_OS2)
916 *pPidSpawned = _spawnvpe(P_NOWAIT, pszExecutable, papszArgs, papszEnvVars);
917 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
918 if (*pPidSpawned != -1)
919 {
920 if (cVerbosity > 0)
921 warnx("debug: spawned %d", *pPidSpawned);
922 }
923 else
924 {
925 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
926 *pPidSpawned = 0;
927 }
928#else
929 rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
930 if (rcExit == 0)
931 {
932 if (cVerbosity > 0)
933 warnx("debug: spawned %d", *pPidSpawned);
934 }
935 else
936 {
937 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
938 *pPidSpawned = 0;
939 }
940#endif
941
942#else /* !KMK */
943 /*
944 * Spawning from inside the kmk_redirect executable.
945 */
946# if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
947 errno = 0;
948# if defined(KBUILD_OS_WINDOWS)
949 rcExit = (int)_spawnvpe(_P_WAIT, pszExecutable, papszArgs, papszEnvVars);
950# else
951 rcExit = (int)_spawnvpe(P_WAIT, pszExecutable, papszArgs, papszEnvVars);
952# endif
953 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
954 if (rcExit != -1 || errno == 0)
955 {
956 *pfIsChildExitCode = K_TRUE;
957 if (cVerbosity > 0)
958 warnx("debug: exit code: %d", rcExit);
959 }
960 else
961 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
962
963# else
964 pid_t pidChild = 0;
965 rcExit = posix_spawnp(&pidChild, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
966 if (rcExit == 0)
967 {
968 *pfIsChildExitCode = K_TRUE;
969 if (cVerbosity > 0)
970 warnx("debug: spawned %d", pidChild);
971
972 /* Wait for the child. */
973 for (;;)
974 {
975 pid_t pid = waitpid(pidChild, &rcExit, 0 /*block*/);
976 if (pid == pidChild)
977 {
978 if (cVerbosity > 0)
979 warnx("debug: %d exit code: %d", pidChild, rcExit);
980 break;
981 }
982 if ( errno != EINTR
983# ifdef ERESTART
984 && errno != ERESTART
985# endif
986 )
987 {
988 rcExit = err(11, "waitpid failed");
989 kill(pidChild, SIGKILL);
990 break;
991 }
992 }
993 }
994 else
995 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
996
997# endif
998#endif /* !KMK */
999 }
1000 }
1001
1002 /*
1003 * Restore the current directory.
1004 */
1005 if (pszSavedCwd)
1006 {
1007 if (chdir(pszSavedCwd) < 0)
1008 warn("Failed to restore directory to '%s'", pszSavedCwd);
1009 }
1010 }
1011#ifdef _MSC_VER
1012 else
1013 rcExit = errx(9, "quite_argv failed: %u", rcExit);
1014
1015 /* Restore the original argv strings, freeing the quote_argv replacements. */
1016 i = cArgs;
1017 while (i-- > 0)
1018 if (papszArgs[i] != papszArgsOriginal[i])
1019 free(papszArgs[i]);
1020 free(papszArgs);
1021#endif
1022 return rcExit;
1023}
1024
1025
1026/**
1027 * The function that does almost everything here... ugly.
1028 */
1029#ifdef KMK
1030int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
1031#else
1032int main(int argc, char **argv, char **envp)
1033#endif
1034{
1035 int rcExit = 0;
1036 KBOOL fChildExitCode = K_FALSE;
1037#ifdef USE_POSIX_SPAWN
1038 posix_spawn_file_actions_t FileActions;
1039#endif
1040 unsigned cOrders = 0;
1041 REDIRECTORDERS aOrders[32];
1042
1043 int iArg;
1044 const char *pszExecutable = NULL;
1045 char **papszEnvVars = NULL;
1046 unsigned cAllocatedEnvVars;
1047 unsigned iEnvVar;
1048 unsigned cEnvVars;
1049 int fWatcomBrainDamage = 0;
1050 int cVerbosity = 0;
1051 char *pszSavedCwd = NULL;
1052 size_t const cbCwdBuf = GET_PATH_MAX;
1053 PATH_VAR(szCwd);
1054#ifdef KBUILD_OS_OS2
1055 ULONG ulLibPath;
1056 char *apszSavedLibPaths[LIBPATHSTRICT + 1] = { NULL, NULL, NULL, NULL };
1057#endif
1058
1059
1060 g_progname = argv[0];
1061
1062 if (argc <= 1)
1063 return usage(stderr, argv[0]);
1064
1065 /*
1066 * Create default program environment.
1067 */
1068#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
1069 if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
1070#else
1071 if (getcwd(szCwd, cbCwdBuf) != NULL)
1072#endif
1073 { /* likely */ }
1074 else
1075 return err(9, "getcwd failed");
1076
1077#if defined(KMK)
1078 /* We get it from kmk and just count it: */
1079 papszEnvVars = pChild->environment;
1080 if (!papszEnvVars)
1081 pChild->environment = papszEnvVars = target_environment(pChild->file);
1082 cEnvVars = 0;
1083 while (papszEnvVars[cEnvVars] != NULL)
1084 cEnvVars++;
1085 cAllocatedEnvVars = cEnvVars;
1086#else
1087 /* We make a copy and we manage ourselves: */
1088 cEnvVars = 0;
1089 while (envp[cEnvVars] != NULL)
1090 cEnvVars++;
1091
1092 cAllocatedEnvVars = cEnvVars + 4;
1093 papszEnvVars = malloc((cAllocatedEnvVars + 1) * sizeof(papszEnvVars));
1094 if (!papszEnvVars)
1095 return errx(9, "out of memory!");
1096
1097 iEnvVar = cEnvVars;
1098 papszEnvVars[iEnvVar] = NULL;
1099 while (iEnvVar-- > 0)
1100 {
1101 papszEnvVars[iEnvVar] = strdup(envp[iEnvVar]);
1102 if (!papszEnvVars[iEnvVar])
1103 {
1104 while (iEnvVar-- > 0)
1105 free(papszEnvVars[iEnvVar]);
1106 free(papszEnvVars);
1107 return errx(9, "out of memory!");
1108 }
1109 }
1110#endif
1111
1112#ifdef USE_POSIX_SPAWN
1113 /*
1114 * Init posix attributes.
1115 */
1116 rcExit = posix_spawn_file_actions_init(&FileActions);
1117 if (rcExit != 0)
1118 rcExit = errx(9, "posix_spawn_file_actions_init failed: %s", strerror(rcExit));
1119#endif
1120
1121 /*
1122 * Parse arguments.
1123 */
1124 for (iArg = 1; rcExit == 0 && iArg < argc; iArg++)
1125 {
1126 char *pszArg = argv[iArg];
1127 if (*pszArg == '-')
1128 {
1129 int fd;
1130 char chOpt;
1131 const char *pszValue;
1132
1133 chOpt = *++pszArg;
1134 pszArg++;
1135 if (chOpt == '-')
1136 {
1137 /* '--' indicates where the bits to execute start. Check if we're
1138 relaunching ourselves here and just continue parsing if we are. */
1139 if (*pszArg == '\0')
1140 {
1141 iArg++;
1142 if ( iArg >= argc
1143 || ( strcmp(argv[iArg], "kmk_builtin_redirect") != 0
1144 && strcmp(argv[iArg], argv[0]) != 0))
1145 break;
1146 continue;
1147 }
1148
1149 if ( strcmp(pszArg, "wcc-brain-damage") == 0
1150 || strcmp(pszArg, "watcom-brain-damage") == 0)
1151 {
1152 fWatcomBrainDamage = 1;
1153 continue;
1154 }
1155
1156 /* convert to short. */
1157 if (strcmp(pszArg, "help") == 0)
1158 chOpt = 'h';
1159 else if (strcmp(pszArg, "version") == 0)
1160 chOpt = 'V';
1161 else if ( strcmp(pszArg, "set") == 0
1162 || strcmp(pszArg, "env") == 0)
1163 chOpt = 'E';
1164 else if (strcmp(pszArg, "append") == 0)
1165 chOpt = 'A';
1166 else if (strcmp(pszArg, "prepend") == 0)
1167 chOpt = 'D';
1168 else if (strcmp(pszArg, "unset") == 0)
1169 chOpt = 'U';
1170 else if ( strcmp(pszArg, "zap-env") == 0
1171 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
1172 chOpt = 'Z';
1173 else if (strcmp(pszArg, "chdir") == 0)
1174 chOpt = 'C';
1175 else if (strcmp(pszArg, "close") == 0)
1176 chOpt = 'c';
1177 else if (strcmp(pszArg, "verbose") == 0)
1178 chOpt = 'v';
1179 else
1180 {
1181 errx(2, "Unknown option: '%s'", pszArg - 2);
1182 rcExit = usage(stderr, argv[0]);
1183 break;
1184 }
1185 pszArg = "";
1186 }
1187
1188 /*
1189 * Deal with the obligatory help and version switches first to get them out of the way.
1190 */
1191 if (chOpt == 'h')
1192 {
1193 usage(stdout, argv[0]);
1194 rcExit = -1;
1195 break;
1196 }
1197 if (chOpt == 'V')
1198 {
1199 kbuild_version(argv[0]);
1200 rcExit = -1;
1201 break;
1202 }
1203
1204 /*
1205 * Get option value first, if the option takes one.
1206 */
1207 if ( chOpt == 'E'
1208 || chOpt == 'A'
1209 || chOpt == 'D'
1210 || chOpt == 'U'
1211 || chOpt == 'C'
1212 || chOpt == 'c'
1213 || chOpt == 'd'
1214 || chOpt == 'e')
1215 {
1216 if (*pszArg != '\0')
1217 pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
1218 else if (++iArg < argc)
1219 pszValue = argv[iArg];
1220 else
1221 {
1222 errx(2, "syntax error: Option -%c requires a value!", chOpt);
1223 rcExit = usage(stderr, argv[0]);
1224 break;
1225 }
1226 }
1227 else
1228 pszValue = NULL;
1229
1230 /*
1231 * Environment switch?
1232 */
1233 if (chOpt == 'E')
1234 {
1235 const char *pchEqual = strchr(pszValue, '=');
1236#ifdef KBUILD_OS_OS2
1237 if ( strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0
1238 || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0
1239 || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0)
1240 {
1241 ULONG ulVar = *pszValue == 'B' ? BEGIN_LIBPATH
1242 : *pszValue == 'E' ? END_LIBPATH
1243 : LIBPATHSTRICT;
1244 APIRET rc;
1245 if (apszSavedLibPaths[ulVar] == NULL)
1246 {
1247 /* The max length is supposed to be 1024 bytes. */
1248 apszSavedLibPaths[ulVar] = calloc(1024, 2);
1249 if (apszSavedLibPaths[ulVar])
1250 {
1251 rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar);
1252 if (rc)
1253 {
1254 rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc);
1255 free(apszSavedLibPaths[ulVar]);
1256 apszSavedLibPaths[ulVar] = NULL;
1257 }
1258 }
1259 else
1260 rcExit = errx(9, "out of memory!");
1261 }
1262 if (rcExit == 0)
1263 {
1264 rc = DosSetExtLIBPATH(pchEqual + 1, ulVar);
1265 if (rc)
1266 rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu",
1267 pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc);
1268 }
1269 continue;
1270 }
1271#endif /* KBUILD_OS_OS2 */
1272
1273 /* We differ from kSubmit here and use putenv sematics. */
1274 if (pchEqual)
1275 {
1276 if (pchEqual[1] != '\0')
1277 {
1278 rcExit = kBuiltinOptEnvSet(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1279#ifdef KMK
1280 pChild->environment = papszEnvVars;
1281#endif
1282 }
1283 else
1284 {
1285 char *pszCopy = strdup(pszValue);
1286 if (pszCopy)
1287 {
1288 pszCopy[pchEqual - pszValue] = '\0';
1289 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszCopy);
1290 free(pszCopy);
1291 }
1292 else
1293 rcExit = errx(1, "out of memory!");
1294 }
1295 continue;
1296 }
1297 /* Simple unset. */
1298 chOpt = 'U';
1299 }
1300
1301 /*
1302 * Append or prepend value to and environment variable.
1303 */
1304 if (chOpt == 'A' || chOpt == 'D')
1305 {
1306#ifdef KBUILD_OS_OS2
1307 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1308 || strcmp(pszValue, "ENDLIBPATH") == 0
1309 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1310 rcExit = errx(2, "error: '%s' cannot currently be appended or prepended to. Please use -E/--set for now.", pszValue);
1311 else
1312#endif
1313 if (chOpt == 'A')
1314 rcExit = kBuiltinOptEnvAppend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1315 else
1316 rcExit = kBuiltinOptEnvPrepend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1317 continue;
1318 }
1319
1320 /*
1321 * Unset environment variable.
1322 */
1323 if (chOpt == 'U')
1324 {
1325#ifdef KBUILD_OS_OS2
1326 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1327 || strcmp(pszValue, "ENDLIBPATH") == 0
1328 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1329 rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue);
1330 else
1331#endif
1332 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszValue);
1333 continue;
1334 }
1335
1336 /*
1337 * Zap environment switch?
1338 */
1339 if ( chOpt == 'Z'
1340 || chOpt == 'i' /* GNU env compatibility. */ )
1341 {
1342 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
1343 free(papszEnvVars[iEnvVar]);
1344 papszEnvVars[0] = NULL;
1345 cEnvVars = 0;
1346 continue;
1347 }
1348
1349 /*
1350 * Change directory switch?
1351 */
1352 if (chOpt == 'C')
1353 {
1354 if (pszSavedCwd == NULL)
1355 pszSavedCwd = strdup(szCwd);
1356 if (pszSavedCwd)
1357 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
1358 else
1359 rcExit = err(9, "out of memory!");
1360 continue;
1361 }
1362
1363
1364 /*
1365 * Verbose operation switch?
1366 */
1367 if (chOpt == 'v')
1368 {
1369 cVerbosity++;
1370 continue;
1371 }
1372
1373 /*
1374 * Executable image other than the first argument following '--'.
1375 */
1376 if (chOpt == 'e')
1377 {
1378 pszExecutable = pszValue;
1379 continue;
1380 }
1381
1382 /*
1383 * Okay, it is some file descriptor opearation. Make sure we've got room for it.
1384 */
1385 if (cOrders + 1 < K_ELEMENTS(aOrders))
1386 {
1387 aOrders[cOrders].fdTarget = -1;
1388 aOrders[cOrders].fdSource = -1;
1389 aOrders[cOrders].fOpen = 0;
1390 aOrders[cOrders].fRemoveOnFailure = 0;
1391 aOrders[cOrders].pszFilename = NULL;
1392#ifndef USE_POSIX_SPAWN
1393 aOrders[cOrders].fdSaved = -1;
1394#endif
1395 }
1396 else
1397 {
1398 rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders));
1399 break;
1400 }
1401
1402 if (chOpt == 'c')
1403 {
1404 /*
1405 * Close the specified file descriptor (no stderr/out/in aliases).
1406 */
1407 char *pszTmp;
1408 fd = (int)strtol(pszValue, &pszTmp, 0);
1409 if (pszTmp == pszValue || *pszTmp != '\0')
1410 rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue);
1411 else if (fd < 0)
1412 rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
1413 else
1414 {
1415 aOrders[cOrders].enmOrder = kRedirectOrder_Close;
1416 aOrders[cOrders].fdTarget = fd;
1417 cOrders++;
1418#ifdef USE_POSIX_SPAWN
1419 rcExit = posix_spawn_file_actions_addclose(&FileActions, fd);
1420 if (rcExit != 0)
1421 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1422#endif
1423 }
1424 }
1425 else if (chOpt == 'd')
1426 {
1427 /*
1428 * Duplicate file handle. Value is fdTarget=fdSource
1429 */
1430 char *pszEqual;
1431 fd = (int)strtol(pszValue, &pszEqual, 0);
1432 if (pszEqual == pszValue)
1433 rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue);
1434 else if (fd < 0)
1435 rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
1436 else if (*pszEqual != '=')
1437 rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
1438 else
1439 {
1440 char *pszEnd;
1441 int fdSource = (int)strtol(++pszEqual, &pszEnd, 0);
1442 if (pszEnd == pszEqual || *pszEnd != '\0')
1443 rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue);
1444 else if (fdSource < 0)
1445 rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue);
1446 else
1447 {
1448 aOrders[cOrders].enmOrder = kRedirectOrder_Dup;
1449 aOrders[cOrders].fdTarget = fd;
1450 aOrders[cOrders].fdSource = fdSource;
1451 cOrders++;
1452#ifdef USE_POSIX_SPAWN
1453 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, fd);
1454 if (rcExit != 0)
1455 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1456#endif
1457 }
1458 }
1459 }
1460 else
1461 {
1462 /*
1463 * Open file as a given file descriptor.
1464 */
1465 int fdOpened;
1466 int fOpen;
1467
1468 /* mode */
1469 switch (chOpt)
1470 {
1471 case 'r':
1472 chOpt = *pszArg++;
1473 if (chOpt == '+')
1474 {
1475 fOpen = O_RDWR;
1476 chOpt = *pszArg++;
1477 }
1478 else
1479 fOpen = O_RDONLY;
1480 break;
1481
1482 case 'w':
1483 chOpt = *pszArg++;
1484 if (chOpt == '+')
1485 {
1486 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1487 chOpt = *pszArg++;
1488 }
1489 else
1490 fOpen = O_WRONLY | O_CREAT | O_TRUNC;
1491 aOrders[cOrders].fRemoveOnFailure = 1;
1492 break;
1493
1494 case 'a':
1495 chOpt = *pszArg++;
1496 if (chOpt == '+')
1497 {
1498 fOpen = O_RDWR | O_CREAT | O_APPEND;
1499 chOpt = *pszArg++;
1500 }
1501 else
1502 fOpen = O_WRONLY | O_CREAT | O_APPEND;
1503 break;
1504
1505 case 'i': /* make sure stdin is read-only. */
1506 fOpen = O_RDONLY;
1507 break;
1508
1509 case '+':
1510 rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]);
1511 continue;
1512
1513 default:
1514 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1515 aOrders[cOrders].fRemoveOnFailure = 1;
1516 break;
1517 }
1518
1519 /* binary / text modifiers */
1520 switch (chOpt)
1521 {
1522 case 'b':
1523 chOpt = *pszArg++;
1524 default:
1525#ifdef O_BINARY
1526 fOpen |= O_BINARY;
1527#elif defined(_O_BINARY)
1528 fOpen |= _O_BINARY;
1529#endif
1530 break;
1531
1532 case 't':
1533#ifdef O_TEXT
1534 fOpen |= O_TEXT;
1535#elif defined(_O_TEXT)
1536 fOpen |= _O_TEXT;
1537#endif
1538 chOpt = *pszArg++;
1539 break;
1540
1541 }
1542
1543 /* convert to file descriptor number */
1544 switch (chOpt)
1545 {
1546 case 'i':
1547 fd = 0;
1548 break;
1549
1550 case 'o':
1551 fd = 1;
1552 break;
1553
1554 case 'e':
1555 fd = 2;
1556 break;
1557
1558 case '0':
1559 if (*pszArg == '\0')
1560 {
1561 fd = 0;
1562 break;
1563 }
1564 case '1':
1565 case '2':
1566 case '3':
1567 case '4':
1568 case '5':
1569 case '6':
1570 case '7':
1571 case '8':
1572 case '9':
1573 pszValue = pszArg - 1;
1574 fd = (int)strtol(pszValue, &pszArg, 0);
1575 if (pszArg == pszValue)
1576 rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]);
1577 else if (fd < 0)
1578 rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
1579 else
1580 break;
1581 continue;
1582
1583 /*
1584 * Invalid argument.
1585 */
1586 default:
1587 rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]);
1588 continue;
1589 }
1590
1591 /*
1592 * Check for the filename.
1593 */
1594 if (*pszArg != '\0')
1595 {
1596 if (*pszArg != ':' && *pszArg != '=')
1597 {
1598 rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')",
1599 pszArg, argv[iArg]);
1600 break;
1601 }
1602 pszArg++;
1603 }
1604 else if (++iArg < argc)
1605 pszArg = argv[iArg];
1606 else
1607 {
1608 rcExit = errx(2, "syntax error: missing filename argument.");
1609 break;
1610 }
1611
1612 /*
1613 * Open the file. We could've used posix_spawn_file_actions_addopen here,
1614 * but that means complicated error reporting. So, since we need to do
1615 * this for windows anyway, just do it the same way everywhere.
1616 */
1617 fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders,
1618 aOrders[cOrders].fRemoveOnFailure, fd);
1619 if (fdOpened >= 0)
1620 {
1621 aOrders[cOrders].enmOrder = kRedirectOrder_Open;
1622 aOrders[cOrders].fdTarget = fd;
1623 aOrders[cOrders].fdSource = fdOpened;
1624 aOrders[cOrders].fOpen = fOpen;
1625 aOrders[cOrders].pszFilename = pszArg;
1626 cOrders++;
1627
1628#ifdef USE_POSIX_SPAWN
1629 if (fdOpened != fd)
1630 {
1631 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd);
1632 if (rcExit != 0)
1633 rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s",
1634 fdOpened, fd, pszArg, strerror(rcExit));
1635 }
1636#endif
1637 }
1638 else
1639 rcExit = 9;
1640 }
1641 }
1642 else
1643 {
1644 errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]);
1645 rcExit = usage(stderr, argv[0]);
1646 }
1647 }
1648 if (!pszExecutable)
1649 pszExecutable = argv[iArg];
1650
1651 /*
1652 * Make sure there's something to execute.
1653 */
1654 if (rcExit == 0 && iArg < argc)
1655 {
1656 /*
1657 * Do the spawning in a separate function (main is far to large as it is by now).
1658 */
1659 rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnvVars, szCwd, pszSavedCwd,
1660#ifdef USE_POSIX_SPAWN
1661 cOrders, aOrders, &FileActions, cVerbosity,
1662#else
1663 cOrders, aOrders, cVerbosity,
1664#endif
1665#ifdef KMK
1666 pPidSpawned,
1667#endif
1668 &fChildExitCode);
1669 }
1670 else if (rcExit == 0)
1671 {
1672 errx(2, "syntax error: nothing to execute!");
1673 rcExit = usage(stderr, argv[0]);
1674 }
1675 /* Help and version sets rcExit to -1. Change it to zero. */
1676 else if (rcExit == -1)
1677 rcExit = 0;
1678
1679 /*
1680 * Cleanup.
1681 */
1682 if (pszSavedCwd)
1683 free(pszSavedCwd);
1684 kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode);
1685#ifdef USE_POSIX_SPAWN
1686 posix_spawn_file_actions_destroy(&FileActions);
1687#endif
1688#ifndef KMK
1689 iEnvVar = cEnvVars;
1690 while (iEnvVar-- > 0)
1691 free(papszEnvVars[iEnvVar]);
1692 free(papszEnvVars);
1693#endif
1694#ifdef KBUILD_OS_OS2
1695 for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++)
1696 if (apszSavedLibPaths[ulLibPath] != NULL)
1697 {
1698 APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath);
1699 if (rc != 0)
1700 warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!",
1701 apszSavedLibPaths[ulLibPath], ulLibPath, rc);
1702 free(apszSavedLibPaths[ulLibPath]);
1703 }
1704#endif
1705
1706 return rcExit;
1707}
1708
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette