VirtualBox

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

Last change on this file since 2996 was 2916, checked in by bird, 8 years ago

fixes

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