VirtualBox

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

Last change on this file since 3064 was 3056, checked in by bird, 7 years ago

redirect: Added trick for skipping fork+exec when dealing with nested kmk_[builtin]_redirect statements. This works around problems when generating tstAsmStructsHC.h using a VBOX_NM macro that already uses kmk_builtin_redirect.

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