VirtualBox

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

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

kmk: warnings found by gcc 7.3.0

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

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