VirtualBox

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

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

kmk_redirect,winchildren: WOW64 standard handle injection fixes.

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