VirtualBox

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

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

kmk_redirect: Added a --stdin-pipe option for tricking a windows rsh implementation into not stopping when stdin is redirected to /dev/null and appears to be empty when read()'ing it.

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