VirtualBox

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

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

kmk_redirect,kmk_kSubmit: Added --append <var=value> and --prepend <var=value> options for modifying the environment (e.g. PATH).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.7 KB
Line 
1/* $Id: redirect.c 3039 2017-05-10 10:55:51Z 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 = 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 = _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, "append") == 0)
1142 chOpt = 'A';
1143 else if (strcmp(pszArg, "prepend") == 0)
1144 chOpt = 'D';
1145 else if (strcmp(pszArg, "unset") == 0)
1146 chOpt = 'U';
1147 else if ( strcmp(pszArg, "zap-env") == 0
1148 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
1149 chOpt = 'Z';
1150 else if (strcmp(pszArg, "chdir") == 0)
1151 chOpt = 'C';
1152 else if (strcmp(pszArg, "close") == 0)
1153 chOpt = 'c';
1154 else if (strcmp(pszArg, "verbose") == 0)
1155 chOpt = 'v';
1156 else
1157 {
1158 errx(2, "Unknown option: '%s'", pszArg - 2);
1159 rcExit = usage(stderr, argv[0]);
1160 break;
1161 }
1162 pszArg = "";
1163 }
1164
1165 /*
1166 * Deal with the obligatory help and version switches first to get them out of the way.
1167 */
1168 if (chOpt == 'h')
1169 {
1170 usage(stdout, argv[0]);
1171 rcExit = -1;
1172 break;
1173 }
1174 if (chOpt == 'V')
1175 {
1176 kbuild_version(argv[0]);
1177 rcExit = -1;
1178 break;
1179 }
1180
1181 /*
1182 * Get option value first, if the option takes one.
1183 */
1184 if ( chOpt == 'E'
1185 || chOpt == 'A'
1186 || chOpt == 'D'
1187 || chOpt == 'U'
1188 || chOpt == 'C'
1189 || chOpt == 'c'
1190 || chOpt == 'd'
1191 || chOpt == 'e')
1192 {
1193 if (*pszArg != '\0')
1194 pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
1195 else if (++iArg < argc)
1196 pszValue = argv[iArg];
1197 else
1198 {
1199 errx(2, "syntax error: Option -%c requires a value!", chOpt);
1200 rcExit = usage(stderr, argv[0]);
1201 break;
1202 }
1203 }
1204 else
1205 pszValue = NULL;
1206
1207 /*
1208 * Environment switch?
1209 */
1210 if (chOpt == 'E')
1211 {
1212 const char *pchEqual = strchr(pszValue, '=');
1213#ifdef KBUILD_OS_OS2
1214 if ( strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0
1215 || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0
1216 || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0)
1217 {
1218 ULONG ulVar = *pszValue == 'B' ? BEGIN_LIBPATH
1219 : *pszValue == 'E' ? END_LIBPATH
1220 : LIBPATHSTRICT;
1221 APIRET rc;
1222 if (apszSavedLibPaths[ulVar] == NULL)
1223 {
1224 /* The max length is supposed to be 1024 bytes. */
1225 apszSavedLibPaths[ulVar] = calloc(1024 * 2);
1226 if (apszSavedLibPaths[ulVar])
1227 {
1228 rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar);
1229 if (rc)
1230 {
1231 rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc);
1232 free(apszSavedLibPaths[ulVar]);
1233 apszSavedLibPaths[ulVar] = NULL;
1234 }
1235 }
1236 else
1237 rcExit = errx(9, "out of memory!");
1238 }
1239 if (rcExit == 0)
1240 {
1241 rc = DosSetExtLIBPATH(pchEqual + 1, ulVar);
1242 if (rc)
1243 rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu",
1244 pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc);
1245 }
1246 continue;
1247 }
1248#endif /* KBUILD_OS_OS2 */
1249
1250 /* We differ from kSubmit here and use putenv sematics. */
1251 if (pchEqual)
1252 {
1253 if (pchEqual[1] != '\0')
1254 {
1255 rcExit = kBuiltinOptEnvSet(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1256#ifdef KMK
1257 pChild->environment = papszEnvVars;
1258#endif
1259 }
1260 else
1261 {
1262 char *pszCopy = strdup(pszValue);
1263 if (pszCopy)
1264 {
1265 pszCopy[pchEqual - pszValue] = '\0';
1266 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszCopy);
1267 free(pszCopy);
1268 }
1269 else
1270 rcExit = errx(1, "out of memory!");
1271 }
1272 continue;
1273 }
1274 /* Simple unset. */
1275 chOpt = 'U';
1276 }
1277
1278 /*
1279 * Append or prepend value to and environment variable.
1280 */
1281 if (chOpt == 'A' || chOpt == 'D')
1282 {
1283#ifdef KBUILD_OS_OS2
1284 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1285 || strcmp(pszValue, "ENDLIBPATH") == 0
1286 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1287 rcExit = errx(2, "error: '%s' cannot currently be appended or prepended to. Please use -E/--set for now.", pszValue);
1288 else
1289#endif
1290 if (chOpt == 'A')
1291 rcExit = kBuiltinOptEnvAppend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1292 else
1293 rcExit = kBuiltinOptEnvPrepend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1294 continue;
1295 }
1296
1297 /*
1298 * Unset environment variable.
1299 */
1300 if (chOpt == 'U')
1301 {
1302#ifdef KBUILD_OS_OS2
1303 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1304 || strcmp(pszValue, "ENDLIBPATH") == 0
1305 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1306 rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue);
1307 else
1308#endif
1309 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszValue);
1310 continue;
1311 }
1312
1313 /*
1314 * Zap environment switch?
1315 */
1316 if ( chOpt == 'Z'
1317 || chOpt == 'i' /* GNU env compatibility. */ )
1318 {
1319 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
1320 free(papszEnvVars[iEnvVar]);
1321 papszEnvVars[0] = NULL;
1322 cEnvVars = 0;
1323 continue;
1324 }
1325
1326 /*
1327 * Change directory switch?
1328 */
1329 if (chOpt == 'C')
1330 {
1331 if (pszSavedCwd == NULL)
1332 pszSavedCwd = strdup(szCwd);
1333 if (pszSavedCwd)
1334 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
1335 else
1336 rcExit = err(9, "out of memory!");
1337 continue;
1338 }
1339
1340
1341 /*
1342 * Verbose operation switch?
1343 */
1344 if (chOpt == 'v')
1345 {
1346 cVerbosity++;
1347 continue;
1348 }
1349
1350 /*
1351 * Executable image other than the first argument following '--'.
1352 */
1353 if (chOpt == 'e')
1354 {
1355 pszExecutable = pszValue;
1356 continue;
1357 }
1358
1359 /*
1360 * Okay, it is some file descriptor opearation. Make sure we've got room for it.
1361 */
1362 if (cOrders + 1 < K_ELEMENTS(aOrders))
1363 {
1364 aOrders[cOrders].fdTarget = -1;
1365 aOrders[cOrders].fdSource = -1;
1366 aOrders[cOrders].fOpen = 0;
1367 aOrders[cOrders].fRemoveOnFailure = 0;
1368 aOrders[cOrders].pszFilename = NULL;
1369#ifndef USE_POSIX_SPAWN
1370 aOrders[cOrders].fdSaved = -1;
1371#endif
1372 }
1373 else
1374 {
1375 rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders));
1376 break;
1377 }
1378
1379 if (chOpt == 'c')
1380 {
1381 /*
1382 * Close the specified file descriptor (no stderr/out/in aliases).
1383 */
1384 char *pszTmp;
1385 fd = (int)strtol(pszValue, &pszTmp, 0);
1386 if (pszTmp == pszValue || *pszTmp != '\0')
1387 rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue);
1388 else if (fd < 0)
1389 rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
1390 else
1391 {
1392 aOrders[cOrders].enmOrder = kRedirectOrder_Close;
1393 aOrders[cOrders].fdTarget = fd;
1394 cOrders++;
1395#ifdef USE_POSIX_SPAWN
1396 rcExit = posix_spawn_file_actions_addclose(&FileActions, fd);
1397 if (rcExit != 0)
1398 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1399#endif
1400 }
1401 }
1402 else if (chOpt == 'd')
1403 {
1404 /*
1405 * Duplicate file handle. Value is fdTarget=fdSource
1406 */
1407 char *pszEqual;
1408 fd = (int)strtol(pszValue, &pszEqual, 0);
1409 if (pszEqual == pszValue)
1410 rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue);
1411 else if (fd < 0)
1412 rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
1413 else if (*pszEqual != '=')
1414 rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
1415 else
1416 {
1417 char *pszEnd;
1418 int fdSource = (int)strtol(++pszEqual, &pszEnd, 0);
1419 if (pszEnd == pszEqual || *pszEnd != '\0')
1420 rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue);
1421 else if (fdSource < 0)
1422 rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue);
1423 else
1424 {
1425 aOrders[cOrders].enmOrder = kRedirectOrder_Dup;
1426 aOrders[cOrders].fdTarget = fd;
1427 aOrders[cOrders].fdSource = fdSource;
1428 cOrders++;
1429#ifdef USE_POSIX_SPAWN
1430 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, fd);
1431 if (rcExit != 0)
1432 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1433#endif
1434 }
1435 }
1436 }
1437 else
1438 {
1439 /*
1440 * Open file as a given file descriptor.
1441 */
1442 int fdOpened;
1443 int fOpen;
1444
1445 /* mode */
1446 switch (chOpt)
1447 {
1448 case 'r':
1449 chOpt = *pszArg++;
1450 if (chOpt == '+')
1451 {
1452 fOpen = O_RDWR;
1453 chOpt = *pszArg++;
1454 }
1455 else
1456 fOpen = O_RDONLY;
1457 break;
1458
1459 case 'w':
1460 chOpt = *pszArg++;
1461 if (chOpt == '+')
1462 {
1463 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1464 chOpt = *pszArg++;
1465 }
1466 else
1467 fOpen = O_WRONLY | O_CREAT | O_TRUNC;
1468 aOrders[cOrders].fRemoveOnFailure = 1;
1469 break;
1470
1471 case 'a':
1472 chOpt = *pszArg++;
1473 if (chOpt == '+')
1474 {
1475 fOpen = O_RDWR | O_CREAT | O_APPEND;
1476 chOpt = *pszArg++;
1477 }
1478 else
1479 fOpen = O_WRONLY | O_CREAT | O_APPEND;
1480 break;
1481
1482 case 'i': /* make sure stdin is read-only. */
1483 fOpen = O_RDONLY;
1484 break;
1485
1486 case '+':
1487 rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]);
1488 continue;
1489
1490 default:
1491 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1492 aOrders[cOrders].fRemoveOnFailure = 1;
1493 break;
1494 }
1495
1496 /* binary / text modifiers */
1497 switch (chOpt)
1498 {
1499 case 'b':
1500 chOpt = *pszArg++;
1501 default:
1502#ifdef O_BINARY
1503 fOpen |= O_BINARY;
1504#elif defined(_O_BINARY)
1505 fOpen |= _O_BINARY;
1506#endif
1507 break;
1508
1509 case 't':
1510#ifdef O_TEXT
1511 fOpen |= O_TEXT;
1512#elif defined(_O_TEXT)
1513 fOpen |= _O_TEXT;
1514#endif
1515 chOpt = *pszArg++;
1516 break;
1517
1518 }
1519
1520 /* convert to file descriptor number */
1521 switch (chOpt)
1522 {
1523 case 'i':
1524 fd = 0;
1525 break;
1526
1527 case 'o':
1528 fd = 1;
1529 break;
1530
1531 case 'e':
1532 fd = 2;
1533 break;
1534
1535 case '0':
1536 if (*pszArg == '\0')
1537 {
1538 fd = 0;
1539 break;
1540 }
1541 case '1':
1542 case '2':
1543 case '3':
1544 case '4':
1545 case '5':
1546 case '6':
1547 case '7':
1548 case '8':
1549 case '9':
1550 pszValue = pszArg - 1;
1551 fd = (int)strtol(pszValue, &pszArg, 0);
1552 if (pszArg == pszValue)
1553 rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]);
1554 else if (fd < 0)
1555 rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
1556 else
1557 break;
1558 continue;
1559
1560 /*
1561 * Invalid argument.
1562 */
1563 default:
1564 rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]);
1565 continue;
1566 }
1567
1568 /*
1569 * Check for the filename.
1570 */
1571 if (*pszArg != '\0')
1572 {
1573 if (*pszArg != ':' && *pszArg != '=')
1574 {
1575 rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')",
1576 pszArg, argv[iArg]);
1577 break;
1578 }
1579 pszArg++;
1580 }
1581 else if (++iArg < argc)
1582 pszArg = argv[iArg];
1583 else
1584 {
1585 rcExit = errx(2, "syntax error: missing filename argument.");
1586 break;
1587 }
1588
1589 /*
1590 * Open the file. We could've used posix_spawn_file_actions_addopen here,
1591 * but that means complicated error reporting. So, since we need to do
1592 * this for windows anyway, just do it the same way everywhere.
1593 */
1594 fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders,
1595 aOrders[cOrders].fRemoveOnFailure, fd);
1596 if (fdOpened >= 0)
1597 {
1598 aOrders[cOrders].enmOrder = kRedirectOrder_Open;
1599 aOrders[cOrders].fdTarget = fd;
1600 aOrders[cOrders].fdSource = fdOpened;
1601 aOrders[cOrders].fOpen = fOpen;
1602 aOrders[cOrders].pszFilename = pszArg;
1603 cOrders++;
1604
1605#ifdef USE_POSIX_SPAWN
1606 if (fdOpened != fd)
1607 {
1608 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd);
1609 if (rcExit != 0)
1610 rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s",
1611 fdOpened, fd, pszArg, strerror(rcExit));
1612 }
1613#endif
1614 }
1615 else
1616 rcExit = 9;
1617 }
1618 }
1619 else
1620 {
1621 errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]);
1622 rcExit = usage(stderr, argv[0]);
1623 }
1624 }
1625 if (!pszExecutable)
1626 pszExecutable = argv[iArg];
1627
1628 /*
1629 * Make sure there's something to execute.
1630 */
1631 if (rcExit == 0 && iArg < argc)
1632 {
1633 /*
1634 * Do the spawning in a separate function (main is far to large as it is by now).
1635 */
1636 rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnvVars, szCwd, pszSavedCwd,
1637#ifdef USE_POSIX_SPAWN
1638 cOrders, aOrders, &FileActions, cVerbosity,
1639#else
1640 cOrders, aOrders, cVerbosity,
1641#endif
1642#ifdef KMK
1643 pPidSpawned,
1644#endif
1645 &fChildExitCode);
1646 }
1647 else if (rcExit == 0)
1648 {
1649 errx(2, "syntax error: nothing to execute!");
1650 rcExit = usage(stderr, argv[0]);
1651 }
1652 /* Help and version sets rcExit to -1. Change it to zero. */
1653 else if (rcExit == -1)
1654 rcExit = 0;
1655
1656 /*
1657 * Cleanup.
1658 */
1659 if (pszSavedCwd)
1660 free(pszSavedCwd);
1661 kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode);
1662#ifdef USE_POSIX_SPAWN
1663 posix_spawn_file_actions_destroy(&FileActions);
1664#endif
1665#ifndef KMK
1666 iEnvVar = cEnvVars;
1667 while (iEnvVar-- > 0)
1668 free(papszEnvVars[iEnvVar]);
1669 free(papszEnvVars);
1670#endif
1671#ifdef KBUILD_OS_OS2
1672 for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++)
1673 if (apszSavedLibPaths[ulLibPath] != NULL)
1674 {
1675 APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath);
1676 if (rc != 0)
1677 warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!",
1678 apszSavedLibPaths[ulLibPath], ulLibPath, rc);
1679 free(apszSavedLibPaths[ulLibPath])
1680 }
1681#endif
1682
1683 return rcExit;
1684}
1685
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