VirtualBox

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

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

kmk,redirect: bootstrap and build fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.6 KB
Line 
1/* $Id: redirect.c 3029 2017-04-13 13:32:42Z 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",
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",
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",
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", 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", 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",
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;
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 = _spawnve(_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 rcExit = (int)_spawnvpe(_P_WAIT, pszExecutable, papszArgs, papszEnvVars);
935 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
936 if (rcExit != -1 || errno == 0)
937 {
938 *pfIsChildExitCode = K_TRUE;
939 if (cVerbosity > 0)
940 warnx("debug: exit code: %d", rcExit);
941 }
942 else
943 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
944
945# else
946 pid_t pidChild = 0;
947 rcExit = posix_spawnp(&pidChild, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
948 if (rcExit == 0)
949 {
950 *pfIsChildExitCode = K_TRUE;
951 if (cVerbosity > 0)
952 warnx("debug: spawned %d", pidChild);
953
954 /* Wait for the child. */
955 for (;;)
956 {
957 pid_t pid = waitpid(pidChild, &rcExit, 0 /*block*/);
958 if (pid == pidChild)
959 {
960 if (cVerbosity > 0)
961 warnx("debug: %d exit code: %d", pidChild, rcExit);
962 break;
963 }
964 if ( errno != EINTR
965# ifdef ERESTART
966 && errno != ERESTART
967# endif
968 )
969 {
970 rcExit = err(11, "waitpid failed");
971 kill(pidChild, SIGKILL);
972 break;
973 }
974 }
975 }
976 else
977 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
978
979# endif
980#endif /* !KMK */
981 }
982 }
983
984 /*
985 * Restore the current directory.
986 */
987 if (pszSavedCwd)
988 {
989 if (chdir(pszSavedCwd) < 0)
990 warn("Failed to restore directory to '%s'", pszSavedCwd);
991 }
992 }
993#ifdef _MSC_VER
994 else
995 rcExit = errx(9, "quite_argv failed: %u", rcExit);
996
997 /* Restore the original argv strings, freeing the quote_argv replacements. */
998 i = cArgs;
999 while (i-- > 0)
1000 if (papszArgs[i] != papszArgsOriginal[i])
1001 free(papszArgs[i]);
1002 free(papszArgs);
1003#endif
1004 return rcExit;
1005}
1006
1007
1008/**
1009 * The function that does almost everything here... ugly.
1010 */
1011#ifdef KMK
1012int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
1013#else
1014int main(int argc, char **argv, char **envp)
1015#endif
1016{
1017 int rcExit = 0;
1018 KBOOL fChildExitCode = K_FALSE;
1019#ifdef USE_POSIX_SPAWN
1020 posix_spawn_file_actions_t FileActions;
1021#endif
1022 unsigned cOrders = 0;
1023 REDIRECTORDERS aOrders[32];
1024
1025 int iArg;
1026 const char *pszExecutable = NULL;
1027 char **papszEnvVars = NULL;
1028 unsigned cAllocatedEnvVars;
1029 unsigned iEnvVar;
1030 unsigned cEnvVars;
1031 int fWatcomBrainDamage = 0;
1032 int cVerbosity = 0;
1033 char *pszSavedCwd = NULL;
1034 size_t const cbCwdBuf = GET_PATH_MAX;
1035 PATH_VAR(szCwd);
1036#ifdef KBUILD_OS_OS2
1037 ULONG ulLibPath;
1038 char *apszSavedLibPaths[LIBPATHSTRICT + 1] = { NULL, NULL, NULL, NULL };
1039#endif
1040
1041
1042 g_progname = argv[0];
1043
1044 if (argc <= 1)
1045 return usage(stderr, argv[0]);
1046
1047 /*
1048 * Create default program environment.
1049 */
1050#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
1051 if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
1052#else
1053 if (getcwd(szCwd, cbCwdBuf) != NULL)
1054#endif
1055 { /* likely */ }
1056 else
1057 return err(9, "getcwd failed");
1058
1059#if defined(KMK)
1060 /* We get it from kmk and just count it: */
1061 papszEnvVars = pChild->environment;
1062 if (!papszEnvVars)
1063 pChild->environment = papszEnvVars = target_environment(pChild->file);
1064 cEnvVars = 0;
1065 while (papszEnvVars[cEnvVars] != NULL)
1066 cEnvVars++;
1067 cAllocatedEnvVars = cEnvVars;
1068#else
1069 /* We make a copy and we manage ourselves: */
1070 cEnvVars = 0;
1071 while (envp[cEnvVars] != NULL)
1072 cEnvVars++;
1073
1074 cAllocatedEnvVars = cEnvVars + 4;
1075 papszEnvVars = malloc((cAllocatedEnvVars + 1) * sizeof(papszEnvVars));
1076 if (!papszEnvVars)
1077 return errx(9, "out of memory!");
1078
1079 iEnvVar = cEnvVars;
1080 papszEnvVars[iEnvVar] = NULL;
1081 while (iEnvVar-- > 0)
1082 {
1083 papszEnvVars[iEnvVar] = strdup(envp[iEnvVar]);
1084 if (!papszEnvVars[iEnvVar])
1085 {
1086 while (iEnvVar-- > 0)
1087 free(papszEnvVars[iEnvVar]);
1088 free(papszEnvVars);
1089 return errx(9, "out of memory!");
1090 }
1091 }
1092#endif
1093
1094#ifdef USE_POSIX_SPAWN
1095 /*
1096 * Init posix attributes.
1097 */
1098 rcExit = posix_spawn_file_actions_init(&FileActions);
1099 if (rcExit != 0)
1100 rcExit = errx(9, "posix_spawn_file_actions_init failed: %s", strerror(rcExit));
1101#endif
1102
1103 /*
1104 * Parse arguments.
1105 */
1106 for (iArg = 1; rcExit == 0 && iArg < argc; iArg++)
1107 {
1108 char *pszArg = argv[iArg];
1109 if (*pszArg == '-')
1110 {
1111 int fd;
1112 char chOpt;
1113 const char *pszValue;
1114
1115 chOpt = *++pszArg;
1116 pszArg++;
1117 if (chOpt == '-')
1118 {
1119 /* '--' indicates where the bits to execute start. */
1120 if (*pszArg == '\0')
1121 {
1122 iArg++;
1123 break;
1124 }
1125
1126 if ( strcmp(pszArg, "wcc-brain-damage") == 0
1127 || strcmp(pszArg, "watcom-brain-damage") == 0)
1128 {
1129 fWatcomBrainDamage = 1;
1130 continue;
1131 }
1132
1133 /* convert to short. */
1134 if (strcmp(pszArg, "help") == 0)
1135 chOpt = 'h';
1136 else if (strcmp(pszArg, "version") == 0)
1137 chOpt = 'V';
1138 else if ( strcmp(pszArg, "set") == 0
1139 || strcmp(pszArg, "env") == 0)
1140 chOpt = 'E';
1141 else if (strcmp(pszArg, "unset") == 0)
1142 chOpt = 'U';
1143 else if ( strcmp(pszArg, "zap-env") == 0
1144 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
1145 chOpt = 'Z';
1146 else if (strcmp(pszArg, "chdir") == 0)
1147 chOpt = 'C';
1148 else if (strcmp(pszArg, "close") == 0)
1149 chOpt = 'c';
1150 else if (strcmp(pszArg, "verbose") == 0)
1151 chOpt = 'v';
1152 else
1153 {
1154 errx(2, "Unknown option: '%s'", pszArg - 2);
1155 rcExit = usage(stderr, argv[0]);
1156 break;
1157 }
1158 pszArg = "";
1159 }
1160
1161 /*
1162 * Deal with the obligatory help and version switches first to get them out of the way.
1163 */
1164 if (chOpt == 'h')
1165 {
1166 usage(stdout, argv[0]);
1167 rcExit = -1;
1168 break;
1169 }
1170 if (chOpt == 'V')
1171 {
1172 kbuild_version(argv[0]);
1173 rcExit = -1;
1174 break;
1175 }
1176
1177 /*
1178 * Get option value first, if the option takes one.
1179 */
1180 if ( chOpt == 'E'
1181 || chOpt == 'U'
1182 || chOpt == 'C'
1183 || chOpt == 'c'
1184 || chOpt == 'd'
1185 || chOpt == 'e')
1186 {
1187 if (*pszArg != '\0')
1188 pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
1189 else if (++iArg < argc)
1190 pszValue = argv[iArg];
1191 else
1192 {
1193 errx(2, "syntax error: Option -%c requires a value!", chOpt);
1194 rcExit = usage(stderr, argv[0]);
1195 break;
1196 }
1197 }
1198 else
1199 pszValue = NULL;
1200
1201 /*
1202 * Environment switch?
1203 */
1204 if (chOpt == 'E')
1205 {
1206 const char *pchEqual = strchr(pszValue, '=');
1207#ifdef KBUILD_OS_OS2
1208 if ( strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0
1209 || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0
1210 || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0)
1211 {
1212 ULONG ulVar = *pszValue == 'B' ? BEGIN_LIBPATH
1213 : *pszValue == 'E' ? END_LIBPATH
1214 : LIBPATHSTRICT;
1215 APIRET rc;
1216 if (apszSavedLibPaths[ulVar] == NULL)
1217 {
1218 /* The max length is supposed to be 1024 bytes. */
1219 apszSavedLibPaths[ulVar] = calloc(1024 * 2);
1220 if (apszSavedLibPaths[ulVar])
1221 {
1222 rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar);
1223 if (rc)
1224 {
1225 rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc);
1226 free(apszSavedLibPaths[ulVar]);
1227 apszSavedLibPaths[ulVar] = NULL;
1228 }
1229 }
1230 else
1231 rcExit = errx(9, "out of memory!");
1232 }
1233 if (rcExit == 0)
1234 {
1235 rc = DosSetExtLIBPATH(pchEqual + 1, ulVar);
1236 if (rc)
1237 rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu",
1238 pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc);
1239 }
1240 continue;
1241 }
1242#endif /* KBUILD_OS_OS2 */
1243
1244 /* We differ from kSubmit here and use putenv sematics. */
1245 if (pchEqual)
1246 {
1247 if (pchEqual[1] != '\0')
1248 {
1249 rcExit = kBuiltinOptEnvSet(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1250#ifdef KMK
1251 pChild->environment = papszEnvVars;
1252#endif
1253 }
1254 else
1255 {
1256 char *pszCopy = strdup(pszValue);
1257 if (pszCopy)
1258 {
1259 pszCopy[pchEqual - pszValue] = '\0';
1260 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszCopy);
1261 free(pszCopy);
1262 }
1263 else
1264 rcExit = errx(1, "out of memory!");
1265 }
1266 continue;
1267 }
1268 /* Simple unset. */
1269 chOpt = 'U';
1270 }
1271
1272 /*
1273 * Unset environment variable.
1274 */
1275 if (chOpt == 'U')
1276 {
1277#ifdef KBUILD_OS_OS2
1278 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1279 || strcmp(pszValue, "ENDLIBPATH") == 0
1280 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1281 rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue);
1282 else
1283#endif
1284 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszValue);
1285 continue;
1286 }
1287
1288 /*
1289 * Zap environment switch?
1290 */
1291 if ( chOpt == 'Z'
1292 || chOpt == 'i' /* GNU env compatibility. */ )
1293 {
1294 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
1295 free(papszEnvVars[iEnvVar]);
1296 papszEnvVars[0] = NULL;
1297 cEnvVars = 0;
1298 continue;
1299 }
1300
1301 /*
1302 * Change directory switch?
1303 */
1304 if (chOpt == 'C')
1305 {
1306 if (pszSavedCwd == NULL)
1307 pszSavedCwd = strdup(szCwd);
1308 if (pszSavedCwd)
1309 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
1310 else
1311 rcExit = err(9, "out of memory!");
1312 continue;
1313 }
1314
1315
1316 /*
1317 * Verbose operation switch?
1318 */
1319 if (chOpt == 'v')
1320 {
1321 cVerbosity++;
1322 continue;
1323 }
1324
1325 /*
1326 * Executable image other than the first argument following '--'.
1327 */
1328 if (chOpt == 'e')
1329 {
1330 pszExecutable = pszValue;
1331 continue;
1332 }
1333
1334 /*
1335 * Okay, it is some file descriptor opearation. Make sure we've got room for it.
1336 */
1337 if (cOrders + 1 < K_ELEMENTS(aOrders))
1338 {
1339 aOrders[cOrders].fdTarget = -1;
1340 aOrders[cOrders].fdSource = -1;
1341 aOrders[cOrders].fOpen = 0;
1342 aOrders[cOrders].fRemoveOnFailure = 0;
1343 aOrders[cOrders].pszFilename = NULL;
1344#ifndef USE_POSIX_SPAWN
1345 aOrders[cOrders].fdSaved = -1;
1346#endif
1347 }
1348 else
1349 {
1350 rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders));
1351 break;
1352 }
1353
1354 if (chOpt == 'c')
1355 {
1356 /*
1357 * Close the specified file descriptor (no stderr/out/in aliases).
1358 */
1359 char *pszTmp;
1360 fd = (int)strtol(pszValue, &pszTmp, 0);
1361 if (pszTmp == pszValue || *pszTmp != '\0')
1362 rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue);
1363 else if (fd < 0)
1364 rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
1365 else
1366 {
1367 aOrders[cOrders].enmOrder = kRedirectOrder_Close;
1368 aOrders[cOrders].fdTarget = fd;
1369 cOrders++;
1370#ifdef USE_POSIX_SPAWN
1371 rcExit = posix_spawn_file_actions_addclose(&FileActions, fd);
1372 if (rcExit != 0)
1373 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1374#endif
1375 }
1376 }
1377 else if (chOpt == 'd')
1378 {
1379 /*
1380 * Duplicate file handle. Value is fdTarget=fdSource
1381 */
1382 char *pszEqual;
1383 fd = (int)strtol(pszValue, &pszEqual, 0);
1384 if (pszEqual == pszValue)
1385 rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue);
1386 else if (fd < 0)
1387 rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
1388 else if (*pszEqual != '=')
1389 rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
1390 else
1391 {
1392 char *pszEnd;
1393 int fdSource = (int)strtol(++pszEqual, &pszEnd, 0);
1394 if (pszEnd == pszEqual || *pszEnd != '\0')
1395 rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue);
1396 else if (fdSource < 0)
1397 rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue);
1398 else
1399 {
1400 aOrders[cOrders].enmOrder = kRedirectOrder_Dup;
1401 aOrders[cOrders].fdTarget = fd;
1402 aOrders[cOrders].fdSource = fdSource;
1403 cOrders++;
1404#ifdef USE_POSIX_SPAWN
1405 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, 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 }
1412 else
1413 {
1414 /*
1415 * Open file as a given file descriptor.
1416 */
1417 int fdOpened;
1418 int fOpen;
1419
1420 /* mode */
1421 switch (chOpt)
1422 {
1423 case 'r':
1424 chOpt = *pszArg++;
1425 if (chOpt == '+')
1426 {
1427 fOpen = O_RDWR;
1428 chOpt = *pszArg++;
1429 }
1430 else
1431 fOpen = O_RDONLY;
1432 break;
1433
1434 case 'w':
1435 chOpt = *pszArg++;
1436 if (chOpt == '+')
1437 {
1438 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1439 chOpt = *pszArg++;
1440 }
1441 else
1442 fOpen = O_WRONLY | O_CREAT | O_TRUNC;
1443 aOrders[cOrders].fRemoveOnFailure = 1;
1444 break;
1445
1446 case 'a':
1447 chOpt = *pszArg++;
1448 if (chOpt == '+')
1449 {
1450 fOpen = O_RDWR | O_CREAT | O_APPEND;
1451 chOpt = *pszArg++;
1452 }
1453 else
1454 fOpen = O_WRONLY | O_CREAT | O_APPEND;
1455 break;
1456
1457 case 'i': /* make sure stdin is read-only. */
1458 fOpen = O_RDONLY;
1459 break;
1460
1461 case '+':
1462 rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]);
1463 continue;
1464
1465 default:
1466 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1467 aOrders[cOrders].fRemoveOnFailure = 1;
1468 break;
1469 }
1470
1471 /* binary / text modifiers */
1472 switch (chOpt)
1473 {
1474 case 'b':
1475 chOpt = *pszArg++;
1476 default:
1477#ifdef O_BINARY
1478 fOpen |= O_BINARY;
1479#elif defined(_O_BINARY)
1480 fOpen |= _O_BINARY;
1481#endif
1482 break;
1483
1484 case 't':
1485#ifdef O_TEXT
1486 fOpen |= O_TEXT;
1487#elif defined(_O_TEXT)
1488 fOpen |= _O_TEXT;
1489#endif
1490 chOpt = *pszArg++;
1491 break;
1492
1493 }
1494
1495 /* convert to file descriptor number */
1496 switch (chOpt)
1497 {
1498 case 'i':
1499 fd = 0;
1500 break;
1501
1502 case 'o':
1503 fd = 1;
1504 break;
1505
1506 case 'e':
1507 fd = 2;
1508 break;
1509
1510 case '0':
1511 if (*pszArg == '\0')
1512 {
1513 fd = 0;
1514 break;
1515 }
1516 case '1':
1517 case '2':
1518 case '3':
1519 case '4':
1520 case '5':
1521 case '6':
1522 case '7':
1523 case '8':
1524 case '9':
1525 pszValue = pszArg - 1;
1526 fd = (int)strtol(pszValue, &pszArg, 0);
1527 if (pszArg == pszValue)
1528 rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]);
1529 else if (fd < 0)
1530 rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
1531 else
1532 break;
1533 continue;
1534
1535 /*
1536 * Invalid argument.
1537 */
1538 default:
1539 rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]);
1540 continue;
1541 }
1542
1543 /*
1544 * Check for the filename.
1545 */
1546 if (*pszArg != '\0')
1547 {
1548 if (*pszArg != ':' && *pszArg != '=')
1549 {
1550 rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')",
1551 pszArg, argv[iArg]);
1552 break;
1553 }
1554 pszArg++;
1555 }
1556 else if (++iArg < argc)
1557 pszArg = argv[iArg];
1558 else
1559 {
1560 rcExit = errx(2, "syntax error: missing filename argument.");
1561 break;
1562 }
1563
1564 /*
1565 * Open the file. We could've used posix_spawn_file_actions_addopen here,
1566 * but that means complicated error reporting. So, since we need to do
1567 * this for windows anyway, just do it the same way everywhere.
1568 */
1569 fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders,
1570 aOrders[cOrders].fRemoveOnFailure, fd);
1571 if (fdOpened >= 0)
1572 {
1573 aOrders[cOrders].enmOrder = kRedirectOrder_Open;
1574 aOrders[cOrders].fdTarget = fd;
1575 aOrders[cOrders].fdSource = fdOpened;
1576 aOrders[cOrders].fOpen = fOpen;
1577 aOrders[cOrders].pszFilename = pszArg;
1578 cOrders++;
1579
1580#ifdef USE_POSIX_SPAWN
1581 if (fdOpened != fd)
1582 {
1583 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd);
1584 if (rcExit != 0)
1585 rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s",
1586 fdOpened, fd, pszArg, strerror(rcExit));
1587 }
1588#endif
1589 }
1590 else
1591 rcExit = 9;
1592 }
1593 }
1594 else
1595 {
1596 errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]);
1597 rcExit = usage(stderr, argv[0]);
1598 }
1599 }
1600 if (!pszExecutable)
1601 pszExecutable = argv[iArg];
1602
1603 /*
1604 * Make sure there's something to execute.
1605 */
1606 if (rcExit == 0 && iArg < argc)
1607 {
1608 /*
1609 * Do the spawning in a separate function (main is far to large as it is by now).
1610 */
1611 rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnvVars, szCwd, pszSavedCwd,
1612#ifdef USE_POSIX_SPAWN
1613 cOrders, aOrders, &FileActions, cVerbosity,
1614#else
1615 cOrders, aOrders, cVerbosity,
1616#endif
1617#ifdef KMK
1618 pPidSpawned,
1619#endif
1620 &fChildExitCode);
1621 }
1622 else if (rcExit == 0)
1623 {
1624 errx(2, "syntax error: nothing to execute!");
1625 rcExit = usage(stderr, argv[0]);
1626 }
1627 /* Help and version sets rcExit to -1. Change it to zero. */
1628 else if (rcExit == -1)
1629 rcExit = 0;
1630
1631 /*
1632 * Cleanup.
1633 */
1634 if (pszSavedCwd)
1635 free(pszSavedCwd);
1636 kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode);
1637#ifdef USE_POSIX_SPAWN
1638 posix_spawn_file_actions_destroy(&FileActions);
1639#endif
1640#ifndef KMK
1641 iEnvVar = cEnvVars;
1642 while (iEnvVar-- > 0)
1643 free(papszEnvVars[iEnvVar]);
1644 free(papszEnvVars);
1645#endif
1646#ifdef KBUILD_OS_OS2
1647 for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++)
1648 if (apszSavedLibPaths[ulLibPath] != NULL)
1649 {
1650 APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath);
1651 if (rc != 0)
1652 warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!",
1653 apszSavedLibPaths[ulLibPath], ulLibPath, rc);
1654 free(apszSavedLibPaths[ulLibPath])
1655 }
1656#endif
1657
1658 return rcExit;
1659}
1660
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