VirtualBox

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

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

redirect.c: solaris 10 build fix (no O_CLOEXEC flag)

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

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