VirtualBox

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

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

kmk_redirect: cleanup [fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.9 KB
Line 
1/* $Id: redirect.c 3164 2018-03-20 03:31:22Z bird $ */
2/** @file
3 * kmk_redirect - Do simple program <-> file redirection (++).
4 */
5
6/*
7 * Copyright (c) 2007-2016 knut st. osmundsen <[email protected]>
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#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
43# include <Windows.h>
44# include <Winternl.h>
45#endif
46#if defined(_MSC_VER)
47# include <ctype.h>
48# include <io.h>
49# include "quote_argv.h"
50#else
51# ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
52# if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
53# define USE_POSIX_SPAWN
54# endif
55# elif !defined(KBUILD_OS_WINDOWS) && !defined(KBUILD_OS_OS2)
56# define USE_POSIX_SPAWN
57# endif
58# include <unistd.h>
59# ifdef USE_POSIX_SPAWN
60# include <spawn.h>
61# endif
62# include <sys/wait.h>
63#endif
64
65#include <k/kDefs.h>
66#include <k/kTypes.h>
67#include "err.h"
68#include "kbuild_version.h"
69#if defined(__gnu_hurd__) && !defined(kmk_builtin_redirect) /* need constant */
70# undef GET_PATH_MAX
71# undef PATH_MAX
72# define GET_PATH_MAX PATH_MAX
73#endif
74#include "kmkbuiltin.h"
75#ifdef KMK
76# include "job.h"
77# include "variable.h"
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 * Alternative approach on windows that use CreateProcess and doesn't require
797 * any serialization wrt handles and CWD.
798 *
799 * @returns 0 on success, non-zero on failure to create.
800 * @param pszExecutable The child process executable.
801 * @param cArgs Number of arguments.
802 * @param papszArgs The child argument vector.
803 * @param papszEnvVars The child environment vector.
804 * @param pszCwd The current working directory of the child.
805 * @param cOrders Number of file operation orders.
806 * @param paOrders The file operation orders.
807 * @param phProcess Where to return process handle.
808 */
809static kRedirectCreateProcessWindows(const char *pszExecutable, int cArgs, char **papszArgs, char **papszEnvVars,
810 const char *pszCwd, unsigned cOrders, REDIRECTORDERS *paOrders, HANDLE *phProcess)
811{
812 static NTSTATUS (NTAPI *s_pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG) = NULL;
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 * Determin host bitness and APIs while we can still easily return.
823 */
824#if K_ARCH_BITS == 32
825 BOOL f64BitHost = TRUE;
826 if (!IsWow64Process(GetCurrentProcess(), &f64BitHost))
827 return errx(9, "IsWow64Process failed: %u", GetLastError());
828#elif K_ARCH_BITS == 64
829 BOOL const f64BitHost = TRUE;
830#else
831# error "K_ARCH_BITS is bad/missing"
832#endif
833 if (cOrders > 0 && !s_pfnNtQueryInformationProcess)
834 {
835 *(FARPROC *)&s_pfnNtQueryInformationProcess = GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryInformationProcess");
836 if (!s_pfnNtQueryInformationProcess)
837 return errx(9, "NtQueryInformationProcess not found!");
838 }
839
840 /*
841 * Start by making the the command line. We just need to put spaces
842 * between the arguments since quote_argv don't the quoting already.
843 */
844 cbArgs = 0;
845 for (i = 0; i < cArgs; i++)
846 cbArgs += strlen(papszArgs[i]) + 1;
847 pszCmdLine = pch = (char *)malloc(cbArgs);
848 if (!pszCmdLine)
849 return errx(9, "out of memory!");
850 for (i = 0; i < cArgs; i++)
851 {
852 size_t cch;
853 if (i != 0)
854 *pch++ = ' ';
855 cch = strlen(papszArgs[i]);
856 memcpy(pch, papszArgs[i], cch);
857 pch += cch;
858 }
859 *pch++ = '\0';
860 assert(pch - pszCmdLine == cbArgs);
861
862 /*
863 * The environment vector is also simple.
864 */
865 cbEnv = 0;
866 for (i = 0; papszEnvVars[i]; i++)
867 cbEnv += strlen(papszEnvVars[i]) + 1;
868 cbEnv++;
869 pszzEnv = pch = (char *)malloc(cbEnv);
870 if (pszzEnv)
871 {
872 char szAbsExe[1024];
873 const char *pszPathVal = NULL;
874 STARTUPINFOA StartupInfo;
875 PROCESS_INFORMATION ProcInfo = { NULL, NULL, 0, 0 };
876
877 for (i = 0; papszEnvVars[i]; i++)
878 {
879 size_t cbSrc = strlen(papszEnvVars[i]) + 1;
880 memcpy(pch, papszEnvVars[i], cbSrc);
881 if ( !pszPathVal
882 && cbSrc >= 5
883 && pch[4] == '='
884 && (pch[0] == 'P' || pch[0] == 'p')
885 && (pch[1] == 'A' || pch[1] == 'a')
886 && (pch[2] == 'T' || pch[2] == 't')
887 && (pch[3] == 'H' || pch[3] == 'h'))
888 pszPathVal = &pch[5];
889 pch += cbSrc;
890 }
891 *pch++ = '\0';
892 assert(pch - pszzEnv == cbEnv);
893
894 /*
895 * Locate the executable.
896 */
897 pszExecutable = kRedirectCreateProcessWindowsFindImage(pszExecutable, szAbsExe, sizeof(szAbsExe), pszPathVal);
898
899 /*
900 * Do basic startup info preparation.
901 */
902 memset(&StartupInfo, 0, sizeof(StartupInfo));
903 StartupInfo.cb = sizeof(StartupInfo);
904 GetStartupInfoA(&StartupInfo);
905 StartupInfo.lpReserved2 = 0; /* No CRT file handle + descriptor info possible, sorry. */
906 StartupInfo.cbReserved2 = 0;
907 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
908
909 /*
910 * If there are no redirection orders, we're good.
911 */
912 if (!cOrders)
913 {
914 if (CreateProcessA(pszExecutable, pszCmdLine, NULL /*pProcAttrs*/, NULL /*pThreadAttrs*/,
915 FALSE /*fInheritHandles*/, 0 /*fFlags*/, pszzEnv, pszCwd, &StartupInfo, &ProcInfo))
916 {
917 CloseHandle(ProcInfo.hThread);
918 *phProcess = ProcInfo.hProcess;
919 rc = 0;
920 }
921 else
922 rc = errx(10, "CreateProcessA(%s) failed: %u", pszExecutable, GetLastError());
923 }
924 else
925 {
926 /*
927 * Execute the orders, ending up with three handles we need to
928 * implant into the guest process.
929 *
930 * This isn't 100% perfect wrt O_APPEND, but it'll have to do for now.
931 */
932 BOOL afReplace[3] = { FALSE, FALSE, FALSE };
933 HANDLE ahChild[3] = { NULL, NULL, NULL };
934 rc = 0;
935 for (i = 0; i < (int)cOrders; i++)
936 {
937 int fdTarget = paOrders[i].fdTarget;
938 assert(fdTarget >= 0 && fdTarget < 3);
939 switch (paOrders[i].enmOrder)
940 {
941 case kRedirectOrder_Open:
942 if ( (paOrders[i].fOpen & O_APPEND)
943 && lseek(paOrders[i].fdSource, 0, SEEK_END) < 0)
944 rc = err(10, "lseek-to-end failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
945 case kRedirectOrder_Dup:
946 ahChild[fdTarget] = (HANDLE)_get_osfhandle(paOrders[i].fdSource);
947 if (ahChild[fdTarget] == NULL || ahChild[fdTarget] == INVALID_HANDLE_VALUE)
948 rc = err(10, "_get_osfhandle failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
949 break;
950 case kRedirectOrder_Close:
951 ahChild[fdTarget] = NULL;
952 break;
953 default:
954 assert(0);
955 }
956 afReplace[fdTarget] = TRUE;
957 }
958 if (rc == 0)
959 {
960 /*
961 * Start the process in suspended animation so we can inject handles.
962 */
963 if (CreateProcessA(pszExecutable, pszCmdLine, NULL /*pProcAttrs*/, NULL /*pThreadAttrs*/,
964 FALSE /*fInheritHandles*/, CREATE_SUSPENDED, pszzEnv, pszCwd, &StartupInfo, &ProcInfo))
965 {
966 /*
967 * Figure out where we need to write the handles.
968 */
969 ULONG cbActual1 = 0;
970 PROCESS_BASIC_INFORMATION BasicInfo = { 0, 0, };
971 NTSTATUS rcNt = s_pfnNtQueryInformationProcess(ProcInfo.hProcess, ProcessBasicInformation,
972 &BasicInfo, sizeof(BasicInfo), &cbActual1);
973 if (NT_SUCCESS(rcNt))
974 {
975 BOOL const f32BitPeb = !f64BitHost;
976 ULONG const cbChildPtr = f32BitPeb ? 4 : 8;
977 PVOID pvSrcInPeb = (char *)BasicInfo.PebBaseAddress + (f32BitPeb ? 0x10 : 0x20);
978 char * pbDst = 0;
979 SIZE_T cbActual2 = 0;
980 if (ReadProcessMemory(ProcInfo.hProcess, pvSrcInPeb, &pbDst, cbChildPtr, &cbActual2))
981 {
982 union
983 {
984 KU32 au32[3];
985 KU64 au64[3];
986 } uBuf;
987 memset(&uBuf, 0, sizeof(uBuf));
988 pbDst += f32BitPeb ? 0x18 : 0x20;
989 if ( (afReplace[0] && afReplace[1] && afReplace[2])
990 || ReadProcessMemory(ProcInfo.hProcess, pbDst, &uBuf, cbChildPtr * 3, &cbActual2))
991 {
992 for (i = 0; i < 3; i++)
993 if (afReplace[i])
994 {
995 HANDLE hInChild = INVALID_HANDLE_VALUE;
996 if ( ahChild[i] == NULL /* just closed*/
997 || DuplicateHandle(GetCurrentProcess(), ahChild[i], ProcInfo.hProcess, &hInChild,
998 0, TRUE /*fInheriable*/, DUPLICATE_SAME_ACCESS))
999 {
1000 if (f32BitPeb)
1001 uBuf.au32[i] = (KU32)(uintptr_t)hInChild;
1002 else
1003 uBuf.au64[i] = (uintptr_t)hInChild;
1004 }
1005 else
1006 rc = errx(10, "Error duplicating %p into the child: %u", ahChild[i], GetLastError());
1007 }
1008 if ( rc == 0
1009 && !WriteProcessMemory(ProcInfo.hProcess, pbDst, &uBuf, cbChildPtr * 3, &cbActual2))
1010 rc = errx(10, "Error writing standard handles at %p LB %u in the child: %u",
1011 pbDst, cbChildPtr * 3, GetLastError());
1012 }
1013 else
1014 rc = errx(10, "Error reading %p LB %u from the child: %u",
1015 pbDst, cbChildPtr * 3, GetLastError());
1016 }
1017 else
1018 rc = errx(10, "Error reading %p LB %u from the child: %u", pvSrcInPeb, cbChildPtr);
1019 }
1020 else
1021 rc = errx(10, "NtQueryInformationProcess failed: %#x", rcNt);
1022
1023 /* Resume the bugger... */
1024 if ( rc == 0
1025 && !ResumeThread(ProcInfo.hThread))
1026 rc = errx(10, "ResumeThread failed: %u", GetLastError());
1027
1028 /* .. or kill it. */
1029 if (rc != 0)
1030 TerminateProcess(ProcInfo.hProcess, rc);
1031
1032 CloseHandle(ProcInfo.hThread);
1033 *phProcess = ProcInfo.hProcess;
1034 rc = 0;
1035 }
1036 else
1037 rc = errx(10, "CreateProcessA(%s) failed: %u", pszExecutable, GetLastError());
1038 }
1039 }
1040 free(pszzEnv);
1041 }
1042 else
1043 rc = errx(9, "out of memory!");
1044 free(pszCmdLine);
1045 return rc;
1046}
1047
1048#endif /* KBUILD_OS_WINDOWS */
1049
1050/**
1051 * Does the child spawning .
1052 *
1053 * @returns Exit code.
1054 * @param pszExecutable The child process executable.
1055 * @param cArgs Number of arguments.
1056 * @param papszArgs The child argument vector.
1057 * @param fWatcomBrainDamage Whether MSC need to do quoting according to
1058 * weird Watcom WCC rules.
1059 * @param papszEnvVars The child environment vector.
1060 * @param pszCwd The current working directory of the child.
1061 * @param pszSavedCwd The saved current working directory. This is
1062 * NULL if the CWD doesn't need changing.
1063 * @param cOrders Number of file operation orders.
1064 * @param paOrders The file operation orders.
1065 * @param pFileActions The posix_spawn file actions.
1066 * @param cVerbosity The verbosity level.
1067 * @param pPidSpawned Where to return the PID of the spawned child
1068 * when we're inside KMK and we're return without
1069 * waiting.
1070 * @param pfIsChildExitCode Where to indicate whether the return exit code
1071 * is from the child or from our setup efforts.
1072 */
1073static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage,
1074 char **papszEnvVars, const char *pszCwd, const char *pszSavedCwd,
1075 unsigned cOrders, REDIRECTORDERS *paOrders,
1076#ifdef USE_POSIX_SPAWN
1077 posix_spawn_file_actions_t *pFileActions,
1078#endif
1079 unsigned cVerbosity,
1080#ifdef KMK
1081 pid_t *pPidSpawned,
1082#endif
1083 KBOOL *pfIsChildExitCode)
1084{
1085 int rcExit = 0;
1086 int i;
1087#ifdef _MSC_VER
1088 char **papszArgsOriginal = papszArgs;
1089#endif
1090 *pfIsChildExitCode = K_FALSE;
1091
1092#ifdef _MSC_VER
1093 /*
1094 * Do MSC parameter quoting.
1095 */
1096 papszArgs = malloc((cArgs + 1) * sizeof(papszArgs[0]));
1097 if (papszArgs)
1098 memcpy(papszArgs, papszArgsOriginal, (cArgs + 1) * sizeof(papszArgs[0]));
1099 else
1100 return errx(9, "out of memory!");
1101
1102 rcExit = quote_argv(cArgs, papszArgs, fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
1103 if (rcExit == 0)
1104#endif
1105 {
1106 /*
1107 * Display what we're about to execute if we're in verbose mode.
1108 */
1109 if (cVerbosity > 0)
1110 {
1111 for (i = 0; i < cArgs; i++)
1112 warnx("debug: argv[%i]=%s<eos>", i, papszArgs[i]);
1113 for (i = 0; i < (int)cOrders; i++)
1114 switch (paOrders[i].enmOrder)
1115 {
1116 case kRedirectOrder_Close:
1117 warnx("debug: close %d\n", paOrders[i].fdTarget);
1118 break;
1119 case kRedirectOrder_Dup:
1120 warnx("debug: dup %d to %d\n", paOrders[i].fdSource, paOrders[i].fdTarget);
1121 break;
1122 case kRedirectOrder_Open:
1123 warnx("debug: open '%s' (%#x) as [%d ->] %d\n",
1124 paOrders[i].pszFilename, paOrders[i].fOpen, paOrders[i].fdSource, paOrders[i].fdTarget);
1125 break;
1126 default:
1127 warnx("error! invalid enmOrder=%d", paOrders[i].enmOrder);
1128 assert(0);
1129 break;
1130 }
1131 if (pszSavedCwd)
1132 warnx("debug: chdir %s\n", pszCwd);
1133 }
1134
1135#ifndef KBUILD_OS_WINDOWS
1136 /*
1137 * Change working directory if so requested.
1138 */
1139 if (pszSavedCwd)
1140 {
1141 if (chdir(pszCwd) < 0)
1142 rcExit = errx(10, "Failed to change directory to '%s'", pszCwd);
1143 }
1144#endif /* KBUILD_OS_WINDOWS */
1145 if (rcExit == 0)
1146 {
1147# if !defined(USE_POSIX_SPAWN) && !defined(KBUILD_OS_WINDOWS)
1148 /*
1149 * Execute the file orders.
1150 */
1151 FILE *pWorkingStdErr = NULL;
1152 rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr);
1153 if (rcExit == 0)
1154# endif
1155 {
1156# ifdef KMK
1157 /*
1158 * We're spawning from within kmk.
1159 */
1160# ifdef KBUILD_OS_WINDOWS
1161 /* Windows is slightly complicated due to handles and winchildren.c. */
1162 HANDLE hProcess = INVALID_HANDLE_VALUE;
1163 rcExit = kRedirectCreateProcessWindows(pszExecutable, cArgs, papszArgs, papszEnvVars, pszCwd,
1164 cOrders, paOrders, &hProcess);
1165 if (rcExit == 0)
1166 {
1167# ifndef CONFIG_NEW_WIN_CHILDREN
1168 if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
1169# else
1170 if (MkWinChildCreateRedirect((intptr_t)hProcess, pPidSpawned) == 0)
1171# endif
1172 {
1173 if (cVerbosity > 0)
1174 warnx("debug: spawned %d", *pPidSpawned);
1175 }
1176 else
1177 {
1178 DWORD dwTmp;
1179# ifndef CONFIG_NEW_WIN_CHILDREN
1180 warn("sub_proc is out of slots, waiting for child...");
1181# else
1182 warn("MkWinChildCreateRedirect failed...");
1183# endif
1184 dwTmp = WaitForSingleObject(hProcess, INFINITE);
1185 if (dwTmp != WAIT_OBJECT_0)
1186 warn("WaitForSingleObject failed: %#x\n", dwTmp);
1187
1188 if (GetExitCodeProcess(hProcess, &dwTmp))
1189 rcExit = (int)dwTmp;
1190 else
1191 {
1192 warn("GetExitCodeProcess failed: %u\n", GetLastError());
1193 TerminateProcess(hProcess, 127);
1194 rcExit = 127;
1195 }
1196
1197 CloseHandle(hProcess);
1198 *pPidSpawned = 0;
1199 *pfIsChildExitCode = K_TRUE;
1200 }
1201 }
1202
1203# elif defined(KBUILD_OS_OS2)
1204 *pPidSpawned = _spawnvpe(P_NOWAIT, pszExecutable, papszArgs, papszEnvVars);
1205 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
1206 if (*pPidSpawned != -1)
1207 {
1208 if (cVerbosity > 0)
1209 warnx("debug: spawned %d", *pPidSpawned);
1210 }
1211 else
1212 {
1213 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
1214 *pPidSpawned = 0;
1215 }
1216# else
1217 rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
1218 if (rcExit == 0)
1219 {
1220 if (cVerbosity > 0)
1221 warnx("debug: spawned %d", *pPidSpawned);
1222 }
1223 else
1224 {
1225 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
1226 *pPidSpawned = 0;
1227 }
1228# endif
1229
1230#else /* !KMK */
1231 /*
1232 * Spawning from inside the kmk_redirect executable.
1233 */
1234# ifdef KBUILD_OS_WINDOWS
1235 HANDLE hProcess = INVALID_HANDLE_VALUE;
1236 rcExit = kRedirectCreateProcessWindows(pszExecutable, cArgs, papszArgs, papszEnvVars, pszCwd,
1237 cOrders, paOrders, &hProcess);
1238 if (rcExit == 0)
1239 {
1240 DWORD dwWait;
1241 do
1242 dwWait = WaitForSingleObject(hProcess, INFINITE);
1243 while (dwWait == WAIT_IO_COMPLETION || dwWait == WAIT_TIMEOUT);
1244
1245 dwWait = 11;
1246 if (GetExitCodeProcess(hProcess, &dwWait))
1247 rcExit = dwWait;
1248 else
1249 rcExit = errx(11, "GetExitCodeProcess(%s) failed: %u", pszExecutable, GetLastError());
1250 }
1251
1252#elif defined(KBUILD_OS_OS2)
1253 errno = 0;
1254 rcExit = (int)_spawnvpe(P_WAIT, pszExecutable, papszArgs, papszEnvVars);
1255 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
1256 if (rcExit != -1 || errno == 0)
1257 {
1258 *pfIsChildExitCode = K_TRUE;
1259 if (cVerbosity > 0)
1260 warnx("debug: exit code: %d", rcExit);
1261 }
1262 else
1263 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
1264
1265# else
1266 pid_t pidChild = 0;
1267 rcExit = posix_spawnp(&pidChild, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
1268 if (rcExit == 0)
1269 {
1270 *pfIsChildExitCode = K_TRUE;
1271 if (cVerbosity > 0)
1272 warnx("debug: spawned %d", pidChild);
1273
1274 /* Wait for the child. */
1275 for (;;)
1276 {
1277 pid_t pid = waitpid(pidChild, &rcExit, 0 /*block*/);
1278 if (pid == pidChild)
1279 {
1280 if (cVerbosity > 0)
1281 warnx("debug: %d exit code: %d", pidChild, rcExit);
1282 break;
1283 }
1284 if ( errno != EINTR
1285# ifdef ERESTART
1286 && errno != ERESTART
1287# endif
1288 )
1289 {
1290 rcExit = err(11, "waitpid failed");
1291 kill(pidChild, SIGKILL);
1292 break;
1293 }
1294 }
1295 }
1296 else
1297 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
1298# endif
1299#endif /* !KMK */
1300 }
1301 }
1302
1303#ifndef KBUILD_OS_WINDOWS
1304 /*
1305 * Restore the current directory.
1306 */
1307 if (pszSavedCwd)
1308 {
1309 if (chdir(pszSavedCwd) < 0)
1310 warn("Failed to restore directory to '%s'", pszSavedCwd);
1311 }
1312#endif
1313 }
1314#ifdef _MSC_VER
1315 else
1316 rcExit = errx(9, "quite_argv failed: %u", rcExit);
1317
1318 /* Restore the original argv strings, freeing the quote_argv replacements. */
1319 i = cArgs;
1320 while (i-- > 0)
1321 if (papszArgs[i] != papszArgsOriginal[i])
1322 free(papszArgs[i]);
1323 free(papszArgs);
1324#endif
1325 return rcExit;
1326}
1327
1328
1329/**
1330 * The function that does almost everything here... ugly.
1331 */
1332#ifdef KMK
1333int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
1334#else
1335int main(int argc, char **argv, char **envp)
1336#endif
1337{
1338 int rcExit = 0;
1339 KBOOL fChildExitCode = K_FALSE;
1340#ifdef USE_POSIX_SPAWN
1341 posix_spawn_file_actions_t FileActions;
1342#endif
1343 unsigned cOrders = 0;
1344 REDIRECTORDERS aOrders[32];
1345
1346 int iArg;
1347 const char *pszExecutable = NULL;
1348 char **papszEnvVars = NULL;
1349 unsigned cAllocatedEnvVars;
1350 unsigned iEnvVar;
1351 unsigned cEnvVars;
1352 int fWatcomBrainDamage = 0;
1353 int cVerbosity = 0;
1354 char *pszSavedCwd = NULL;
1355 size_t const cbCwdBuf = GET_PATH_MAX;
1356 PATH_VAR(szCwd);
1357#ifdef KBUILD_OS_OS2
1358 ULONG ulLibPath;
1359 char *apszSavedLibPaths[LIBPATHSTRICT + 1] = { NULL, NULL, NULL, NULL };
1360#endif
1361
1362
1363 g_progname = argv[0];
1364
1365 if (argc <= 1)
1366 return usage(stderr, argv[0]);
1367
1368 /*
1369 * Create default program environment.
1370 */
1371#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
1372 if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
1373#else
1374 if (getcwd(szCwd, cbCwdBuf) != NULL)
1375#endif
1376 { /* likely */ }
1377 else
1378 return err(9, "getcwd failed");
1379
1380#if defined(KMK)
1381 /* We get it from kmk and just count it: */
1382 papszEnvVars = pChild->environment;
1383 if (!papszEnvVars)
1384 pChild->environment = papszEnvVars = target_environment(pChild->file);
1385 cEnvVars = 0;
1386 while (papszEnvVars[cEnvVars] != NULL)
1387 cEnvVars++;
1388 cAllocatedEnvVars = cEnvVars;
1389#else
1390 /* We make a copy and we manage ourselves: */
1391 cEnvVars = 0;
1392 while (envp[cEnvVars] != NULL)
1393 cEnvVars++;
1394
1395 cAllocatedEnvVars = cEnvVars + 4;
1396 papszEnvVars = malloc((cAllocatedEnvVars + 1) * sizeof(papszEnvVars));
1397 if (!papszEnvVars)
1398 return errx(9, "out of memory!");
1399
1400 iEnvVar = cEnvVars;
1401 papszEnvVars[iEnvVar] = NULL;
1402 while (iEnvVar-- > 0)
1403 {
1404 papszEnvVars[iEnvVar] = strdup(envp[iEnvVar]);
1405 if (!papszEnvVars[iEnvVar])
1406 {
1407 while (iEnvVar-- > 0)
1408 free(papszEnvVars[iEnvVar]);
1409 free(papszEnvVars);
1410 return errx(9, "out of memory!");
1411 }
1412 }
1413#endif
1414
1415#ifdef USE_POSIX_SPAWN
1416 /*
1417 * Init posix attributes.
1418 */
1419 rcExit = posix_spawn_file_actions_init(&FileActions);
1420 if (rcExit != 0)
1421 rcExit = errx(9, "posix_spawn_file_actions_init failed: %s", strerror(rcExit));
1422#endif
1423
1424 /*
1425 * Parse arguments.
1426 */
1427 for (iArg = 1; rcExit == 0 && iArg < argc; iArg++)
1428 {
1429 char *pszArg = argv[iArg];
1430 if (*pszArg == '-')
1431 {
1432 int fd;
1433 char chOpt;
1434 const char *pszValue;
1435
1436 chOpt = *++pszArg;
1437 pszArg++;
1438 if (chOpt == '-')
1439 {
1440 /* '--' indicates where the bits to execute start. Check if we're
1441 relaunching ourselves here and just continue parsing if we are. */
1442 if (*pszArg == '\0')
1443 {
1444 iArg++;
1445 if ( iArg >= argc
1446 || ( strcmp(argv[iArg], "kmk_builtin_redirect") != 0
1447 && strcmp(argv[iArg], argv[0]) != 0))
1448 break;
1449 continue;
1450 }
1451
1452 if ( strcmp(pszArg, "wcc-brain-damage") == 0
1453 || strcmp(pszArg, "watcom-brain-damage") == 0)
1454 {
1455 fWatcomBrainDamage = 1;
1456 continue;
1457 }
1458
1459 /* convert to short. */
1460 if (strcmp(pszArg, "help") == 0)
1461 chOpt = 'h';
1462 else if (strcmp(pszArg, "version") == 0)
1463 chOpt = 'V';
1464 else if ( strcmp(pszArg, "set") == 0
1465 || strcmp(pszArg, "env") == 0)
1466 chOpt = 'E';
1467 else if (strcmp(pszArg, "append") == 0)
1468 chOpt = 'A';
1469 else if (strcmp(pszArg, "prepend") == 0)
1470 chOpt = 'D';
1471 else if (strcmp(pszArg, "unset") == 0)
1472 chOpt = 'U';
1473 else if ( strcmp(pszArg, "zap-env") == 0
1474 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
1475 chOpt = 'Z';
1476 else if (strcmp(pszArg, "chdir") == 0)
1477 chOpt = 'C';
1478 else if (strcmp(pszArg, "close") == 0)
1479 chOpt = 'c';
1480 else if (strcmp(pszArg, "verbose") == 0)
1481 chOpt = 'v';
1482 else
1483 {
1484 errx(2, "Unknown option: '%s'", pszArg - 2);
1485 rcExit = usage(stderr, argv[0]);
1486 break;
1487 }
1488 pszArg = "";
1489 }
1490
1491 /*
1492 * Deal with the obligatory help and version switches first to get them out of the way.
1493 */
1494 if (chOpt == 'h')
1495 {
1496 usage(stdout, argv[0]);
1497 rcExit = -1;
1498 break;
1499 }
1500 if (chOpt == 'V')
1501 {
1502 kbuild_version(argv[0]);
1503 rcExit = -1;
1504 break;
1505 }
1506
1507 /*
1508 * Get option value first, if the option takes one.
1509 */
1510 if ( chOpt == 'E'
1511 || chOpt == 'A'
1512 || chOpt == 'D'
1513 || chOpt == 'U'
1514 || chOpt == 'C'
1515 || chOpt == 'c'
1516 || chOpt == 'd'
1517 || chOpt == 'e')
1518 {
1519 if (*pszArg != '\0')
1520 pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
1521 else if (++iArg < argc)
1522 pszValue = argv[iArg];
1523 else
1524 {
1525 errx(2, "syntax error: Option -%c requires a value!", chOpt);
1526 rcExit = usage(stderr, argv[0]);
1527 break;
1528 }
1529 }
1530 else
1531 pszValue = NULL;
1532
1533 /*
1534 * Environment switch?
1535 */
1536 if (chOpt == 'E')
1537 {
1538 const char *pchEqual = strchr(pszValue, '=');
1539#ifdef KBUILD_OS_OS2
1540 if ( strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0
1541 || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0
1542 || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0)
1543 {
1544 ULONG ulVar = *pszValue == 'B' ? BEGIN_LIBPATH
1545 : *pszValue == 'E' ? END_LIBPATH
1546 : LIBPATHSTRICT;
1547 APIRET rc;
1548 if (apszSavedLibPaths[ulVar] == NULL)
1549 {
1550 /* The max length is supposed to be 1024 bytes. */
1551 apszSavedLibPaths[ulVar] = calloc(1024, 2);
1552 if (apszSavedLibPaths[ulVar])
1553 {
1554 rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar);
1555 if (rc)
1556 {
1557 rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc);
1558 free(apszSavedLibPaths[ulVar]);
1559 apszSavedLibPaths[ulVar] = NULL;
1560 }
1561 }
1562 else
1563 rcExit = errx(9, "out of memory!");
1564 }
1565 if (rcExit == 0)
1566 {
1567 rc = DosSetExtLIBPATH(pchEqual + 1, ulVar);
1568 if (rc)
1569 rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu",
1570 pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc);
1571 }
1572 continue;
1573 }
1574#endif /* KBUILD_OS_OS2 */
1575
1576 /* We differ from kSubmit here and use putenv sematics. */
1577 if (pchEqual)
1578 {
1579 if (pchEqual[1] != '\0')
1580 {
1581 rcExit = kBuiltinOptEnvSet(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1582#ifdef KMK
1583 pChild->environment = papszEnvVars;
1584#endif
1585 }
1586 else
1587 {
1588 char *pszCopy = strdup(pszValue);
1589 if (pszCopy)
1590 {
1591 pszCopy[pchEqual - pszValue] = '\0';
1592 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszCopy);
1593 free(pszCopy);
1594 }
1595 else
1596 rcExit = errx(1, "out of memory!");
1597 }
1598 continue;
1599 }
1600 /* Simple unset. */
1601 chOpt = 'U';
1602 }
1603
1604 /*
1605 * Append or prepend value to and environment variable.
1606 */
1607 if (chOpt == 'A' || chOpt == 'D')
1608 {
1609#ifdef KBUILD_OS_OS2
1610 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1611 || strcmp(pszValue, "ENDLIBPATH") == 0
1612 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1613 rcExit = errx(2, "error: '%s' cannot currently be appended or prepended to. Please use -E/--set for now.", pszValue);
1614 else
1615#endif
1616 if (chOpt == 'A')
1617 rcExit = kBuiltinOptEnvAppend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1618 else
1619 rcExit = kBuiltinOptEnvPrepend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1620 continue;
1621 }
1622
1623 /*
1624 * Unset environment variable.
1625 */
1626 if (chOpt == 'U')
1627 {
1628#ifdef KBUILD_OS_OS2
1629 if ( strcmp(pszValue, "BEGINLIBPATH") == 0
1630 || strcmp(pszValue, "ENDLIBPATH") == 0
1631 || strcmp(pszValue, "LIBPATHSTRICT") == 0)
1632 rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue);
1633 else
1634#endif
1635 rcExit = kBuiltinOptEnvUnset(papszEnvVars, &cEnvVars, cVerbosity, pszValue);
1636 continue;
1637 }
1638
1639 /*
1640 * Zap environment switch?
1641 */
1642 if ( chOpt == 'Z'
1643 || chOpt == 'i' /* GNU env compatibility. */ )
1644 {
1645 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
1646 free(papszEnvVars[iEnvVar]);
1647 papszEnvVars[0] = NULL;
1648 cEnvVars = 0;
1649 continue;
1650 }
1651
1652 /*
1653 * Change directory switch?
1654 */
1655 if (chOpt == 'C')
1656 {
1657 if (pszSavedCwd == NULL)
1658 pszSavedCwd = strdup(szCwd);
1659 if (pszSavedCwd)
1660 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
1661 else
1662 rcExit = err(9, "out of memory!");
1663 continue;
1664 }
1665
1666
1667 /*
1668 * Verbose operation switch?
1669 */
1670 if (chOpt == 'v')
1671 {
1672 cVerbosity++;
1673 continue;
1674 }
1675
1676 /*
1677 * Executable image other than the first argument following '--'.
1678 */
1679 if (chOpt == 'e')
1680 {
1681 pszExecutable = pszValue;
1682 continue;
1683 }
1684
1685 /*
1686 * Okay, it is some file descriptor opearation. Make sure we've got room for it.
1687 */
1688 if (cOrders + 1 < K_ELEMENTS(aOrders))
1689 {
1690 aOrders[cOrders].fdTarget = -1;
1691 aOrders[cOrders].fdSource = -1;
1692 aOrders[cOrders].fOpen = 0;
1693 aOrders[cOrders].fRemoveOnFailure = 0;
1694 aOrders[cOrders].pszFilename = NULL;
1695#ifndef USE_POSIX_SPAWN
1696 aOrders[cOrders].fdSaved = -1;
1697#endif
1698 }
1699 else
1700 {
1701 rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders));
1702 break;
1703 }
1704
1705 if (chOpt == 'c')
1706 {
1707 /*
1708 * Close the specified file descriptor (no stderr/out/in aliases).
1709 */
1710 char *pszTmp;
1711 fd = (int)strtol(pszValue, &pszTmp, 0);
1712 if (pszTmp == pszValue || *pszTmp != '\0')
1713 rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue);
1714 else if (fd < 0)
1715 rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
1716#ifdef ONLY_TARGET_STANDARD_HANDLES
1717 else if (fd > 2)
1718 rcExit = errx(2, "error: %d is not a standard descriptor number", fd);
1719#endif
1720 else
1721 {
1722 aOrders[cOrders].enmOrder = kRedirectOrder_Close;
1723 aOrders[cOrders].fdTarget = fd;
1724 cOrders++;
1725#ifdef USE_POSIX_SPAWN
1726 rcExit = posix_spawn_file_actions_addclose(&FileActions, fd);
1727 if (rcExit != 0)
1728 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1729#endif
1730 }
1731 }
1732 else if (chOpt == 'd')
1733 {
1734 /*
1735 * Duplicate file handle. Value is fdTarget=fdSource
1736 */
1737 char *pszEqual;
1738 fd = (int)strtol(pszValue, &pszEqual, 0);
1739 if (pszEqual == pszValue)
1740 rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue);
1741 else if (fd < 0)
1742 rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
1743#ifdef ONLY_TARGET_STANDARD_HANDLES
1744 else if (fd > 2)
1745 rcExit = errx(2, "error: target %d is not a standard descriptor number", fd);
1746#endif
1747 else if (*pszEqual != '=')
1748 rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
1749 else
1750 {
1751 char *pszEnd;
1752 int fdSource = (int)strtol(++pszEqual, &pszEnd, 0);
1753 if (pszEnd == pszEqual || *pszEnd != '\0')
1754 rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue);
1755 else if (fdSource < 0)
1756 rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue);
1757 else
1758 {
1759 aOrders[cOrders].enmOrder = kRedirectOrder_Dup;
1760 aOrders[cOrders].fdTarget = fd;
1761 aOrders[cOrders].fdSource = fdSource;
1762 cOrders++;
1763#ifdef USE_POSIX_SPAWN
1764 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, fd);
1765 if (rcExit != 0)
1766 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
1767#endif
1768 }
1769 }
1770 }
1771 else
1772 {
1773 /*
1774 * Open file as a given file descriptor.
1775 */
1776 int fdOpened;
1777 int fOpen;
1778
1779 /* mode */
1780 switch (chOpt)
1781 {
1782 case 'r':
1783 chOpt = *pszArg++;
1784 if (chOpt == '+')
1785 {
1786 fOpen = O_RDWR;
1787 chOpt = *pszArg++;
1788 }
1789 else
1790 fOpen = O_RDONLY;
1791 break;
1792
1793 case 'w':
1794 chOpt = *pszArg++;
1795 if (chOpt == '+')
1796 {
1797 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1798 chOpt = *pszArg++;
1799 }
1800 else
1801 fOpen = O_WRONLY | O_CREAT | O_TRUNC;
1802 aOrders[cOrders].fRemoveOnFailure = 1;
1803 break;
1804
1805 case 'a':
1806 chOpt = *pszArg++;
1807 if (chOpt == '+')
1808 {
1809 fOpen = O_RDWR | O_CREAT | O_APPEND;
1810 chOpt = *pszArg++;
1811 }
1812 else
1813 fOpen = O_WRONLY | O_CREAT | O_APPEND;
1814 break;
1815
1816 case 'i': /* make sure stdin is read-only. */
1817 fOpen = O_RDONLY;
1818 break;
1819
1820 case '+':
1821 rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]);
1822 continue;
1823
1824 default:
1825 fOpen = O_RDWR | O_CREAT | O_TRUNC;
1826 aOrders[cOrders].fRemoveOnFailure = 1;
1827 break;
1828 }
1829
1830 /* binary / text modifiers */
1831 switch (chOpt)
1832 {
1833 case 'b':
1834 chOpt = *pszArg++;
1835 default:
1836#ifdef O_BINARY
1837 fOpen |= O_BINARY;
1838#elif defined(_O_BINARY)
1839 fOpen |= _O_BINARY;
1840#endif
1841 break;
1842
1843 case 't':
1844#ifdef O_TEXT
1845 fOpen |= O_TEXT;
1846#elif defined(_O_TEXT)
1847 fOpen |= _O_TEXT;
1848#endif
1849 chOpt = *pszArg++;
1850 break;
1851
1852 }
1853
1854 /* convert to file descriptor number */
1855 switch (chOpt)
1856 {
1857 case 'i':
1858 fd = 0;
1859 break;
1860
1861 case 'o':
1862 fd = 1;
1863 break;
1864
1865 case 'e':
1866 fd = 2;
1867 break;
1868
1869 case '0':
1870 if (*pszArg == '\0')
1871 {
1872 fd = 0;
1873 break;
1874 }
1875 /* fall thru */
1876 case '1':
1877 case '2':
1878 case '3':
1879 case '4':
1880 case '5':
1881 case '6':
1882 case '7':
1883 case '8':
1884 case '9':
1885 pszValue = pszArg - 1;
1886 fd = (int)strtol(pszValue, &pszArg, 0);
1887 if (pszArg == pszValue)
1888 rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]);
1889 else if (fd < 0)
1890 rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
1891#ifdef ONLY_TARGET_STANDARD_HANDLES
1892 else if (fd > 2)
1893 rcExit = errx(2, "error: %d is not a standard descriptor number", fd);
1894#endif
1895 else
1896 break;
1897 continue;
1898
1899 /*
1900 * Invalid argument.
1901 */
1902 default:
1903 rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]);
1904 continue;
1905 }
1906
1907 /*
1908 * Check for the filename.
1909 */
1910 if (*pszArg != '\0')
1911 {
1912 if (*pszArg != ':' && *pszArg != '=')
1913 {
1914 rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')",
1915 pszArg, argv[iArg]);
1916 break;
1917 }
1918 pszArg++;
1919 }
1920 else if (++iArg < argc)
1921 pszArg = argv[iArg];
1922 else
1923 {
1924 rcExit = errx(2, "syntax error: missing filename argument.");
1925 break;
1926 }
1927
1928 /*
1929 * Open the file. We could've used posix_spawn_file_actions_addopen here,
1930 * but that means complicated error reporting. So, since we need to do
1931 * this for windows anyway, just do it the same way everywhere.
1932 */
1933 fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders,
1934 aOrders[cOrders].fRemoveOnFailure, fd);
1935 if (fdOpened >= 0)
1936 {
1937 aOrders[cOrders].enmOrder = kRedirectOrder_Open;
1938 aOrders[cOrders].fdTarget = fd;
1939 aOrders[cOrders].fdSource = fdOpened;
1940 aOrders[cOrders].fOpen = fOpen;
1941 aOrders[cOrders].pszFilename = pszArg;
1942 cOrders++;
1943
1944#ifdef USE_POSIX_SPAWN
1945 if (fdOpened != fd)
1946 {
1947 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd);
1948 if (rcExit != 0)
1949 rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s",
1950 fdOpened, fd, pszArg, strerror(rcExit));
1951 }
1952#endif
1953 }
1954 else
1955 rcExit = 9;
1956 }
1957 }
1958 else
1959 {
1960 errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]);
1961 rcExit = usage(stderr, argv[0]);
1962 }
1963 }
1964 if (!pszExecutable)
1965 pszExecutable = argv[iArg];
1966
1967 /*
1968 * Make sure there's something to execute.
1969 */
1970 if (rcExit == 0 && iArg < argc)
1971 {
1972 /*
1973 * Do the spawning in a separate function (main is far to large as it is by now).
1974 */
1975 rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage,
1976 papszEnvVars,
1977 szCwd, pszSavedCwd,
1978#ifdef USE_POSIX_SPAWN
1979 cOrders, aOrders, &FileActions, cVerbosity,
1980#else
1981 cOrders, aOrders, cVerbosity,
1982#endif
1983#ifdef KMK
1984 pPidSpawned,
1985#endif
1986 &fChildExitCode);
1987 }
1988 else if (rcExit == 0)
1989 {
1990 errx(2, "syntax error: nothing to execute!");
1991 rcExit = usage(stderr, argv[0]);
1992 }
1993 /* Help and version sets rcExit to -1. Change it to zero. */
1994 else if (rcExit == -1)
1995 rcExit = 0;
1996
1997 /*
1998 * Cleanup.
1999 */
2000 if (pszSavedCwd)
2001 free(pszSavedCwd);
2002 kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode);
2003#ifdef USE_POSIX_SPAWN
2004 posix_spawn_file_actions_destroy(&FileActions);
2005#endif
2006#ifndef KMK
2007 iEnvVar = cEnvVars;
2008 while (iEnvVar-- > 0)
2009 free(papszEnvVars[iEnvVar]);
2010 free(papszEnvVars);
2011#endif
2012#ifdef KBUILD_OS_OS2
2013 for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++)
2014 if (apszSavedLibPaths[ulLibPath] != NULL)
2015 {
2016 APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath);
2017 if (rc != 0)
2018 warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!",
2019 apszSavedLibPaths[ulLibPath], ulLibPath, rc);
2020 free(apszSavedLibPaths[ulLibPath]);
2021 }
2022#endif
2023
2024 return rcExit;
2025}
2026
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