VirtualBox

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

Last change on this file since 3247 was 3247, checked in by bird, 6 years ago

kmkbuiltin: warnings and build fixes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette