VirtualBox

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

Last change on this file since 3387 was 3332, checked in by bird, 4 years ago

kmk_redirect: Updated usage.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use