3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
22 #include <sys/cygwin.h>
29 #include "child_info.h"
30 #include "shared_info.h"
33 #include "perthread.h"
36 #include "cygthread.h"
38 #define LINE_BUF_CHUNK (CYG_MAX_PATH * 2)
40 static suffix_info std_suffixes[] =
42 suffix_info (".exe", 1), suffix_info ("", 1),
43 suffix_info (".com"), suffix_info (".cmd"),
44 suffix_info (".bat"), suffix_info (".dll"),
51 /* Add .exe to PROG if not already present and see if that exists.
52 If not, return PROG (converted from posix to win32 rules if necessary).
53 The result is always BUF.
55 Returns (possibly NULL) suffix */
58 perhaps_suffix (const char *prog, path_conv& buf)
62 debug_printf ("prog '%s'", prog);
63 buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes);
65 if (!buf.exists () || buf.isdir ())
67 else if (buf.known_suffix)
68 ext = (char *) buf + (buf.known_suffix - buf.get_win32 ());
70 ext = strchr (buf, '\0');
72 debug_printf ("buf %s, suffix found '%s'", (char *) buf, ext);
76 /* Find an executable name, possibly by appending known executable
77 suffixes to it. The win32-translated name is placed in 'buf'.
78 Any found suffix is returned in known_suffix.
80 If the file is not found and !null_if_not_found then the win32 version
81 of name is placed in buf and returned. Otherwise the contents of buf
82 is undefined and NULL is returned. */
84 const char * __stdcall
85 find_exec (const char *name, path_conv& buf, const char *mywinenv,
86 unsigned opt, const char **known_suffix)
88 const char *suffix = "";
89 debug_printf ("find_exec (%s)", name);
90 const char *retval = buf;
91 char tmp[CYG_MAX_PATH];
92 const char *posix = (opt & FE_NATIVE) ? NULL : name;
93 bool has_slash = strchr (name, '/');
95 /* Check to see if file can be opened as is first.
96 Win32 systems always check . first, but PATH may not be set up to
98 if ((has_slash || opt & FE_CWD)
99 && (suffix = perhaps_suffix (name, buf)) != NULL)
101 if (posix && !has_slash)
105 strcpy (tmp + 2, name);
113 const char *posix_path;
115 /* Return the error condition if this is an absolute path or if there
116 is no PATH to search. */
117 if (strchr (name, '/') || strchr (name, '\\') ||
119 !(winpath = getwinenv (mywinenv)) ||
120 !(path = winpath->get_native ()) ||
124 debug_printf ("%s%s", mywinenv, path);
126 posix = (opt & FE_NATIVE) ? NULL : tmp;
127 posix_path = winpath->get_posix () - 1;
128 /* Iterate over the specified path, looking for the file with and
129 without executable extensions. */
133 char *eotmp = strccpy (tmp, &path, ';');
134 /* An empty path or '.' means the current directory, but we've
135 already tried that. */
136 if (opt & FE_CWD && (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0')))
140 strcpy (eotmp, name);
142 debug_printf ("trying %s", tmp);
144 if ((suffix = perhaps_suffix (tmp, buf)) != NULL)
148 eotmp = strccpy (tmp, &posix_path, ':');
152 strcpy (eotmp, name);
157 while (*path && *++path && (posix_path = strchr (posix_path, ':')));
161 /* Couldn't find anything in the given path.
162 Take the appropriate action based on null_if_not_found. */
165 else if (opt & FE_NATIVE)
172 buf.set_path (posix);
173 debug_printf ("%s = find_exec (%s)", (char *) buf, name);
175 *known_suffix = suffix ?: strchr (buf, '\0');
179 /* Utility for spawn_guts. */
182 handle (int n, int direction)
184 fhandler_base *fh = cygheap->fdtab[n];
187 return INVALID_HANDLE_VALUE;
188 if (fh->get_close_on_exec ())
189 return INVALID_HANDLE_VALUE;
191 return fh->get_handle ();
192 return fh->get_output_handle ();
196 iscmd (const char *argv0, const char *what)
199 n = strlen (argv0) - strlen (what);
200 if (n >= 2 && argv0[1] != ':')
202 return n >= 0 && strcasematch (argv0 + n, what) &&
203 (n == 0 || isdirsep (argv0[n - 1]));
212 linebuf () : ix (0), buf (NULL), alloced (0) {}
213 ~linebuf () {if (buf) free (buf);}
214 void add (const char *what, int len);
215 void add (const char *what) {add (what, strlen (what));}
216 void prepend (const char *what, int len);
220 linebuf::add (const char *what, int len)
223 if ((newix = ix + len) >= alloced || !buf)
225 alloced += LINE_BUF_CHUNK + newix;
226 buf = (char *) realloc (buf, alloced + 1);
228 memcpy (buf + ix, what, len);
234 linebuf::prepend (const char *what, int len)
238 if ((newix = ix + len) >= alloced)
240 alloced += LINE_BUF_CHUNK + newix;
241 buf = (char *) realloc (buf, alloced + 1);
244 if ((buflen = strlen (buf)))
245 memmove (buf + len, buf, buflen + 1);
248 memcpy (buf, what, len);
259 av (int ac, const char * const *av) : calloced (0), error (false), argc (ac)
261 argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *));
262 memcpy (argv, av, (argc + 1) * sizeof (char *));
268 for (int i = 0; i < calloced; i++)
274 int unshift (const char *what, int conv = 0);
275 operator char **() {return argv;}
276 void all_calloced () {calloced = argc;}
277 void replace0_maybe (const char *arg0)
279 /* Note: Assumes that argv array has not yet been "unshifted" */
281 && (argv[0] = cstrdup1 (arg0)))
286 void dup_maybe (int i)
289 && !(argv[i] = cstrdup1 (argv[i])))
294 for (int i = calloced; i < argc; i++)
295 if (!(argv[i] = cstrdup1 (argv[i])))
301 av::unshift (const char *what, int conv)
304 av = (char **) crealloc (argv, (argc + 2) * sizeof (char *));
309 memmove (argv + 1, argv, (argc + 1) * sizeof (char *));
310 char buf[CYG_MAX_PATH + 1];
313 cygwin_conv_to_posix_path (what, buf);
314 char *p = strchr (buf, '\0') - 4;
315 if (p > buf && strcasematch (p, ".exe"))
319 if (!(*argv = cstrdup1 (what)))
326 struct pthread_cleanup
328 _sig_func_ptr oldint;
329 _sig_func_ptr oldquit;
331 pthread_cleanup (): oldint (NULL), oldquit (NULL), oldmask (0) {}
335 do_cleanup (void *args)
337 # define cleanup ((pthread_cleanup *) args)
339 signal (SIGINT, cleanup->oldint);
340 if (cleanup->oldquit)
341 signal (SIGQUIT, cleanup->oldquit);
342 if (cleanup->oldmask)
343 sigprocmask (SIG_SETMASK, &(cleanup->oldmask), NULL);
349 spawn_guts (const char * prog_arg, const char *const *argv,
350 const char *const envp[], int mode)
357 if (prog_arg == NULL)
359 syscall_printf ("prog_arg is NULL");
364 syscall_printf ("spawn_guts (%d, %.9500s)", mode, prog_arg);
368 syscall_printf ("argv is NULL");
377 STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
379 child_info_spawn ciresrv;
380 si.lpReserved2 = (LPBYTE) &ciresrv;
381 si.cbReserved2 = sizeof (ciresrv);
384 if (mode != _P_OVERLAY)
389 HANDLE subproc_ready;
390 if (chtype != PROC_EXEC)
391 subproc_ready = NULL;
394 subproc_ready = CreateEvent (&sec_all, TRUE, FALSE, NULL);
395 ProtectHandleINH (subproc_ready);
398 init_child_info (chtype, &ciresrv, (mode == _P_OVERLAY) ? myself->pid : 1,
401 ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info));
402 ciresrv.moreinfo->old_title = NULL;
404 /* CreateProcess takes one long string that is the command line (sigh).
405 We need to quote any argument that has whitespace or embedded "'s. */
408 for (ac = 0; argv[ac]; ac++)
411 av newargv (ac, argv);
413 int null_app_name = 0;
414 if (ac == 3 && argv[1][0] == '/' && argv[1][1] == 'c' &&
415 (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
417 real_path.check (prog_arg);
419 if (!real_path.error)
420 one_line.add (real_path);
422 one_line.add (argv[0]);
425 one_line.add (argv[1]);
427 one_line.add (argv[2]);
428 strcpy (real_path, argv[0]);
430 goto skip_arg_parsing;
434 if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL)
442 /* If the file name ends in either .exe, .com, .bat, or .cmd we assume
443 that it is NOT a script file */
446 HANDLE hnd = CreateFile (real_path, GENERIC_READ,
447 FILE_SHARE_READ | FILE_SHARE_WRITE,
448 &sec_none_nih, OPEN_EXISTING,
449 FILE_ATTRIBUTE_NORMAL, 0);
450 if (hnd == INVALID_HANDLE_VALUE)
458 char buf[2 * CYG_MAX_PATH + 1];
459 buf[0] = buf[1] = buf[2] = buf[sizeof (buf) - 1] = '\0';
460 if (!ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0))
469 if (buf[0] == 'M' && buf[1] == 'Z')
472 debug_printf ("%s is a script", (char *) real_path);
476 if (buf[0] != '#' || buf[1] != '!')
478 pgm = (char *) "/bin/sh";
485 pgm += strspn (pgm, " \t");
486 for (ptr = pgm, arg1 = NULL;
487 *ptr && *ptr != '\r' && *ptr != '\n';
489 if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
491 /* Null terminate the initial command and step over
492 any additional white space. If we've hit the
493 end of the line, exit the loop. Otherwise, we've
494 found the first argument. Position the current
495 pointer on the last known white space. */
497 char *newptr = ptr + 1;
498 newptr += strspn (newptr, " \t");
499 if (!*newptr || *newptr == '\r' || *newptr == '\n')
508 /* Replace argv[0] with the full path to the script if this is the
509 first time through the loop. */
510 newargv.replace0_maybe (prog_arg);
513 * pgm interpreter name
514 * arg1 optional string
517 newargv.unshift (arg1);
519 /* FIXME: This should not be using FE_NATIVE. It should be putting
520 the posix path on the argv list. */
521 find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext);
522 newargv.unshift (real_path, 1);
525 if (real_path.iscygexec ())
529 for (int i = 0; i < newargv.argc; i++)
534 newargv.dup_maybe (i);
535 a = i ? newargv[i] : (char *) real_path;
536 int len = strlen (a);
537 if (len != 0 && !strpbrk (a, " \t\n\r\""))
538 one_line.add (a, len);
541 one_line.add ("\"", 1);
542 /* Handle embedded special characters " and \.
543 A " is always preceded by a \.
544 A \ is not special unless it precedes a ". If it does,
545 then all preceding \'s must be doubled to avoid having
546 the Windows command line parser interpret the \ as quoting
547 the ". This rule applies to a string of \'s before the end
548 of the string, since cygwin/windows uses a " to delimit the
550 for (; (p = strpbrk (a, "\"\\")); a = ++p)
552 one_line.add (a, p - a);
553 /* Find length of string of backslashes */
554 int n = strspn (p, "\\");
556 one_line.add ("\\\"", 2); /* No backslashes, so it must be a ".
557 The " has to be protected with a backslash. */
560 one_line.add (p, n); /* Add the run of backslashes */
561 /* Need to double up all of the preceding
562 backslashes if they precede a quote or EOS. */
563 if (!p[n] || p[n] == '"')
565 p += n - 1; /* Point to last backslash */
570 one_line.add ("\"", 1);
573 one_line.add (" ", 1);
579 one_line.buf[one_line.ix - 1] = '\0';
581 one_line.add ("", 1);
584 if (one_line.ix > 32767)
586 debug_printf ("Command line too long (>32K), return E2BIG");
593 newargv.all_calloced ();
596 set_errno (newargv.error);
600 ciresrv.moreinfo->argc = newargv.argc;
601 ciresrv.moreinfo->argv = newargv;
602 ciresrv.hexec_proc = hexec_proc;
604 if (mode != _P_OVERLAY ||
605 !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc,
606 &ciresrv.moreinfo->myself_pinfo, 0,
607 TRUE, DUPLICATE_SAME_ACCESS))
608 ciresrv.moreinfo->myself_pinfo = NULL;
610 VerifyHandle (ciresrv.moreinfo->myself_pinfo);
613 PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
614 si.lpReserved = NULL;
616 si.dwFlags = STARTF_USESTDHANDLES;
617 si.hStdInput = handle (0, 0); /* Get input handle */
618 si.hStdOutput = handle (1, 1); /* Get output handle */
619 si.hStdError = handle (2, 1); /* Get output handle */
622 int flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (hMainProc);
624 if (mode == _P_DETACH || !set_console_state_for_spawn ())
625 flags |= DETACHED_PROCESS;
626 if (mode != _P_OVERLAY)
627 flags |= CREATE_SUSPENDED;
629 /* Some file types (currently only sockets) need extra effort in the
630 parent after CreateProcess and before copying the datastructures
631 to the child. So we have to start the child in suspend state,
632 unfortunately, to avoid a race condition. */
633 if (cygheap->fdtab.need_fixup_before ())
634 flags |= CREATE_SUSPENDED;
637 const char *runpath = null_app_name ? NULL : (const char *) real_path;
639 syscall_printf ("null_app_name %d (%s, %.9500s)", null_app_name, runpath, one_line.buf);
643 cygbench ("spawn-guts");
645 cygheap->fdtab.set_file_pointers_for_exec ();
646 cygheap->user.deimpersonate ();
647 /* When ruid != euid we create the new process under the current original
648 account and impersonate in child, this way maintaining the different
649 effective vs. real ids.
650 FIXME: If ruid != euid and ruid != saved_uid we currently give
651 up on ruid. The new process will have ruid == euid. */
652 if (!cygheap->user.issetuid ()
653 || (cygheap->user.saved_uid == cygheap->user.real_uid
654 && cygheap->user.saved_gid == cygheap->user.real_gid
655 && !cygheap->user.groups.issetgroups ()))
657 ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc,
658 real_path.iscygexec ());
659 newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
660 rc = CreateProcess (runpath, /* image name - with full path */
661 one_line.buf, /* what was passed to exec */
662 &sec_none_nih,/* process security attrs */
663 &sec_none_nih,/* thread security attrs */
664 TRUE, /* inherit handles from parent */
666 envblock, /* environment */
667 0, /* use current drive/directory */
673 /* Give access to myself */
674 if (mode == _P_OVERLAY)
677 /* allow the child to interact with our window station/desktop */
679 SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION;
684 hwst = GetProcessWindowStation ();
685 SetUserObjectSecurity (hwst, &dsi, get_null_sd ());
686 GetUserObjectInformation (hwst, UOI_NAME, wstname, 1024, &n);
687 hdsk = GetThreadDesktop (GetCurrentThreadId ());
688 SetUserObjectSecurity (hdsk, &dsi, get_null_sd ());
689 GetUserObjectInformation (hdsk, UOI_NAME, dskname, 1024, &n);
690 strcat (wstname, "\\");
691 strcat (wstname, dskname);
692 si.lpDesktop = wstname;
694 ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc,
695 real_path.iscygexec ());
696 newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
697 rc = CreateProcessAsUser (cygheap->user.token (),
698 runpath, /* image name - with full path */
699 one_line.buf, /* what was passed to exec */
700 &sec_none_nih, /* process security attrs */
701 &sec_none_nih, /* thread security attrs */
702 TRUE, /* inherit handles from parent */
704 envblock, /* environment */
705 0, /* use current drive/directory */
710 /* Restore impersonation. In case of _P_OVERLAY this isn't
711 allowed since it would overwrite child data. */
712 if (mode != _P_OVERLAY || !rc)
713 cygheap->user.reimpersonate ();
720 /* Set errno now so that debugging messages from it appear before our
721 final debugging message [this is a general rule for debugging
726 syscall_printf ("CreateProcess failed, %E");
728 ForceCloseHandle (subproc_ready);
729 cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
733 /* FIXME: There is a small race here */
736 pthread_cleanup cleanup;
737 pthread_cleanup_push (do_cleanup, (void *) &cleanup);
738 if (mode == _P_SYSTEM)
740 sigset_t child_block;
741 cleanup.oldint = signal (SIGINT, SIG_IGN);
742 cleanup.oldquit = signal (SIGQUIT, SIG_IGN);
743 sigemptyset (&child_block);
744 sigaddset (&child_block, SIGCHLD);
745 (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup.oldmask);
748 /* Fixup the parent datastructure if needed and resume the child's
750 if (!cygheap->fdtab.need_fixup_before ())
751 cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
754 cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
755 cygheap_setup_for_child_cleanup (newheap, &ciresrv, 1);
756 if (mode == _P_OVERLAY)
758 ResumeThread (pi.hThread);
759 cygthread::terminate ();
763 if (mode != _P_OVERLAY)
764 cygpid = cygwin_pid (pi.dwProcessId);
766 cygpid = myself->pid;
768 /* We print the original program name here so the user can see that too. */
769 syscall_printf ("%d = spawn_guts (%s, %.9500s)",
770 rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf);
772 /* Name the handle similarly to proc_subproc. */
773 ProtectHandle1 (pi.hProcess, childhProc);
775 if (mode == _P_OVERLAY)
777 /* These are both duplicated in the child code. We do this here,
778 primarily for strace. */
780 hExeced = pi.hProcess;
781 dwExeced = pi.dwProcessId;
782 strcpy (myself->progname, real_path);
787 myself->set_has_pgid_children ();
788 ProtectHandle (pi.hThread);
789 pinfo child (cygpid, PID_IN_USE);
792 syscall_printf ("pinfo failed");
793 if (get_errno () != ENOMEM)
798 child->dwProcessId = pi.dwProcessId;
799 child->hProcess = pi.hProcess;
800 if (!child.remember ())
802 syscall_printf ("process table full");
808 strcpy (child->progname, real_path);
809 /* FIXME: This introduces an unreferenced, open handle into the child.
810 The purpose is to keep the pid shared memory open so that all of
811 the fields filled out by child.remember do not disappear and so there
812 is not a brief period during which the pid is not available.
813 However, we should try to find another way to do this eventually. */
814 (void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess,
815 NULL, 0, 0, DUPLICATE_SAME_ACCESS);
816 /* Start the child running */
817 ResumeThread (pi.hThread);
820 ForceCloseHandle (pi.hThread);
822 sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
829 if (mode == _P_OVERLAY)
832 HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, subproc_ready};
833 for (int i = 0; i < 100; i++)
835 switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, INFINITE))
838 sigproc_printf ("subprocess exited");
840 if (!GetExitCodeProcess (pi.hProcess, &exitcode))
845 case WAIT_OBJECT_0 + 1:
846 sigproc_printf ("signal arrived");
847 reset_signal_arrived ();
849 case WAIT_OBJECT_0 + 2:
850 if (my_parent_is_alive ())
851 res |= EXIT_REPARENTING;
852 else if (!myself->ppid_handle)
855 sigproc_terminate ();
860 system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E",
861 nwait, myself->pid, myself->dwProcessId);
862 system_printf ("waitbuf[0] %p %d", waitbuf[0],
863 WaitForSingleObject (waitbuf[0], 0));
864 system_printf ("waitbuf[1] %p = %d", waitbuf[1],
865 WaitForSingleObject (waitbuf[1], 0));
866 system_printf ("waitbuf[w] %p = %d", waitbuf[2],
867 WaitForSingleObject (waitbuf[2], 0));
875 ForceCloseHandle (subproc_ready);
877 sigproc_printf ("res = %x", res);
879 if (res & EXIT_REPARENTING)
881 /* Try to reparent child process.
882 * Make handles to child available to parent process and exit with
883 * EXIT_REPARENTING status. Wait() syscall in parent will then wait
884 * for newly created child.
886 HANDLE oldh = myself->hProcess;
887 HANDLE h = myself->ppid_handle;
888 sigproc_printf ("parent handle %p", h);
889 int rc = DuplicateHandle (hMainProc, pi.hProcess, h, &myself->hProcess,
890 0, FALSE, DUPLICATE_SAME_ACCESS);
891 sigproc_printf ("%d = DuplicateHandle, oldh %p, newh %p",
892 rc, oldh, myself->hProcess);
893 VerifyHandle (myself->hProcess);
894 if (!rc && my_parent_is_alive ())
896 system_printf ("Reparent failed, parent handle %p, %E", h);
897 system_printf ("my dwProcessId %d, myself->dwProcessId %d",
898 GetCurrentProcessId (), myself->dwProcessId);
899 system_printf ("old hProcess %p, hProcess %p", oldh, myself->hProcess);
910 ForceCloseHandle1 (pi.hProcess, childhProc);
912 myself->exit (res, 1);
916 waitpid (cygpid, (int *) &res, 0);
919 res = 0; /* Lose all memory of this child. */
931 pthread_cleanup_pop (1);
936 cwait (int *result, int pid, int)
938 return waitpid (pid, result, 0);
942 * Helper function for spawn runtime calls.
943 * Doesn't search the path.
947 spawnve (int mode, const char *path, const char *const *argv,
948 const char *const *envp)
952 vfork_save *vf = vfork_storage.val ();
954 if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
960 syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp);
965 /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
966 /* Just act as an exec if _P_OVERLAY set. */
967 spawn_guts (path, argv, envp, mode);
968 /* Errno should be set by spawn_guts. */
978 ret = spawn_guts (path, argv, envp, mode);
984 debug_printf ("longjmping due to vfork");
985 vf->restore_pid (ret);
999 * spawn functions as implemented in the MS runtime library.
1000 * Most of these based on (and copied from) newlib/libc/posix/execXX.c
1004 spawnl (int mode, const char *path, const char *arg0, ...)
1008 const char *argv[256];
1010 va_start (args, arg0);
1015 argv[i] = va_arg (args, const char *);
1016 while (argv[i++] != NULL);
1020 return spawnve (mode, path, (char * const *) argv, cur_environ ());
1024 spawnle (int mode, const char *path, const char *arg0, ...)
1028 const char * const *envp;
1029 const char *argv[256];
1031 va_start (args, arg0);
1036 argv[i] = va_arg (args, const char *);
1037 while (argv[i++] != NULL);
1039 envp = va_arg (args, const char * const *);
1042 return spawnve (mode, path, (char * const *) argv, (char * const *) envp);
1046 spawnlp (int mode, const char *path, const char *arg0, ...)
1050 const char *argv[256];
1052 va_start (args, arg0);
1057 argv[i] = va_arg (args, const char *);
1058 while (argv[i++] != NULL);
1062 return spawnvpe (mode, path, (char * const *) argv, cur_environ ());
1066 spawnlpe (int mode, const char *path, const char *arg0, ...)
1070 const char * const *envp;
1071 const char *argv[256];
1073 va_start (args, arg0);
1078 argv[i] = va_arg (args, const char *);
1079 while (argv[i++] != NULL);
1081 envp = va_arg (args, const char * const *);
1084 return spawnvpe (mode, path, (char * const *) argv, envp);
1088 spawnv (int mode, const char *path, const char * const *argv)
1090 return spawnve (mode, path, argv, cur_environ ());
1094 spawnvp (int mode, const char *path, const char * const *argv)
1096 return spawnvpe (mode, path, argv, cur_environ ());
1100 spawnvpe (int mode, const char *file, const char * const *argv,
1101 const char * const *envp)
1104 return spawnve (mode, find_exec (file, buf), argv, envp);