2000-02-18 03:38:33 +08:00
|
|
|
/* spawn.cc
|
|
|
|
|
2002-01-14 04:03:03 +08:00
|
|
|
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
This file is part of Cygwin.
|
|
|
|
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
|
|
details. */
|
|
|
|
|
2000-08-03 00:28:18 +08:00
|
|
|
#include "winsup.h"
|
2000-02-18 03:38:33 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <process.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
2000-07-28 01:30:51 +08:00
|
|
|
#include <wingdi.h>
|
|
|
|
#include <winuser.h>
|
2000-02-18 03:38:33 +08:00
|
|
|
#include <ctype.h>
|
2000-08-22 13:10:20 +08:00
|
|
|
#include "cygerrno.h"
|
2000-09-08 10:56:55 +08:00
|
|
|
#include <sys/cygwin.h>
|
2001-03-05 14:28:25 +08:00
|
|
|
#include "perprocess.h"
|
2001-07-27 03:22:24 +08:00
|
|
|
#include "security.h"
|
2000-08-22 13:10:20 +08:00
|
|
|
#include "fhandler.h"
|
|
|
|
#include "path.h"
|
2000-08-12 13:35:42 +08:00
|
|
|
#include "dtable.h"
|
2000-08-22 13:10:20 +08:00
|
|
|
#include "sync.h"
|
|
|
|
#include "sigproc.h"
|
2000-09-07 05:03:10 +08:00
|
|
|
#include "cygheap.h"
|
2000-09-02 04:54:22 +08:00
|
|
|
#include "child_info.h"
|
2001-01-28 13:51:15 +08:00
|
|
|
#include "shared_info.h"
|
2000-08-12 13:35:42 +08:00
|
|
|
#include "pinfo.h"
|
2000-09-08 10:56:55 +08:00
|
|
|
#define NEED_VFORK
|
2000-08-22 13:10:20 +08:00
|
|
|
#include "perthread.h"
|
2000-09-08 10:56:55 +08:00
|
|
|
#include "registry.h"
|
|
|
|
#include "environ.h"
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
#define LINE_BUF_CHUNK (MAX_PATH * 2)
|
|
|
|
|
2000-10-24 10:25:27 +08:00
|
|
|
static suffix_info std_suffixes[] =
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
|
|
|
suffix_info (".exe", 1), suffix_info ("", 1),
|
|
|
|
suffix_info (".com"), suffix_info (".cmd"),
|
|
|
|
suffix_info (".bat"), suffix_info (".dll"),
|
|
|
|
suffix_info (NULL)
|
|
|
|
};
|
|
|
|
|
2000-10-24 10:25:27 +08:00
|
|
|
HANDLE hExeced;
|
2002-01-10 11:21:27 +08:00
|
|
|
DWORD dwExeced;
|
2000-10-24 10:25:27 +08:00
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
/* Add .exe to PROG if not already present and see if that exists.
|
|
|
|
If not, return PROG (converted from posix to win32 rules if necessary).
|
|
|
|
The result is always BUF.
|
|
|
|
|
|
|
|
Returns (possibly NULL) suffix */
|
|
|
|
|
|
|
|
static const char *
|
2002-01-10 11:21:27 +08:00
|
|
|
perhaps_suffix (const char *prog, path_conv& buf)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
|
|
|
char *ext;
|
|
|
|
|
|
|
|
debug_printf ("prog '%s'", prog);
|
2000-07-18 03:18:21 +08:00
|
|
|
buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-10-05 09:39:08 +08:00
|
|
|
if (!buf.exists () || buf.isdir ())
|
2000-02-18 03:38:33 +08:00
|
|
|
ext = NULL;
|
2000-04-26 13:13:32 +08:00
|
|
|
else if (buf.known_suffix)
|
2001-10-01 12:10:07 +08:00
|
|
|
ext = (char *) buf + (buf.known_suffix - buf.get_win32 ());
|
2000-02-18 03:38:33 +08:00
|
|
|
else
|
|
|
|
ext = strchr (buf, '\0');
|
|
|
|
|
2000-04-26 13:13:32 +08:00
|
|
|
debug_printf ("buf %s, suffix found '%s'", (char *) buf, ext);
|
2000-02-18 03:38:33 +08:00
|
|
|
return ext;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find an executable name, possibly by appending known executable
|
|
|
|
suffixes to it. The win32-translated name is placed in 'buf'.
|
|
|
|
Any found suffix is returned in known_suffix.
|
|
|
|
|
|
|
|
If the file is not found and !null_if_not_found then the win32 version
|
|
|
|
of name is placed in buf and returned. Otherwise the contents of buf
|
|
|
|
is undefined and NULL is returned. */
|
|
|
|
|
|
|
|
const char * __stdcall
|
2000-04-26 13:13:32 +08:00
|
|
|
find_exec (const char *name, path_conv& buf, const char *mywinenv,
|
2001-10-31 08:55:32 +08:00
|
|
|
unsigned opt, const char **known_suffix)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
|
|
|
const char *suffix = "";
|
|
|
|
debug_printf ("find_exec (%s)", name);
|
2002-03-22 11:24:30 +08:00
|
|
|
const char *retval = buf;
|
2001-10-31 08:55:32 +08:00
|
|
|
char tmp[MAX_PATH];
|
|
|
|
const char *posix = (opt & FE_NATIVE) ? NULL : name;
|
|
|
|
bool has_slash = strchr (name, '/');
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
/* Check to see if file can be opened as is first.
|
|
|
|
Win32 systems always check . first, but PATH may not be set up to
|
|
|
|
do this. */
|
2001-10-31 08:55:32 +08:00
|
|
|
if ((has_slash || opt & FE_CWD)
|
|
|
|
&& (suffix = perhaps_suffix (name, buf)) != NULL)
|
|
|
|
{
|
|
|
|
if (posix && !has_slash)
|
|
|
|
{
|
|
|
|
tmp[0] = '.';
|
|
|
|
tmp[1] = '/';
|
|
|
|
strcpy (tmp + 2, name);
|
|
|
|
posix = tmp;
|
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
win_env *winpath;
|
|
|
|
const char *path;
|
2001-10-31 08:55:32 +08:00
|
|
|
const char *posix_path;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
/* Return the error condition if this is an absolute path or if there
|
|
|
|
is no PATH to search. */
|
|
|
|
if (strchr (name, '/') || strchr (name, '\\') ||
|
2000-05-30 08:38:51 +08:00
|
|
|
isdrive (name) ||
|
2000-02-18 03:38:33 +08:00
|
|
|
!(winpath = getwinenv (mywinenv)) ||
|
|
|
|
!(path = winpath->get_native ()) ||
|
|
|
|
*path == '\0')
|
|
|
|
goto errout;
|
|
|
|
|
|
|
|
debug_printf ("%s%s", mywinenv, path);
|
|
|
|
|
2001-10-31 08:55:32 +08:00
|
|
|
posix = (opt & FE_NATIVE) ? NULL : tmp;
|
|
|
|
posix_path = winpath->get_posix () - 1;
|
2000-02-18 03:38:33 +08:00
|
|
|
/* Iterate over the specified path, looking for the file with and
|
|
|
|
without executable extensions. */
|
|
|
|
do
|
|
|
|
{
|
2001-10-31 08:55:32 +08:00
|
|
|
posix_path++;
|
2000-02-18 03:38:33 +08:00
|
|
|
char *eotmp = strccpy (tmp, &path, ';');
|
|
|
|
/* An empty path or '.' means the current directory, but we've
|
|
|
|
already tried that. */
|
2001-10-31 08:55:32 +08:00
|
|
|
if (opt & FE_CWD && (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0')))
|
2000-02-18 03:38:33 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
*eotmp++ = '\\';
|
|
|
|
strcpy (eotmp, name);
|
|
|
|
|
|
|
|
debug_printf ("trying %s", tmp);
|
|
|
|
|
|
|
|
if ((suffix = perhaps_suffix (tmp, buf)) != NULL)
|
2001-10-31 08:55:32 +08:00
|
|
|
{
|
|
|
|
if (posix == tmp)
|
|
|
|
{
|
|
|
|
eotmp = strccpy (tmp, &posix_path, ':');
|
|
|
|
if (eotmp == tmp)
|
|
|
|
*eotmp++ = '.';
|
|
|
|
*eotmp++ = '/';
|
|
|
|
strcpy (eotmp, name);
|
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
2001-10-31 08:55:32 +08:00
|
|
|
while (*path && *++path && (posix_path = strchr (posix_path, ':')));
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
errout:
|
2001-10-31 08:55:32 +08:00
|
|
|
posix = NULL;
|
2000-02-18 03:38:33 +08:00
|
|
|
/* Couldn't find anything in the given path.
|
|
|
|
Take the appropriate action based on null_if_not_found. */
|
2001-10-31 08:55:32 +08:00
|
|
|
if (opt & FE_NNF)
|
2000-02-18 03:38:33 +08:00
|
|
|
retval = NULL;
|
2001-10-31 08:55:32 +08:00
|
|
|
else if (opt & FE_NATIVE)
|
2000-04-26 13:13:32 +08:00
|
|
|
buf.check (name);
|
2002-03-22 11:24:30 +08:00
|
|
|
else
|
|
|
|
retval = name;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
out:
|
2001-10-31 08:55:32 +08:00
|
|
|
if (posix)
|
|
|
|
buf.set_path (posix);
|
2000-04-26 13:13:32 +08:00
|
|
|
debug_printf ("%s = find_exec (%s)", (char *) buf, name);
|
2000-02-18 03:38:33 +08:00
|
|
|
if (known_suffix)
|
|
|
|
*known_suffix = suffix ?: strchr (buf, '\0');
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Utility for spawn_guts. */
|
|
|
|
|
|
|
|
static HANDLE
|
|
|
|
handle (int n, int direction)
|
|
|
|
{
|
2001-04-19 05:10:15 +08:00
|
|
|
fhandler_base *fh = cygheap->fdtab[n];
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
if (!fh)
|
|
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
if (fh->get_close_on_exec ())
|
|
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
if (direction == 0)
|
|
|
|
return fh->get_handle ();
|
|
|
|
return fh->get_output_handle ();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
iscmd (const char *argv0, const char *what)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
n = strlen (argv0) - strlen (what);
|
|
|
|
if (n >= 2 && argv0[1] != ':')
|
|
|
|
return 0;
|
2000-07-02 01:30:35 +08:00
|
|
|
return n >= 0 && strcasematch (argv0 + n, what) &&
|
2000-02-18 03:38:33 +08:00
|
|
|
(n == 0 || isdirsep (argv0[n - 1]));
|
|
|
|
}
|
|
|
|
|
|
|
|
class linebuf
|
|
|
|
{
|
2001-08-23 01:50:22 +08:00
|
|
|
public:
|
2000-02-18 03:38:33 +08:00
|
|
|
size_t ix;
|
|
|
|
char *buf;
|
|
|
|
size_t alloced;
|
2000-10-20 12:20:21 +08:00
|
|
|
linebuf () : ix (0), buf (NULL), alloced (0) {}
|
2000-02-18 03:38:33 +08:00
|
|
|
~linebuf () {/* if (buf) free (buf);*/}
|
|
|
|
void add (const char *what, int len);
|
|
|
|
void add (const char *what) {add (what, strlen (what));}
|
|
|
|
void prepend (const char *what, int len);
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
linebuf::add (const char *what, int len)
|
|
|
|
{
|
|
|
|
size_t newix;
|
|
|
|
if ((newix = ix + len) >= alloced || !buf)
|
|
|
|
{
|
|
|
|
alloced += LINE_BUF_CHUNK + newix;
|
|
|
|
buf = (char *) realloc (buf, alloced + 1);
|
|
|
|
}
|
|
|
|
memcpy (buf + ix, what, len);
|
|
|
|
ix = newix;
|
|
|
|
buf[ix] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
linebuf::prepend (const char *what, int len)
|
|
|
|
{
|
|
|
|
int buflen;
|
|
|
|
size_t newix;
|
|
|
|
if ((newix = ix + len) >= alloced)
|
|
|
|
{
|
|
|
|
alloced += LINE_BUF_CHUNK + newix;
|
|
|
|
buf = (char *) realloc (buf, alloced + 1);
|
|
|
|
buf[ix] = '\0';
|
|
|
|
}
|
|
|
|
if ((buflen = strlen (buf)))
|
|
|
|
memmove (buf + len, buf, buflen + 1);
|
|
|
|
else
|
|
|
|
buf[newix] = '\0';
|
|
|
|
memcpy (buf, what, len);
|
|
|
|
ix = newix;
|
|
|
|
}
|
|
|
|
|
2000-09-14 03:57:00 +08:00
|
|
|
class av
|
2000-09-03 12:16:35 +08:00
|
|
|
{
|
|
|
|
char **argv;
|
2000-09-14 03:57:00 +08:00
|
|
|
int calloced;
|
2001-08-23 01:50:22 +08:00
|
|
|
public:
|
2000-09-14 03:57:00 +08:00
|
|
|
int argc;
|
|
|
|
av (int ac, const char * const *av) : calloced (0), argc (ac)
|
2000-09-03 12:16:35 +08:00
|
|
|
{
|
2000-10-14 13:52:38 +08:00
|
|
|
argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *));
|
2000-09-03 12:16:35 +08:00
|
|
|
memcpy (argv, av, (argc + 1) * sizeof (char *));
|
|
|
|
}
|
|
|
|
~av ()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < calloced; i++)
|
|
|
|
cfree (argv[i]);
|
|
|
|
cfree (argv);
|
|
|
|
}
|
|
|
|
int unshift (const char *what, int conv = 0);
|
|
|
|
operator char **() {return argv;}
|
2000-09-14 03:57:00 +08:00
|
|
|
void all_calloced () {calloced = argc;}
|
|
|
|
void replace0_maybe (const char *arg0)
|
|
|
|
{
|
|
|
|
/* Note: Assumes that argv array has not yet been "unshifted" */
|
|
|
|
if (!calloced)
|
|
|
|
{
|
2000-09-27 13:12:09 +08:00
|
|
|
argv[0] = cstrdup1 (arg0);
|
2000-09-14 03:57:00 +08:00
|
|
|
calloced = 1;
|
|
|
|
}
|
|
|
|
}
|
2000-09-26 00:36:12 +08:00
|
|
|
void dup_maybe (int i)
|
2000-09-14 03:57:00 +08:00
|
|
|
{
|
|
|
|
if (i >= calloced)
|
2000-09-27 13:12:09 +08:00
|
|
|
argv[i] = cstrdup1 (argv[i]);
|
2000-09-14 03:57:00 +08:00
|
|
|
}
|
|
|
|
void dup_all ()
|
|
|
|
{
|
|
|
|
for (int i = calloced; i < argc; i++)
|
2000-09-27 13:12:09 +08:00
|
|
|
argv[i] = cstrdup1 (argv[i]);
|
2000-09-14 03:57:00 +08:00
|
|
|
}
|
2000-09-03 12:16:35 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
av::unshift (const char *what, int conv)
|
|
|
|
{
|
|
|
|
char **av;
|
|
|
|
av = (char **) crealloc (argv, (argc + 2) * sizeof (char *));
|
|
|
|
if (!av)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
argv = av;
|
|
|
|
memmove (argv + 1, argv, (argc + 1) * sizeof (char *));
|
|
|
|
char buf[MAX_PATH + 1];
|
|
|
|
if (conv)
|
|
|
|
{
|
|
|
|
cygwin_conv_to_posix_path (what, buf);
|
|
|
|
char *p = strchr (buf, '\0') - 4;
|
|
|
|
if (p > buf && strcasematch (p, ".exe"))
|
|
|
|
*p = '\0';
|
|
|
|
what = buf;
|
|
|
|
}
|
2000-09-27 13:12:09 +08:00
|
|
|
*argv = cstrdup1 (what);
|
2000-09-03 12:16:35 +08:00
|
|
|
argc++;
|
|
|
|
calloced++;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2000-07-30 00:24:59 +08:00
|
|
|
static int __stdcall
|
2000-02-18 03:38:33 +08:00
|
|
|
spawn_guts (HANDLE hToken, const char * prog_arg, const char *const *argv,
|
2000-07-30 00:24:59 +08:00
|
|
|
const char *const envp[], int mode)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
|
|
|
BOOL rc;
|
2000-07-30 00:24:59 +08:00
|
|
|
pid_t cygpid;
|
2000-11-04 13:54:57 +08:00
|
|
|
sigframe thisframe (mainthread);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
if (prog_arg == NULL)
|
|
|
|
{
|
|
|
|
syscall_printf ("prog_arg is NULL");
|
2001-03-19 04:58:18 +08:00
|
|
|
set_errno (EINVAL);
|
2000-02-18 03:38:33 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2001-06-25 05:57:50 +08:00
|
|
|
syscall_printf ("spawn_guts (%d, %.132s)", mode, prog_arg);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
if (argv == NULL)
|
|
|
|
{
|
|
|
|
syscall_printf ("argv is NULL");
|
2001-03-19 04:58:18 +08:00
|
|
|
set_errno (EINVAL);
|
2000-02-18 03:38:33 +08:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2000-09-03 12:16:35 +08:00
|
|
|
path_conv real_path;
|
|
|
|
|
|
|
|
linebuf one_line;
|
|
|
|
|
|
|
|
STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
|
|
|
|
|
|
|
|
child_info_spawn ciresrv;
|
|
|
|
si.lpReserved2 = (LPBYTE) &ciresrv;
|
|
|
|
si.cbReserved2 = sizeof (ciresrv);
|
|
|
|
|
|
|
|
DWORD chtype;
|
2001-10-04 10:34:20 +08:00
|
|
|
if (mode != _P_OVERLAY)
|
2000-09-03 12:16:35 +08:00
|
|
|
chtype = PROC_SPAWN;
|
|
|
|
else
|
2000-10-14 13:52:38 +08:00
|
|
|
chtype = PROC_EXEC;
|
|
|
|
|
2001-07-17 11:41:52 +08:00
|
|
|
HANDLE spr;
|
2001-10-04 10:34:20 +08:00
|
|
|
if (chtype != PROC_EXEC)
|
2001-07-17 11:41:52 +08:00
|
|
|
spr = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spr = CreateEvent (&sec_all, TRUE, FALSE, NULL);
|
|
|
|
ProtectHandle (spr);
|
|
|
|
}
|
2000-09-03 12:16:35 +08:00
|
|
|
|
2001-10-04 10:34:20 +08:00
|
|
|
init_child_info (chtype, &ciresrv, (mode == _P_OVERLAY) ? myself->pid : 1,
|
2001-11-05 14:09:15 +08:00
|
|
|
spr);
|
2000-09-03 12:16:35 +08:00
|
|
|
if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &ciresrv.parent, 0, 1,
|
|
|
|
DUPLICATE_SAME_ACCESS))
|
|
|
|
{
|
|
|
|
system_printf ("couldn't create handle to myself for child, %E");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2000-09-27 13:12:09 +08:00
|
|
|
ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info));
|
2000-10-15 09:37:07 +08:00
|
|
|
ciresrv.moreinfo->old_title = NULL;
|
2000-09-03 12:16:35 +08:00
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
/* CreateProcess takes one long string that is the command line (sigh).
|
|
|
|
We need to quote any argument that has whitespace or embedded "'s. */
|
|
|
|
|
2000-09-03 12:16:35 +08:00
|
|
|
int ac;
|
|
|
|
for (ac = 0; argv[ac]; ac++)
|
2000-02-18 03:38:33 +08:00
|
|
|
/* nothing */;
|
|
|
|
|
2000-09-03 12:16:35 +08:00
|
|
|
av newargv (ac, argv);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-10-20 12:20:21 +08:00
|
|
|
int null_app_name = 0;
|
2000-09-03 12:16:35 +08:00
|
|
|
if (ac == 3 && argv[1][0] == '/' && argv[1][1] == 'c' &&
|
2000-02-18 03:38:33 +08:00
|
|
|
(iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
|
|
|
|
{
|
2000-10-20 12:20:21 +08:00
|
|
|
real_path.check (prog_arg);
|
2000-10-21 12:53:49 +08:00
|
|
|
one_line.add ("\"");
|
2000-10-20 12:20:21 +08:00
|
|
|
if (!real_path.error)
|
|
|
|
one_line.add (real_path);
|
|
|
|
else
|
|
|
|
one_line.add (argv[0]);
|
2000-10-21 12:53:49 +08:00
|
|
|
one_line.add ("\"");
|
2000-02-18 03:38:33 +08:00
|
|
|
one_line.add (" ");
|
|
|
|
one_line.add (argv[1]);
|
|
|
|
one_line.add (" ");
|
|
|
|
one_line.add (argv[2]);
|
2000-09-03 12:16:35 +08:00
|
|
|
strcpy (real_path, argv[0]);
|
2000-10-20 12:20:21 +08:00
|
|
|
null_app_name = 1;
|
2000-02-18 03:38:33 +08:00
|
|
|
goto skip_arg_parsing;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *ext;
|
2000-09-03 12:16:35 +08:00
|
|
|
if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
|
|
|
set_errno (ENOENT);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
/* If the file name ends in either .exe, .com, .bat, or .cmd we assume
|
|
|
|
that it is NOT a script file */
|
|
|
|
while (*ext == '\0')
|
|
|
|
{
|
|
|
|
HANDLE hnd = CreateFileA (real_path,
|
|
|
|
GENERIC_READ,
|
|
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
|
&sec_none_nih,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
0);
|
|
|
|
if (hnd == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
__seterrno ();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD done;
|
|
|
|
|
|
|
|
char buf[2 * MAX_PATH + 1];
|
2001-03-19 04:58:18 +08:00
|
|
|
buf[0] = buf[1] = buf[2] = buf[sizeof (buf) - 1] = '\0';
|
2000-09-30 09:56:40 +08:00
|
|
|
if (!ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0))
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
|
|
|
CloseHandle (hnd);
|
|
|
|
__seterrno ();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle (hnd);
|
|
|
|
|
|
|
|
if (buf[0] == 'M' && buf[1] == 'Z')
|
|
|
|
break;
|
|
|
|
|
2000-09-14 03:57:00 +08:00
|
|
|
debug_printf ("%s is a script", (char *) real_path);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-09-14 03:57:00 +08:00
|
|
|
char *pgm, *arg1;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
if (buf[0] != '#' || buf[1] != '!')
|
|
|
|
{
|
2000-09-03 12:16:35 +08:00
|
|
|
pgm = (char *) "/bin/sh";
|
2000-02-18 03:38:33 +08:00
|
|
|
arg1 = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-09-14 03:57:00 +08:00
|
|
|
char *ptr;
|
2000-02-18 03:38:33 +08:00
|
|
|
pgm = buf + 2;
|
|
|
|
pgm += strspn (pgm, " \t");
|
|
|
|
for (ptr = pgm, arg1 = NULL;
|
|
|
|
*ptr && *ptr != '\r' && *ptr != '\n';
|
|
|
|
ptr++)
|
|
|
|
if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
|
|
|
|
{
|
|
|
|
/* Null terminate the initial command and step over
|
|
|
|
any additional white space. If we've hit the
|
2000-09-30 09:56:40 +08:00
|
|
|
end of the line, exit the loop. Otherwise, we've
|
|
|
|
found the first argument. Position the current
|
2000-02-18 03:38:33 +08:00
|
|
|
pointer on the last known white space. */
|
|
|
|
*ptr = '\0';
|
|
|
|
char *newptr = ptr + 1;
|
|
|
|
newptr += strspn (newptr, " \t");
|
|
|
|
if (!*newptr || *newptr == '\r' || *newptr == '\n')
|
|
|
|
break;
|
|
|
|
arg1 = newptr;
|
|
|
|
ptr = newptr - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ptr = '\0';
|
|
|
|
}
|
|
|
|
|
2000-09-14 03:57:00 +08:00
|
|
|
/* Replace argv[0] with the full path to the script if this is the
|
|
|
|
first time through the loop. */
|
2000-09-19 21:48:52 +08:00
|
|
|
newargv.replace0_maybe (prog_arg);
|
2000-10-28 13:41:44 +08:00
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
/* pointers:
|
|
|
|
* pgm interpreter name
|
|
|
|
* arg1 optional string
|
|
|
|
*/
|
2000-09-03 12:16:35 +08:00
|
|
|
if (arg1)
|
|
|
|
newargv.unshift (arg1);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-10-31 08:55:32 +08:00
|
|
|
/* FIXME: This should not be using FE_NATIVE. It should be putting
|
2001-11-05 14:09:15 +08:00
|
|
|
the posix path on the argv list. */
|
2001-10-31 08:55:32 +08:00
|
|
|
find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext);
|
2000-09-03 12:16:35 +08:00
|
|
|
newargv.unshift (real_path, 1);
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
2000-09-03 12:16:35 +08:00
|
|
|
if (real_path.iscygexec ())
|
2000-09-14 03:57:00 +08:00
|
|
|
newargv.dup_all ();
|
2000-09-03 12:16:35 +08:00
|
|
|
else
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2000-09-03 12:16:35 +08:00
|
|
|
for (int i = 0; i < newargv.argc; i++)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2000-09-03 12:16:35 +08:00
|
|
|
char *p = NULL;
|
|
|
|
const char *a;
|
|
|
|
|
2000-09-14 03:57:00 +08:00
|
|
|
newargv.dup_maybe (i);
|
2000-09-26 08:52:21 +08:00
|
|
|
a = i ? newargv[i] : (char *) real_path;
|
2000-09-03 12:16:35 +08:00
|
|
|
int len = strlen (a);
|
|
|
|
if (len != 0 && !strpbrk (a, " \t\n\r\""))
|
|
|
|
one_line.add (a, len);
|
|
|
|
else
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2000-09-03 12:16:35 +08:00
|
|
|
one_line.add ("\"", 1);
|
2000-11-21 06:10:13 +08:00
|
|
|
/* Handle embedded special characters " and \.
|
|
|
|
A " is always preceded by a \.
|
|
|
|
A \ is not special unless it precedes a ". If it does,
|
|
|
|
then all preceding \'s must be doubled to avoid having
|
|
|
|
the Windows command line parser interpret the \ as quoting
|
|
|
|
the ". This rule applies to a string of \'s before the end
|
|
|
|
of the string, since cygwin/windows uses a " to delimit the
|
|
|
|
argument. */
|
2000-09-03 12:16:35 +08:00
|
|
|
for (; (p = strpbrk (a, "\"\\")); a = ++p)
|
|
|
|
{
|
|
|
|
one_line.add (a, p - a);
|
2000-11-21 06:10:13 +08:00
|
|
|
/* Find length of string of backslashes */
|
|
|
|
int n = strspn (p, "\\");
|
|
|
|
if (!n)
|
|
|
|
one_line.add ("\\\"", 2); /* No backslashes, so it must be a ".
|
|
|
|
The " has to be protected with a backslash. */
|
|
|
|
else
|
2000-11-21 03:35:45 +08:00
|
|
|
{
|
2000-11-21 06:10:13 +08:00
|
|
|
one_line.add (p, n); /* Add the run of backslashes */
|
|
|
|
/* Need to double up all of the preceding
|
|
|
|
backslashes if they precede a quote or EOS. */
|
|
|
|
if (!p[n] || p[n] == '"')
|
|
|
|
one_line.add (p, n);
|
|
|
|
p += n - 1; /* Point to last backslash */
|
2000-11-21 03:35:45 +08:00
|
|
|
}
|
2000-09-03 12:16:35 +08:00
|
|
|
}
|
|
|
|
if (*a)
|
|
|
|
one_line.add (a);
|
|
|
|
one_line.add ("\"", 1);
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
2000-09-03 12:16:35 +08:00
|
|
|
MALLOC_CHECK;
|
|
|
|
one_line.add (" ", 1);
|
|
|
|
MALLOC_CHECK;
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
2000-09-03 12:16:35 +08:00
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
MALLOC_CHECK;
|
2000-09-03 12:16:35 +08:00
|
|
|
if (one_line.ix)
|
|
|
|
one_line.buf[one_line.ix - 1] = '\0';
|
|
|
|
else
|
|
|
|
one_line.add ("", 1);
|
2000-02-18 03:38:33 +08:00
|
|
|
MALLOC_CHECK;
|
|
|
|
}
|
2000-09-14 03:57:00 +08:00
|
|
|
|
|
|
|
newargv.all_calloced ();
|
2000-09-03 12:16:35 +08:00
|
|
|
ciresrv.moreinfo->argc = newargv.argc;
|
|
|
|
ciresrv.moreinfo->argv = newargv;
|
|
|
|
|
2000-11-09 04:36:37 +08:00
|
|
|
ciresrv.moreinfo->envc = envsize (envp, 1);
|
2000-10-17 07:55:58 +08:00
|
|
|
ciresrv.moreinfo->envp = (char **) cmalloc (HEAP_1_ARGV, ciresrv.moreinfo->envc);
|
|
|
|
ciresrv.hexec_proc = hexec_proc;
|
2000-09-03 12:16:35 +08:00
|
|
|
char **c;
|
|
|
|
const char * const *e;
|
2000-10-17 07:55:58 +08:00
|
|
|
for (c = ciresrv.moreinfo->envp, e = envp; *e;)
|
2000-09-27 13:12:09 +08:00
|
|
|
*c++ = cstrdup1 (*e++);
|
2000-09-03 12:16:35 +08:00
|
|
|
*c = NULL;
|
|
|
|
if (mode != _P_OVERLAY ||
|
2001-07-19 01:05:34 +08:00
|
|
|
!DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc,
|
|
|
|
&ciresrv.moreinfo->myself_pinfo, 0,
|
2000-09-03 12:16:35 +08:00
|
|
|
TRUE, DUPLICATE_SAME_ACCESS))
|
|
|
|
ciresrv.moreinfo->myself_pinfo = NULL;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
skip_arg_parsing:
|
2000-02-23 12:07:13 +08:00
|
|
|
PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
|
2000-02-18 03:38:33 +08:00
|
|
|
si.lpReserved = NULL;
|
|
|
|
si.lpDesktop = NULL;
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
si.hStdInput = handle (0, 0); /* Get input handle */
|
|
|
|
si.hStdOutput = handle (1, 1); /* Get output handle */
|
|
|
|
si.hStdError = handle (2, 1); /* Get output handle */
|
|
|
|
si.cb = sizeof (si);
|
|
|
|
|
2000-10-17 07:55:58 +08:00
|
|
|
int flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (hMainProc);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
if (mode == _P_DETACH || !set_console_state_for_spawn ())
|
|
|
|
flags |= DETACHED_PROCESS;
|
2000-10-17 07:55:58 +08:00
|
|
|
if (mode != _P_OVERLAY)
|
|
|
|
flags |= CREATE_SUSPENDED;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-10-26 18:13:41 +08:00
|
|
|
/* Some file types (currently only sockets) need extra effort in the
|
|
|
|
parent after CreateProcess and before copying the datastructures
|
|
|
|
to the child. So we have to start the child in suspend state,
|
|
|
|
unfortunately, to avoid a race condition. */
|
2001-04-19 05:10:15 +08:00
|
|
|
if (cygheap->fdtab.need_fixup_before ())
|
2000-10-26 18:13:41 +08:00
|
|
|
flags |= CREATE_SUSPENDED;
|
|
|
|
|
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
/* Build windows style environment list */
|
2000-09-03 12:16:35 +08:00
|
|
|
char *envblock;
|
|
|
|
if (real_path.iscygexec ())
|
|
|
|
envblock = NULL;
|
|
|
|
else
|
2000-11-15 17:01:33 +08:00
|
|
|
envblock = winenv (envp, 0);
|
2000-09-03 12:16:35 +08:00
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
/* Preallocated buffer for `sec_user' call */
|
|
|
|
char sa_buf[1024];
|
|
|
|
|
2001-05-01 05:19:42 +08:00
|
|
|
if (!hToken && cygheap->user.impersonated
|
|
|
|
&& cygheap->user.token != INVALID_HANDLE_VALUE)
|
2000-11-16 05:04:02 +08:00
|
|
|
hToken = cygheap->user.token;
|
2000-06-17 03:36:07 +08:00
|
|
|
|
2000-10-20 12:20:21 +08:00
|
|
|
const char *runpath = null_app_name ? NULL : (const char *) real_path;
|
|
|
|
|
|
|
|
syscall_printf ("spawn_guts null_app_name %d (%s, %.132s)", null_app_name, runpath, one_line.buf);
|
|
|
|
|
2001-09-14 08:49:00 +08:00
|
|
|
void *newheap;
|
2000-10-20 12:20:21 +08:00
|
|
|
cygbench ("spawn-guts");
|
2000-10-17 07:55:58 +08:00
|
|
|
if (!hToken)
|
|
|
|
{
|
|
|
|
ciresrv.moreinfo->uid = getuid ();
|
2001-01-28 13:51:15 +08:00
|
|
|
/* FIXME: This leaks a handle in the CreateProcessAsUser case since the
|
|
|
|
child process doesn't know about cygwin_mount_h. */
|
|
|
|
ciresrv.mount_h = cygwin_mount_h;
|
2001-09-14 08:49:00 +08:00
|
|
|
newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
|
2000-10-20 12:20:21 +08:00
|
|
|
rc = CreateProcess (runpath, /* image name - with full path */
|
2000-10-17 07:55:58 +08:00
|
|
|
one_line.buf, /* what was passed to exec */
|
|
|
|
/* process security attrs */
|
2002-02-19 13:58:44 +08:00
|
|
|
sec_user_nih (sa_buf),
|
2000-10-17 07:55:58 +08:00
|
|
|
/* thread security attrs */
|
2002-02-19 13:58:44 +08:00
|
|
|
sec_user_nih (sa_buf),
|
2000-10-17 07:55:58 +08:00
|
|
|
TRUE, /* inherit handles from parent */
|
|
|
|
flags,
|
|
|
|
envblock,/* environment */
|
|
|
|
0, /* use current drive/directory */
|
|
|
|
&si,
|
|
|
|
&pi);
|
|
|
|
}
|
|
|
|
else
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2001-05-01 05:19:42 +08:00
|
|
|
cygsid sid;
|
|
|
|
DWORD ret_len;
|
|
|
|
if (!GetTokenInformation (hToken, TokenUser, &sid, sizeof sid, &ret_len))
|
2001-09-08 05:32:07 +08:00
|
|
|
{
|
* fork.cc (fork): Eliminate superfluous call to getuid().
* security.h: New define `NO_SID'. Remove declarations of functions
moved to methods into class cygsid.
(class cygsid): Declare new methods `getfromstr', `get_sid',
`getfrompw', `getfromgr', `get_rid', `get_uid', `get_gid', `string'
and new constructors and operators =, == and !=.
Declare new global cygsids `well_known_XXX_sid' substituting the
corresponding `get_XXX_sid' functions. Remove declarations of
these functions.
* sec_helper.cc (well_known_admin_sid): New global variable.
(well_known_system_sid): Ditto
(well_known_creator_owner_sid): Ditto
(well_known_world_sid): Ditto
(cygsid::string): New method, substituting `convert_sid_to_string_sid'.
(cygsid::get_sid): New method, substituting `get_sid'.
(cygsid::getfromstr): New method, substituting
`convert_string_sid_to_sid'.
(cygsid::getfrompw): New method, substituting `get_pw_sid'.
(cygsid::getfromgr): New method, substituting `get_gr_sid'.
(cygsid::get_id): New method, substituting `get_id_from_sid'.
(get_admin_sid): Eliminated.
(get_system_sid): Ditto.
(get_creator_owner_sid): Ditto.
(get_world_sid): Ditto.
* grp.cc: Use new cygsid methods and well known sids throughout.
* registry.cc: Ditto.
* sec_acl.cc: Ditto.
* security.cc: Ditto.
* shared.cc: Ditto.
* syscalls.cc (seteuid): Ditto. Eliminate redundant conditional.
* uinfo.cc (internal_getlogin): Ditto.
* spawn.cc (spawn_guts) Revert previous patch.
2001-05-16 03:23:31 +08:00
|
|
|
sid = NO_SID;
|
2001-05-01 05:19:42 +08:00
|
|
|
system_printf ("GetTokenInformation: %E");
|
|
|
|
}
|
|
|
|
/* Retrieve security attributes before setting psid to NULL
|
|
|
|
since it's value is needed by `sec_user'. */
|
2002-02-19 13:58:44 +08:00
|
|
|
PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf, sid);
|
2001-05-01 05:19:42 +08:00
|
|
|
|
|
|
|
/* Remove impersonation */
|
|
|
|
if (cygheap->user.impersonated
|
2001-09-08 05:32:07 +08:00
|
|
|
&& cygheap->user.token != INVALID_HANDLE_VALUE)
|
* autoload.cc: Add load statements for `LookupAccountNameW',
`LsaClose', `LsaEnumerateAccountRights', `LsaFreeMemory',
`LsaOpenPolicy', `LsaQueryInformationPolicy', `NetLocalGroupEnum',
`NetLocalGroupGetMembers', `NetServerEnum', `NetUserGetGroups' and
`NtCreateToken'.
* ntdll.h: Add declaration for `NtCreateToken'.
* sec_helper.cc: Add `well_known_local_sid', `well_known_dialup_sid',
`well_known_network_sid', `well_known_batch_sid',
`well_known_interactive_sid', `well_known_service_sid' and
`well_known_authenticated_users_sid'.
(cygsid::string): Define as const method.
(cygsid::get_sid): Set psid to NO_SID on error.
(cygsid::getfromstr): Ditto.
(cygsid::getfrompw): Simplify.
(cygsid::getfromgr): Check for gr == NULL.
(legal_sid_type): Move to security.h.
(set_process_privilege): Return -1 on error, otherwise 0 or 1 related
to previous privilege setting.
* security.cc (extract_nt_dom_user): Remove `static'.
(lsa2wchar): New function.
(open_local_policy): Ditto.
(close_local_policy): Ditto.
(get_lsa_srv_inf): Ditto.
(get_logon_server): Ditto.
(get_logon_server_and_user_domain): Ditto.
(get_user_groups): Ditto.
(is_group_member): Ditto.
(get_user_local_groups): Ditto.
(sid_in_token_groups): Ditto.
(get_user_primary_group): Ditto.
(get_group_sidlist): Ditto.
(get_system_priv_list): Ditto.
(get_priv_list): Ditto.
(get_dacl): Ditto.
(create_token): Ditto.
(subauth): Return immediately if SE_TCB_NAME can't be assigned.
Change all return statements in case of error to jumps to `out'
label. Add `out' label to support cleanup.
* security.h: Add extern declarations for `well_known_local_sid',
`well_known_dialup_sid', `well_known_network_sid',
`well_known_batch_sid', `well_known_interactive_sid',
`well_known_service_sid' and `well_known_authenticated_users_sid'.
Add extern declarations for functions `create_token',
`extract_nt_dom_user' and `get_logon_server_and_user_domain'.
(class cygsid): Add method `assign'. Change operator= to call new
`assign' method. Add `debug_print' method.
(class cygsidlist): New class.
(legal_sid_type): Moved from sec_helper.cc to here.
* spawn.cc (spawn_guts) Revert reversion of previous patch.
Call `RevertToSelf' and `ImpersonateLoggedOnUser' instead of `seteuid'
again.
* syscalls.cc (seteuid): Rearranged. Call `create_token' now when
needed. Call `subauth' if `create_token' fails. Try setting token
owner and primary group only if token was not explicitely created
by `create_token'.
* uinfo.cc (internal_getlogin): Try harder to generate correct user
information. Especially don't trust return value of `GetUserName'.
2001-05-20 16:10:47 +08:00
|
|
|
RevertToSelf ();
|
2001-05-01 05:19:42 +08:00
|
|
|
|
|
|
|
/* Load users registry hive. */
|
|
|
|
load_registry_hive (sid);
|
|
|
|
|
2000-02-18 03:38:33 +08:00
|
|
|
/* allow the child to interact with our window station/desktop */
|
|
|
|
HANDLE hwst, hdsk;
|
|
|
|
SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION;
|
|
|
|
DWORD n;
|
|
|
|
char wstname[1024];
|
|
|
|
char dskname[1024];
|
|
|
|
|
* (child_info.h, cygheap.h, dcrt0.cc, dir.cc, fhandler.cc, fhandler.h,
fhandler_clipboard.cc, fhandler_disk_file.cc, fhandler_dsp.cc,
fhandler_floppy.cc, fhandler_mem.cc, fhandler_random.cc,
fhandler_tape.cc, fhandler_zero.cc, grp.cc, mmap.cc, passwd.cc,
pinfo.cc, pinfo.h, pipe.cc, sec_acl.cc, sec_helper.cc, security.cc,
security.h, spawn.cc, syscalls.cc, thread.h, uinfo.cc, winsup.h):
Change usage of uid_t to __uid16_t, gid_t to __gid16_t and
off_t to __off32_t throughout. Use INVALID_UID, INVALID_GID and
INVALID_SEEK instead casting -1 to the appropriate type.
* winsup.h: Define INVALID_UID, INVALID_GID and INVALID_SEEK.
* include/cygwin/acl.h: Define internal __aclent16_t and __aclent32_t
types. Don't declare acl functions when compiling Cygwin.
* include/cygwin/grp.h: Declare getgrgid() and getgrnam() with
correct types for internal usage.
2002-02-10 21:38:51 +08:00
|
|
|
ciresrv.moreinfo->uid = ILLEGAL_UID;
|
2001-03-19 04:58:18 +08:00
|
|
|
hwst = GetProcessWindowStation ();
|
|
|
|
SetUserObjectSecurity (hwst, &dsi, get_null_sd ());
|
|
|
|
GetUserObjectInformation (hwst, UOI_NAME, wstname, 1024, &n);
|
|
|
|
hdsk = GetThreadDesktop (GetCurrentThreadId ());
|
|
|
|
SetUserObjectSecurity (hdsk, &dsi, get_null_sd ());
|
|
|
|
GetUserObjectInformation (hdsk, UOI_NAME, dskname, 1024, &n);
|
2000-02-18 03:38:33 +08:00
|
|
|
strcat (wstname, "\\");
|
|
|
|
strcat (wstname, dskname);
|
|
|
|
si.lpDesktop = wstname;
|
|
|
|
|
2001-09-14 08:49:00 +08:00
|
|
|
newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
|
2000-02-18 03:38:33 +08:00
|
|
|
rc = CreateProcessAsUser (hToken,
|
2000-10-20 12:20:21 +08:00
|
|
|
runpath, /* image name - with full path */
|
2000-02-18 03:38:33 +08:00
|
|
|
one_line.buf, /* what was passed to exec */
|
2000-09-03 12:16:35 +08:00
|
|
|
sec_attribs, /* process security attrs */
|
|
|
|
sec_attribs, /* thread security attrs */
|
2000-02-18 03:38:33 +08:00
|
|
|
TRUE, /* inherit handles from parent */
|
|
|
|
flags,
|
|
|
|
envblock,/* environment */
|
|
|
|
0, /* use current drive/directory */
|
|
|
|
&si,
|
|
|
|
&pi);
|
2000-07-20 04:27:27 +08:00
|
|
|
/* Restore impersonation. In case of _P_OVERLAY this isn't
|
2000-09-03 12:16:35 +08:00
|
|
|
allowed since it would overwrite child data. */
|
2001-10-04 10:34:20 +08:00
|
|
|
if (mode != _P_OVERLAY
|
2000-11-16 05:04:02 +08:00
|
|
|
&& cygheap->user.impersonated
|
|
|
|
&& cygheap->user.token != INVALID_HANDLE_VALUE)
|
* autoload.cc: Add load statements for `LookupAccountNameW',
`LsaClose', `LsaEnumerateAccountRights', `LsaFreeMemory',
`LsaOpenPolicy', `LsaQueryInformationPolicy', `NetLocalGroupEnum',
`NetLocalGroupGetMembers', `NetServerEnum', `NetUserGetGroups' and
`NtCreateToken'.
* ntdll.h: Add declaration for `NtCreateToken'.
* sec_helper.cc: Add `well_known_local_sid', `well_known_dialup_sid',
`well_known_network_sid', `well_known_batch_sid',
`well_known_interactive_sid', `well_known_service_sid' and
`well_known_authenticated_users_sid'.
(cygsid::string): Define as const method.
(cygsid::get_sid): Set psid to NO_SID on error.
(cygsid::getfromstr): Ditto.
(cygsid::getfrompw): Simplify.
(cygsid::getfromgr): Check for gr == NULL.
(legal_sid_type): Move to security.h.
(set_process_privilege): Return -1 on error, otherwise 0 or 1 related
to previous privilege setting.
* security.cc (extract_nt_dom_user): Remove `static'.
(lsa2wchar): New function.
(open_local_policy): Ditto.
(close_local_policy): Ditto.
(get_lsa_srv_inf): Ditto.
(get_logon_server): Ditto.
(get_logon_server_and_user_domain): Ditto.
(get_user_groups): Ditto.
(is_group_member): Ditto.
(get_user_local_groups): Ditto.
(sid_in_token_groups): Ditto.
(get_user_primary_group): Ditto.
(get_group_sidlist): Ditto.
(get_system_priv_list): Ditto.
(get_priv_list): Ditto.
(get_dacl): Ditto.
(create_token): Ditto.
(subauth): Return immediately if SE_TCB_NAME can't be assigned.
Change all return statements in case of error to jumps to `out'
label. Add `out' label to support cleanup.
* security.h: Add extern declarations for `well_known_local_sid',
`well_known_dialup_sid', `well_known_network_sid',
`well_known_batch_sid', `well_known_interactive_sid',
`well_known_service_sid' and `well_known_authenticated_users_sid'.
Add extern declarations for functions `create_token',
`extract_nt_dom_user' and `get_logon_server_and_user_domain'.
(class cygsid): Add method `assign'. Change operator= to call new
`assign' method. Add `debug_print' method.
(class cygsidlist): New class.
(legal_sid_type): Moved from sec_helper.cc to here.
* spawn.cc (spawn_guts) Revert reversion of previous patch.
Call `RevertToSelf' and `ImpersonateLoggedOnUser' instead of `seteuid'
again.
* syscalls.cc (seteuid): Rearranged. Call `create_token' now when
needed. Call `subauth' if `create_token' fails. Try setting token
owner and primary group only if token was not explicitely created
by `create_token'.
* uinfo.cc (internal_getlogin): Try harder to generate correct user
information. Especially don't trust return value of `GetUserName'.
2001-05-20 16:10:47 +08:00
|
|
|
ImpersonateLoggedOnUser (cygheap->user.token);
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
2000-09-03 12:16:35 +08:00
|
|
|
if (envblock)
|
|
|
|
free (envblock);
|
2000-02-18 03:38:33 +08:00
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
/* Set errno now so that debugging messages from it appear before our
|
|
|
|
final debugging message [this is a general rule for debugging
|
|
|
|
messages]. */
|
2000-08-03 11:02:41 +08:00
|
|
|
if (!rc)
|
|
|
|
{
|
|
|
|
__seterrno ();
|
|
|
|
syscall_printf ("CreateProcess failed, %E");
|
2000-10-20 12:20:21 +08:00
|
|
|
if (spr)
|
|
|
|
ForceCloseHandle (spr);
|
2001-09-14 08:49:00 +08:00
|
|
|
cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
|
2000-08-03 11:02:41 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-10-26 18:13:41 +08:00
|
|
|
/* Fixup the parent datastructure if needed and resume the child's
|
|
|
|
main thread. */
|
2001-09-14 08:49:00 +08:00
|
|
|
if (!cygheap->fdtab.need_fixup_before ())
|
|
|
|
cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
|
|
|
|
else
|
2000-10-26 18:13:41 +08:00
|
|
|
{
|
2001-04-19 05:10:15 +08:00
|
|
|
cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
|
2001-09-14 08:49:00 +08:00
|
|
|
cygheap_setup_for_child_cleanup (newheap, &ciresrv, 1);
|
2000-10-26 18:13:41 +08:00
|
|
|
if (mode == _P_OVERLAY)
|
2001-09-08 05:32:07 +08:00
|
|
|
ResumeThread (pi.hThread);
|
2000-10-26 18:13:41 +08:00
|
|
|
}
|
|
|
|
|
2000-07-30 00:24:59 +08:00
|
|
|
if (mode == _P_OVERLAY)
|
|
|
|
cygpid = myself->pid;
|
|
|
|
else
|
|
|
|
cygpid = cygwin_pid (pi.dwProcessId);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
/* We print the original program name here so the user can see that too. */
|
|
|
|
syscall_printf ("%d = spawn_guts (%s, %.132s)",
|
2000-10-09 10:53:44 +08:00
|
|
|
rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-07-30 00:24:59 +08:00
|
|
|
/* Name the handle similarly to proc_subproc. */
|
|
|
|
ProtectHandle1 (pi.hProcess, childhProc);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
if (mode == _P_OVERLAY)
|
|
|
|
{
|
2000-10-17 07:55:58 +08:00
|
|
|
/* These are both duplicated in the child code. We do this here,
|
|
|
|
primarily for strace. */
|
2000-10-24 10:25:27 +08:00
|
|
|
strace.execing = 1;
|
|
|
|
hExeced = pi.hProcess;
|
2002-01-10 11:21:27 +08:00
|
|
|
dwExeced = pi.dwProcessId;
|
2001-06-25 05:57:50 +08:00
|
|
|
strcpy (myself->progname, real_path);
|
2001-08-05 05:10:52 +08:00
|
|
|
close_all_files ();
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-10-25 11:54:50 +08:00
|
|
|
myself->set_has_pgid_children ();
|
2000-10-17 07:55:58 +08:00
|
|
|
ProtectHandle (pi.hThread);
|
2000-07-30 00:24:59 +08:00
|
|
|
pinfo child (cygpid, 1);
|
|
|
|
if (!child)
|
|
|
|
{
|
|
|
|
set_errno (EAGAIN);
|
|
|
|
syscall_printf ("-1 = spawnve (), process table full");
|
|
|
|
return -1;
|
|
|
|
}
|
2000-02-18 03:38:33 +08:00
|
|
|
child->dwProcessId = pi.dwProcessId;
|
|
|
|
child->hProcess = pi.hProcess;
|
2000-07-30 00:24:59 +08:00
|
|
|
child.remember ();
|
2001-06-25 05:57:50 +08:00
|
|
|
strcpy (child->progname, real_path);
|
2001-10-04 10:34:20 +08:00
|
|
|
/* FIXME: This introduces an unreferenced, open handle into the child.
|
2001-11-05 14:09:15 +08:00
|
|
|
The purpose is to keep the pid shared memory open so that all of
|
2001-10-04 10:34:20 +08:00
|
|
|
the fields filled out by child.remember do not disappear and so there
|
|
|
|
is not a brief period during which the pid is not available.
|
|
|
|
However, we should try to find another way to do this eventually. */
|
|
|
|
(void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess,
|
|
|
|
NULL, 0, 0, DUPLICATE_SAME_ACCESS);
|
2000-10-17 07:55:58 +08:00
|
|
|
/* Start the child running */
|
|
|
|
ResumeThread (pi.hThread);
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ForceCloseHandle (pi.hThread);
|
|
|
|
|
2000-10-17 07:55:58 +08:00
|
|
|
sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
|
|
|
|
|
2000-11-16 05:04:02 +08:00
|
|
|
if (hToken && hToken != cygheap->user.token)
|
2000-02-18 03:38:33 +08:00
|
|
|
CloseHandle (hToken);
|
|
|
|
|
|
|
|
DWORD res;
|
2000-10-14 13:52:38 +08:00
|
|
|
BOOL exited;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-10-14 13:52:38 +08:00
|
|
|
res = 0;
|
|
|
|
exited = FALSE;
|
|
|
|
MALLOC_CHECK;
|
2001-07-17 11:41:52 +08:00
|
|
|
if (mode == _P_OVERLAY)
|
2000-10-14 13:52:38 +08:00
|
|
|
{
|
2001-07-17 11:41:52 +08:00
|
|
|
int nwait = 3;
|
|
|
|
HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, spr};
|
|
|
|
for (int i = 0; i < 100; i++)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2001-07-17 11:41:52 +08:00
|
|
|
switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, INFINITE))
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2001-07-17 11:41:52 +08:00
|
|
|
case WAIT_OBJECT_0:
|
|
|
|
sigproc_printf ("subprocess exited");
|
|
|
|
DWORD exitcode;
|
|
|
|
if (!GetExitCodeProcess (pi.hProcess, &exitcode))
|
|
|
|
exitcode = 1;
|
|
|
|
res |= exitcode;
|
|
|
|
exited = TRUE;
|
|
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
|
|
sigproc_printf ("signal arrived");
|
|
|
|
reset_signal_arrived ();
|
|
|
|
continue;
|
|
|
|
case WAIT_OBJECT_0 + 2:
|
2001-03-12 22:49:29 +08:00
|
|
|
if (myself->ppid_handle)
|
|
|
|
res |= EXIT_REPARENTING;
|
2000-11-15 14:27:48 +08:00
|
|
|
if (!my_parent_is_alive ())
|
2000-03-15 12:49:36 +08:00
|
|
|
{
|
2001-07-17 11:41:52 +08:00
|
|
|
nwait = 2;
|
2000-10-14 13:52:38 +08:00
|
|
|
sigproc_terminate ();
|
|
|
|
continue;
|
2000-03-15 12:49:36 +08:00
|
|
|
}
|
2001-07-17 11:41:52 +08:00
|
|
|
break;
|
|
|
|
case WAIT_FAILED:
|
|
|
|
system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E",
|
|
|
|
nwait, myself->pid, myself->dwProcessId);
|
|
|
|
system_printf ("waitbuf[0] %p %d", waitbuf[0],
|
|
|
|
WaitForSingleObject (waitbuf[0], 0));
|
|
|
|
system_printf ("waitbuf[1] %p = %d", waitbuf[1],
|
|
|
|
WaitForSingleObject (waitbuf[1], 0));
|
|
|
|
system_printf ("waitbuf[w] %p = %d", waitbuf[2],
|
|
|
|
WaitForSingleObject (waitbuf[2], 0));
|
|
|
|
set_errno (ECHILD);
|
|
|
|
try_to_debug ();
|
|
|
|
return -1;
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2001-07-17 11:41:52 +08:00
|
|
|
ForceCloseHandle (spr);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-07-17 11:41:52 +08:00
|
|
|
sigproc_printf ("res = %x", res);
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2001-07-17 11:41:52 +08:00
|
|
|
if (res & EXIT_REPARENTING)
|
2000-02-18 03:38:33 +08:00
|
|
|
{
|
2001-07-17 11:41:52 +08:00
|
|
|
/* Try to reparent child process.
|
|
|
|
* Make handles to child available to parent process and exit with
|
|
|
|
* EXIT_REPARENTING status. Wait() syscall in parent will then wait
|
|
|
|
* for newly created child.
|
|
|
|
*/
|
|
|
|
HANDLE oldh = myself->hProcess;
|
|
|
|
HANDLE h = myself->ppid_handle;
|
|
|
|
sigproc_printf ("parent handle %p", h);
|
|
|
|
int rc = DuplicateHandle (hMainProc, pi.hProcess, h, &myself->hProcess,
|
|
|
|
0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
|
|
sigproc_printf ("%d = DuplicateHandle, oldh %p, newh %p",
|
|
|
|
rc, oldh, myself->hProcess);
|
|
|
|
if (!rc && my_parent_is_alive ())
|
|
|
|
{
|
|
|
|
system_printf ("Reparent failed, parent handle %p, %E", h);
|
|
|
|
system_printf ("my dwProcessId %d, myself->dwProcessId %d",
|
|
|
|
GetCurrentProcessId (), myself->dwProcessId);
|
|
|
|
system_printf ("old hProcess %p, hProcess %p", oldh, myself->hProcess);
|
|
|
|
}
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
2000-10-14 13:52:38 +08:00
|
|
|
|
2001-07-17 11:41:52 +08:00
|
|
|
}
|
2001-06-27 05:03:08 +08:00
|
|
|
|
2000-10-14 13:52:38 +08:00
|
|
|
MALLOC_CHECK;
|
2000-02-18 03:38:33 +08:00
|
|
|
|
2000-09-03 12:16:35 +08:00
|
|
|
switch (mode)
|
|
|
|
{
|
2000-10-14 13:52:38 +08:00
|
|
|
case _P_OVERLAY:
|
2000-10-17 07:55:58 +08:00
|
|
|
ForceCloseHandle1 (pi.hProcess, childhProc);
|
2000-10-14 13:52:38 +08:00
|
|
|
proc_terminate ();
|
2000-10-21 13:53:43 +08:00
|
|
|
myself->exit (res, 1);
|
2000-10-14 13:52:38 +08:00
|
|
|
break;
|
2000-09-03 12:16:35 +08:00
|
|
|
case _P_WAIT:
|
|
|
|
waitpid (cygpid, (int *) &res, 0);
|
|
|
|
break;
|
|
|
|
case _P_DETACH:
|
|
|
|
res = 0; /* Lose all memory of this child. */
|
|
|
|
break;
|
|
|
|
case _P_NOWAIT:
|
|
|
|
case _P_NOWAITO:
|
|
|
|
case _P_VFORK:
|
|
|
|
res = cygpid;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2000-02-18 03:38:33 +08:00
|
|
|
|
|
|
|
return (int) res;
|
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
cwait (int *result, int pid, int)
|
|
|
|
{
|
|
|
|
return waitpid (pid, result, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper function for spawn runtime calls.
|
|
|
|
* Doesn't search the path.
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" int
|
|
|
|
_spawnve (HANDLE hToken, int mode, const char *path, const char *const *argv,
|
|
|
|
const char *const *envp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
vfork_save *vf = vfork_storage.val ();
|
|
|
|
|
|
|
|
if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
|
|
|
|
mode = _P_NOWAIT;
|
|
|
|
else
|
|
|
|
vf = NULL;
|
|
|
|
|
|
|
|
syscall_printf ("_spawnve (%s, %s, %x)", path, argv[0], envp);
|
|
|
|
|
|
|
|
switch (mode)
|
|
|
|
{
|
2000-09-03 12:16:35 +08:00
|
|
|
case _P_OVERLAY:
|
|
|
|
/* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
|
|
|
|
/* Just act as an exec if _P_OVERLAY set. */
|
|
|
|
spawn_guts (hToken, path, argv, envp, mode);
|
|
|
|
/* Errno should be set by spawn_guts. */
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
case _P_VFORK:
|
|
|
|
case _P_NOWAIT:
|
|
|
|
case _P_NOWAITO:
|
|
|
|
case _P_WAIT:
|
|
|
|
case _P_DETACH:
|
|
|
|
subproc_init ();
|
2000-10-14 13:52:38 +08:00
|
|
|
ret = spawn_guts (hToken, path, argv, envp, mode);
|
2000-09-03 12:16:35 +08:00
|
|
|
if (vf && ret > 0)
|
|
|
|
{
|
2001-06-25 05:57:50 +08:00
|
|
|
debug_printf ("longjmping due to vfork");
|
2000-09-03 12:16:35 +08:00
|
|
|
vf->pid = ret;
|
|
|
|
longjmp (vf->j, 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
set_errno (EINVAL);
|
|
|
|
ret = -1;
|
|
|
|
break;
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* spawn functions as implemented in the MS runtime library.
|
|
|
|
* Most of these based on (and copied from) newlib/libc/posix/execXX.c
|
|
|
|
*/
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnl (int mode, const char *path, const char *arg0, ...)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
va_list args;
|
|
|
|
const char *argv[256];
|
|
|
|
|
|
|
|
va_start (args, arg0);
|
|
|
|
argv[0] = arg0;
|
|
|
|
i = 1;
|
|
|
|
|
|
|
|
do
|
|
|
|
argv[i] = va_arg (args, const char *);
|
|
|
|
while (argv[i++] != NULL);
|
|
|
|
|
|
|
|
va_end (args);
|
|
|
|
|
2000-08-03 00:28:18 +08:00
|
|
|
return _spawnve (NULL, mode, path, (char * const *) argv, cur_environ ());
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnle (int mode, const char *path, const char *arg0, ...)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
va_list args;
|
|
|
|
const char * const *envp;
|
|
|
|
const char *argv[256];
|
|
|
|
|
|
|
|
va_start (args, arg0);
|
|
|
|
argv[0] = arg0;
|
|
|
|
i = 1;
|
|
|
|
|
|
|
|
do
|
|
|
|
argv[i] = va_arg (args, const char *);
|
|
|
|
while (argv[i++] != NULL);
|
|
|
|
|
|
|
|
envp = va_arg (args, const char * const *);
|
|
|
|
va_end (args);
|
|
|
|
|
|
|
|
return _spawnve (NULL, mode, path, (char * const *) argv,
|
|
|
|
(char * const *) envp);
|
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnlp (int mode, const char *path, const char *arg0, ...)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
va_list args;
|
|
|
|
const char *argv[256];
|
|
|
|
|
|
|
|
va_start (args, arg0);
|
|
|
|
argv[0] = arg0;
|
|
|
|
i = 1;
|
|
|
|
|
|
|
|
do
|
|
|
|
argv[i] = va_arg (args, const char *);
|
|
|
|
while (argv[i++] != NULL);
|
|
|
|
|
|
|
|
va_end (args);
|
|
|
|
|
2000-08-03 00:28:18 +08:00
|
|
|
return spawnvpe (mode, path, (char * const *) argv, cur_environ ());
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnlpe (int mode, const char *path, const char *arg0, ...)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
va_list args;
|
|
|
|
const char * const *envp;
|
|
|
|
const char *argv[256];
|
|
|
|
|
|
|
|
va_start (args, arg0);
|
|
|
|
argv[0] = arg0;
|
|
|
|
i = 1;
|
|
|
|
|
|
|
|
do
|
|
|
|
argv[i] = va_arg (args, const char *);
|
|
|
|
while (argv[i++] != NULL);
|
|
|
|
|
|
|
|
envp = va_arg (args, const char * const *);
|
|
|
|
va_end (args);
|
|
|
|
|
|
|
|
return spawnvpe (mode, path, (char * const *) argv, envp);
|
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnv (int mode, const char *path, const char * const *argv)
|
|
|
|
{
|
2000-08-03 00:28:18 +08:00
|
|
|
return _spawnve (NULL, mode, path, argv, cur_environ ());
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnve (int mode, const char *path, char * const *argv,
|
|
|
|
const char * const *envp)
|
|
|
|
{
|
|
|
|
return _spawnve (NULL, mode, path, argv, envp);
|
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnvp (int mode, const char *path, const char * const *argv)
|
|
|
|
{
|
2000-08-03 00:28:18 +08:00
|
|
|
return spawnvpe (mode, path, argv, cur_environ ());
|
2000-02-18 03:38:33 +08:00
|
|
|
}
|
|
|
|
|
2001-08-23 01:50:22 +08:00
|
|
|
extern "C" int
|
2000-02-18 03:38:33 +08:00
|
|
|
spawnvpe (int mode, const char *file, const char * const *argv,
|
|
|
|
const char * const *envp)
|
|
|
|
{
|
2000-04-26 13:13:32 +08:00
|
|
|
path_conv buf;
|
2000-02-18 03:38:33 +08:00
|
|
|
return _spawnve (NULL, mode, find_exec (file, buf), argv, envp);
|
|
|
|
}
|