ntdll: Move fork and exec support to the Unix library.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
22970932d0
commit
412555e0cd
|
@ -53,6 +53,7 @@ C_SRCS = \
|
|||
unix/debug.c \
|
||||
unix/env.c \
|
||||
unix/loader.c \
|
||||
unix/process.c \
|
||||
unix/server.c \
|
||||
unix/signal_arm.c \
|
||||
unix/signal_arm64.c \
|
||||
|
|
|
@ -840,133 +840,6 @@ NTSTATUS WINAPI NtSuspendProcess( HANDLE handle )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* build_argv
|
||||
*
|
||||
* Build an argv array from a command-line.
|
||||
* 'reserved' is the number of args to reserve before the first one.
|
||||
*/
|
||||
static char **build_argv( const UNICODE_STRING *cmdlineW, int reserved )
|
||||
{
|
||||
int argc;
|
||||
char **argv;
|
||||
char *arg, *s, *d, *cmdline;
|
||||
int in_quotes, bcount, len;
|
||||
|
||||
len = cmdlineW->Length / sizeof(WCHAR);
|
||||
if (!(cmdline = RtlAllocateHeap( GetProcessHeap(), 0, len * 3 + 1 ))) return NULL;
|
||||
len = ntdll_wcstoumbs( cmdlineW->Buffer, len, cmdline, len * 3, FALSE );
|
||||
cmdline[len++] = 0;
|
||||
|
||||
argc = reserved + 1;
|
||||
bcount = 0;
|
||||
in_quotes = 0;
|
||||
s = cmdline;
|
||||
while (1)
|
||||
{
|
||||
if (*s == '\0' || ((*s == ' ' || *s == '\t') && !in_quotes))
|
||||
{
|
||||
/* space */
|
||||
argc++;
|
||||
/* skip the remaining spaces */
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == '\0') break;
|
||||
bcount = 0;
|
||||
continue;
|
||||
}
|
||||
else if (*s == '\\') bcount++; /* '\', count them */
|
||||
else if ((*s == '"') && ((bcount & 1) == 0))
|
||||
{
|
||||
if (in_quotes && s[1] == '"') s++;
|
||||
else
|
||||
{
|
||||
/* unescaped '"' */
|
||||
in_quotes = !in_quotes;
|
||||
bcount = 0;
|
||||
}
|
||||
}
|
||||
else bcount = 0; /* a regular character */
|
||||
s++;
|
||||
}
|
||||
if (!(argv = RtlAllocateHeap( GetProcessHeap(), 0, argc * sizeof(*argv) + len )))
|
||||
{
|
||||
RtlFreeHeap( GetProcessHeap(), 0, cmdline );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
arg = d = s = (char *)(argv + argc);
|
||||
memcpy( d, cmdline, len );
|
||||
bcount = 0;
|
||||
in_quotes = 0;
|
||||
argc = reserved;
|
||||
while (*s)
|
||||
{
|
||||
if ((*s == ' ' || *s == '\t') && !in_quotes)
|
||||
{
|
||||
/* Close the argument and copy it */
|
||||
*d = 0;
|
||||
argv[argc++] = arg;
|
||||
/* skip the remaining spaces */
|
||||
do
|
||||
{
|
||||
s++;
|
||||
} while (*s == ' ' || *s == '\t');
|
||||
|
||||
/* Start with a new argument */
|
||||
arg = d = s;
|
||||
bcount = 0;
|
||||
}
|
||||
else if (*s == '\\')
|
||||
{
|
||||
*d++ = *s++;
|
||||
bcount++;
|
||||
}
|
||||
else if (*s == '"')
|
||||
{
|
||||
if ((bcount & 1) == 0)
|
||||
{
|
||||
/* Preceded by an even number of '\', this is half that
|
||||
* number of '\', plus a '"' which we discard.
|
||||
*/
|
||||
d -= bcount/2;
|
||||
s++;
|
||||
if (in_quotes && *s == '"')
|
||||
{
|
||||
*d++ = '"';
|
||||
s++;
|
||||
}
|
||||
else in_quotes = !in_quotes;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Preceded by an odd number of '\', this is half that
|
||||
* number of '\' followed by a '"'
|
||||
*/
|
||||
d = d - bcount / 2 - 1;
|
||||
*d++ = '"';
|
||||
s++;
|
||||
}
|
||||
bcount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* a regular character */
|
||||
*d++ = *s++;
|
||||
bcount = 0;
|
||||
}
|
||||
}
|
||||
if (*arg)
|
||||
{
|
||||
*d = '\0';
|
||||
argv[argc++] = arg;
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
RtlFreeHeap( GetProcessHeap(), 0, cmdline );
|
||||
return argv;
|
||||
}
|
||||
|
||||
|
||||
static inline const WCHAR *get_params_string( const RTL_USER_PROCESS_PARAMETERS *params,
|
||||
const UNICODE_STRING *str )
|
||||
{
|
||||
|
@ -1035,136 +908,6 @@ static startup_info_t *create_startup_info( const RTL_USER_PROCESS_PARAMETERS *p
|
|||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
/***********************************************************************
|
||||
* terminate_main_thread
|
||||
*
|
||||
* On some versions of Mac OS X, the execve system call fails with
|
||||
* ENOTSUP if the process has multiple threads. Wine is always multi-
|
||||
* threaded on Mac OS X because it specifically reserves the main thread
|
||||
* for use by the system frameworks (see apple_main_thread() in
|
||||
* libs/wine/loader.c). So, when we need to exec without first forking,
|
||||
* we need to terminate the main thread first. We do this by installing
|
||||
* a custom run loop source onto the main run loop and signaling it.
|
||||
* The source's "perform" callback is pthread_exit and it will be
|
||||
* executed on the main thread, terminating it.
|
||||
*
|
||||
* Returns TRUE if there's still hope the main thread has terminated or
|
||||
* will soon. Return FALSE if we've given up.
|
||||
*/
|
||||
static BOOL terminate_main_thread(void)
|
||||
{
|
||||
static int delayms;
|
||||
|
||||
if (!delayms)
|
||||
{
|
||||
CFRunLoopSourceContext source_context = { 0 };
|
||||
CFRunLoopSourceRef source;
|
||||
|
||||
source_context.perform = pthread_exit;
|
||||
if (!(source = CFRunLoopSourceCreate( NULL, 0, &source_context )))
|
||||
return FALSE;
|
||||
|
||||
CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
|
||||
CFRunLoopSourceSignal( source );
|
||||
CFRunLoopWakeUp( CFRunLoopGetMain() );
|
||||
CFRelease( source );
|
||||
|
||||
delayms = 20;
|
||||
}
|
||||
|
||||
if (delayms > 1000)
|
||||
return FALSE;
|
||||
|
||||
usleep(delayms * 1000);
|
||||
delayms *= 2;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* set_stdio_fd
|
||||
*/
|
||||
static void set_stdio_fd( int stdin_fd, int stdout_fd )
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
if (stdin_fd == -1 || stdout_fd == -1)
|
||||
{
|
||||
fd = open( "/dev/null", O_RDWR );
|
||||
if (stdin_fd == -1) stdin_fd = fd;
|
||||
if (stdout_fd == -1) stdout_fd = fd;
|
||||
}
|
||||
|
||||
dup2( stdin_fd, 0 );
|
||||
dup2( stdout_fd, 1 );
|
||||
if (fd != -1) close( fd );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* spawn_loader
|
||||
*/
|
||||
static NTSTATUS spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int socketfd,
|
||||
const char *unixdir, char *winedebug, const pe_image_info_t *pe_info )
|
||||
{
|
||||
const int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64);
|
||||
pid_t pid;
|
||||
int stdin_fd = -1, stdout_fd = -1;
|
||||
char **argv;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
|
||||
argv = build_argv( ¶ms->CommandLine, 2 );
|
||||
|
||||
wine_server_handle_to_fd( params->hStdInput, FILE_READ_DATA, &stdin_fd, NULL );
|
||||
wine_server_handle_to_fd( params->hStdOutput, FILE_WRITE_DATA, &stdout_fd, NULL );
|
||||
|
||||
if (!(pid = fork())) /* child */
|
||||
{
|
||||
if (!(pid = fork())) /* grandchild */
|
||||
{
|
||||
if (params->ConsoleFlags ||
|
||||
params->ConsoleHandle == (HANDLE)1 /* KERNEL32_CONSOLE_ALLOC */ ||
|
||||
(params->hStdInput == INVALID_HANDLE_VALUE && params->hStdOutput == INVALID_HANDLE_VALUE))
|
||||
{
|
||||
setsid();
|
||||
set_stdio_fd( -1, -1 ); /* close stdin and stdout */
|
||||
}
|
||||
else set_stdio_fd( stdin_fd, stdout_fd );
|
||||
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
|
||||
if (winedebug) putenv( winedebug );
|
||||
if (unixdir) chdir( unixdir );
|
||||
|
||||
unix_funcs->exec_wineloader( argv, socketfd, is_child_64bit,
|
||||
pe_info->base, pe_info->base + pe_info->map_size );
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
_exit(pid == -1);
|
||||
}
|
||||
|
||||
if (pid != -1)
|
||||
{
|
||||
/* reap child */
|
||||
pid_t wret;
|
||||
do {
|
||||
wret = waitpid(pid, NULL, 0);
|
||||
} while (wret < 0 && errno == EINTR);
|
||||
}
|
||||
else status = FILE_GetNtStatus();
|
||||
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
RtlFreeHeap( GetProcessHeap(), 0, argv );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* is_builtin_path
|
||||
*/
|
||||
|
@ -1404,9 +1147,6 @@ static char *get_unix_curdir( const RTL_USER_PROCESS_PARAMETERS *params )
|
|||
*/
|
||||
static NTSTATUS fork_and_exec( UNICODE_STRING *path, const RTL_USER_PROCESS_PARAMETERS *params )
|
||||
{
|
||||
pid_t pid;
|
||||
int fd[2], stdin_fd = -1, stdout_fd = -1;
|
||||
char **argv, **envp;
|
||||
char *unixdir;
|
||||
ANSI_STRING unix_name;
|
||||
NTSTATUS status;
|
||||
|
@ -1414,79 +1154,8 @@ static NTSTATUS fork_and_exec( UNICODE_STRING *path, const RTL_USER_PROCESS_PARA
|
|||
status = wine_nt_to_unix_file_name( path, &unix_name, FILE_OPEN, FALSE );
|
||||
if (status) return status;
|
||||
|
||||
#ifdef HAVE_PIPE2
|
||||
if (pipe2( fd, O_CLOEXEC ) == -1)
|
||||
#endif
|
||||
{
|
||||
if (pipe(fd) == -1)
|
||||
{
|
||||
RtlFreeAnsiString( &unix_name );
|
||||
return STATUS_TOO_MANY_OPENED_FILES;
|
||||
}
|
||||
fcntl( fd[0], F_SETFD, FD_CLOEXEC );
|
||||
fcntl( fd[1], F_SETFD, FD_CLOEXEC );
|
||||
}
|
||||
|
||||
wine_server_handle_to_fd( params->hStdInput, FILE_READ_DATA, &stdin_fd, NULL );
|
||||
wine_server_handle_to_fd( params->hStdOutput, FILE_WRITE_DATA, &stdout_fd, NULL );
|
||||
|
||||
argv = build_argv( ¶ms->CommandLine, 0 );
|
||||
envp = build_envp( params->Environment );
|
||||
unixdir = get_unix_curdir( params );
|
||||
|
||||
if (!(pid = fork())) /* child */
|
||||
{
|
||||
if (!(pid = fork())) /* grandchild */
|
||||
{
|
||||
close( fd[0] );
|
||||
|
||||
if (params->ConsoleFlags ||
|
||||
params->ConsoleHandle == (HANDLE)1 /* KERNEL32_CONSOLE_ALLOC */ ||
|
||||
(params->hStdInput == INVALID_HANDLE_VALUE && params->hStdOutput == INVALID_HANDLE_VALUE))
|
||||
{
|
||||
setsid();
|
||||
set_stdio_fd( -1, -1 ); /* close stdin and stdout */
|
||||
}
|
||||
else set_stdio_fd( stdin_fd, stdout_fd );
|
||||
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
|
||||
/* Reset signals that we previously set to SIG_IGN */
|
||||
signal( SIGPIPE, SIG_DFL );
|
||||
|
||||
if (unixdir) chdir( unixdir );
|
||||
|
||||
if (argv && envp) execve( unix_name.Buffer, argv, envp );
|
||||
}
|
||||
|
||||
if (pid <= 0) /* grandchild if exec failed or child if fork failed */
|
||||
{
|
||||
status = FILE_GetNtStatus();
|
||||
write( fd[1], &status, sizeof(status) );
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
_exit(0); /* child if fork succeeded */
|
||||
}
|
||||
close( fd[1] );
|
||||
|
||||
if (pid != -1)
|
||||
{
|
||||
/* reap child */
|
||||
pid_t wret;
|
||||
do {
|
||||
wret = waitpid(pid, NULL, 0);
|
||||
} while (wret < 0 && errno == EINTR);
|
||||
read( fd[0], &status, sizeof(status) ); /* if we read something, exec or second fork failed */
|
||||
}
|
||||
else status = FILE_GetNtStatus();
|
||||
|
||||
close( fd[0] );
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
RtlFreeHeap( GetProcessHeap(), 0, argv );
|
||||
RtlFreeHeap( GetProcessHeap(), 0, envp );
|
||||
status = unix_funcs->fork_and_exec( unix_name.Buffer, unixdir, params );
|
||||
RtlFreeHeap( GetProcessHeap(), 0, unixdir );
|
||||
RtlFreeAnsiString( &unix_name );
|
||||
return status;
|
||||
|
@ -1503,7 +1172,6 @@ NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status )
|
|||
static const WCHAR comW[] = {'.','c','o','m',0};
|
||||
static const WCHAR pifW[] = {'.','p','i','f',0};
|
||||
|
||||
int socketfd[2];
|
||||
WCHAR *p, *cmdline;
|
||||
UNICODE_STRING strW;
|
||||
pe_image_info_t pe_info;
|
||||
|
@ -1548,49 +1216,7 @@ NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status )
|
|||
return status;
|
||||
}
|
||||
|
||||
/* exec the new process */
|
||||
|
||||
if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1) return STATUS_TOO_MANY_OPENED_FILES;
|
||||
#ifdef SO_PASSCRED
|
||||
else
|
||||
{
|
||||
int enable = 1;
|
||||
setsockopt( socketfd[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable) );
|
||||
}
|
||||
#endif
|
||||
wine_server_send_fd( socketfd[1] );
|
||||
close( socketfd[1] );
|
||||
|
||||
SERVER_START_REQ( exec_process )
|
||||
{
|
||||
req->socket_fd = socketfd[1];
|
||||
req->cpu = pe_info.cpu;
|
||||
status = wine_server_call( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if (!status)
|
||||
{
|
||||
const int is_child_64bit = (pe_info.cpu == CPU_x86_64 || pe_info.cpu == CPU_ARM64);
|
||||
char **argv = build_argv( &strW, 2 );
|
||||
if (argv)
|
||||
{
|
||||
do
|
||||
{
|
||||
status = unix_funcs->exec_wineloader( argv, socketfd[0], is_child_64bit,
|
||||
pe_info.base, pe_info.base + pe_info.map_size );
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
while (errno == ENOTSUP && terminate_main_thread());
|
||||
#else
|
||||
while (0);
|
||||
#endif
|
||||
RtlFreeHeap( GetProcessHeap(), 0, argv );
|
||||
}
|
||||
else status = STATUS_NO_MEMORY;
|
||||
}
|
||||
close( socketfd[0] );
|
||||
return status;
|
||||
return unix_funcs->exec_process( &strW, &pe_info );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1743,7 +1369,8 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
|
|||
|
||||
/* create the child process */
|
||||
|
||||
if ((status = spawn_loader( params, socketfd[0], unixdir, winedebug, &pe_info ))) goto done;
|
||||
if ((status = unix_funcs->spawn_process( params, socketfd[0], unixdir, winedebug, &pe_info )))
|
||||
goto done;
|
||||
|
||||
close( socketfd[0] );
|
||||
socketfd[0] = -1;
|
||||
|
|
|
@ -408,6 +408,14 @@ static void init_unix_codepage(void)
|
|||
#endif /* __APPLE__ || __ANDROID__ */
|
||||
|
||||
|
||||
static inline SIZE_T get_env_length( const WCHAR *env )
|
||||
{
|
||||
const WCHAR *end = env;
|
||||
while (*end) end += wcslen(end) + 1;
|
||||
return end + 1 - env;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* is_special_env_var
|
||||
*
|
||||
|
@ -446,6 +454,122 @@ DWORD ntdll_umbstowcs( const char *src, DWORD srclen, WCHAR *dst, DWORD dstlen )
|
|||
}
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* ntdll_wcstoumbs
|
||||
*/
|
||||
int ntdll_wcstoumbs( const WCHAR *src, DWORD srclen, char *dst, DWORD dstlen, BOOL strict )
|
||||
{
|
||||
DWORD i, reslen;
|
||||
|
||||
if (unix_table.CodePage)
|
||||
{
|
||||
if (unix_table.DBCSOffsets)
|
||||
{
|
||||
const unsigned short *uni2cp = unix_table.WideCharTable;
|
||||
for (i = dstlen; srclen && i; i--, srclen--, src++)
|
||||
{
|
||||
unsigned short ch = uni2cp[*src];
|
||||
if (ch >> 8)
|
||||
{
|
||||
if (strict && unix_table.DBCSOffsets[unix_table.DBCSOffsets[ch >> 8] + (ch & 0xff)] != *src)
|
||||
return -1;
|
||||
if (i == 1) break; /* do not output a partial char */
|
||||
i--;
|
||||
*dst++ = ch >> 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unix_table.MultiByteTable[ch] != *src) return -1;
|
||||
*dst++ = (char)ch;
|
||||
}
|
||||
}
|
||||
reslen = dstlen - i;
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned char *uni2cp = unix_table.WideCharTable;
|
||||
reslen = min( srclen, dstlen );
|
||||
for (i = 0; i < reslen; i++)
|
||||
{
|
||||
unsigned char ch = uni2cp[src[i]];
|
||||
if (strict && unix_table.MultiByteTable[ch] != src[i]) return -1;
|
||||
dst[i] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
else RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
|
||||
|
||||
return reslen;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* build_envp
|
||||
*
|
||||
* Build the environment of a new child process.
|
||||
*/
|
||||
char **build_envp( const WCHAR *envW )
|
||||
{
|
||||
static const char * const unix_vars[] = { "PATH", "TEMP", "TMP", "HOME" };
|
||||
char **envp;
|
||||
char *env, *p;
|
||||
int count = 1, length, lenW;
|
||||
unsigned int i;
|
||||
|
||||
lenW = get_env_length( envW );
|
||||
if (!(env = malloc( lenW * 3 ))) return NULL;
|
||||
length = ntdll_wcstoumbs( envW, lenW, env, lenW * 3, FALSE );
|
||||
|
||||
for (p = env; *p; p += strlen(p) + 1, count++)
|
||||
if (is_special_env_var( p )) length += 4; /* prefix it with "WINE" */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE( unix_vars ); i++)
|
||||
{
|
||||
if (!(p = getenv(unix_vars[i]))) continue;
|
||||
length += strlen(unix_vars[i]) + strlen(p) + 2;
|
||||
count++;
|
||||
}
|
||||
|
||||
if ((envp = malloc( count * sizeof(*envp) + length )))
|
||||
{
|
||||
char **envptr = envp;
|
||||
char *dst = (char *)(envp + count);
|
||||
|
||||
/* some variables must not be modified, so we get them directly from the unix env */
|
||||
for (i = 0; i < ARRAY_SIZE( unix_vars ); i++)
|
||||
{
|
||||
if (!(p = getenv( unix_vars[i] ))) continue;
|
||||
*envptr++ = strcpy( dst, unix_vars[i] );
|
||||
strcat( dst, "=" );
|
||||
strcat( dst, p );
|
||||
dst += strlen(dst) + 1;
|
||||
}
|
||||
|
||||
/* now put the Windows environment strings */
|
||||
for (p = env; *p; p += strlen(p) + 1)
|
||||
{
|
||||
if (*p == '=') continue; /* skip drive curdirs, this crashes some unix apps */
|
||||
if (!strncmp( p, "WINEPRELOADRESERVE=", sizeof("WINEPRELOADRESERVE=")-1 )) continue;
|
||||
if (!strncmp( p, "WINELOADERNOEXEC=", sizeof("WINELOADERNOEXEC=")-1 )) continue;
|
||||
if (!strncmp( p, "WINESERVERSOCKET=", sizeof("WINESERVERSOCKET=")-1 )) continue;
|
||||
if (is_special_env_var( p )) /* prefix it with "WINE" */
|
||||
{
|
||||
*envptr++ = strcpy( dst, "WINE" );
|
||||
strcat( dst, p );
|
||||
}
|
||||
else
|
||||
{
|
||||
*envptr++ = strcpy( dst, p );
|
||||
}
|
||||
dst += strlen(dst) + 1;
|
||||
}
|
||||
*envptr = 0;
|
||||
}
|
||||
free( env );
|
||||
return envp;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* set_process_name
|
||||
*
|
||||
|
|
|
@ -424,8 +424,8 @@ static NTSTATUS loader_exec( const char *loader, char **argv, int is_child_64bit
|
|||
*
|
||||
* argv[0] and argv[1] must be reserved for the preloader and loader respectively.
|
||||
*/
|
||||
static NTSTATUS CDECL exec_wineloader( char **argv, int socketfd, int is_child_64bit,
|
||||
ULONGLONG res_start, ULONGLONG res_end )
|
||||
NTSTATUS exec_wineloader( char **argv, int socketfd, int is_child_64bit,
|
||||
ULONGLONG res_start, ULONGLONG res_end )
|
||||
{
|
||||
const char *loader = argv0;
|
||||
const char *loader_env = getenv( "WINELOADER" );
|
||||
|
@ -906,7 +906,6 @@ static struct unix_funcs unix_funcs =
|
|||
get_version,
|
||||
get_build_id,
|
||||
get_host_version,
|
||||
exec_wineloader,
|
||||
map_so_dll,
|
||||
virtual_map_section,
|
||||
virtual_get_system_info,
|
||||
|
@ -925,6 +924,9 @@ static struct unix_funcs unix_funcs =
|
|||
exit_thread,
|
||||
exit_process,
|
||||
get_thread_ldt_entry,
|
||||
spawn_process,
|
||||
exec_process,
|
||||
fork_and_exec,
|
||||
wine_server_call,
|
||||
server_send_fd,
|
||||
server_get_unix_fd,
|
||||
|
|
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* NT process handling
|
||||
*
|
||||
* Copyright 1996-1998 Marcus Meissner
|
||||
* Copyright 2018, 2020 Alexandre Julliard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#pragma makedep unix
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIMES_H
|
||||
# include <sys/times.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
#ifdef HAVE_MACH_MACH_H
|
||||
# include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
#include "ntstatus.h"
|
||||
#define WIN32_NO_STATUS
|
||||
#include "windef.h"
|
||||
#include "winternl.h"
|
||||
#include "unix_private.h"
|
||||
#include "wine/exception.h"
|
||||
#include "wine/server.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
|
||||
static char **build_argv( const UNICODE_STRING *cmdline, int reserved )
|
||||
{
|
||||
char **argv, *arg, *src, *dst;
|
||||
int argc, in_quotes = 0, bcount = 0, len = cmdline->Length / sizeof(WCHAR);
|
||||
|
||||
if (!(src = malloc( len * 3 + 1 ))) return NULL;
|
||||
len = ntdll_wcstoumbs( cmdline->Buffer, len, src, len * 3, FALSE );
|
||||
src[len++] = 0;
|
||||
|
||||
argc = reserved + 2 + len / 2;
|
||||
argv = malloc( argc * sizeof(*argv) + len );
|
||||
arg = dst = (char *)(argv + argc);
|
||||
argc = reserved;
|
||||
while (*src)
|
||||
{
|
||||
if ((*src == ' ' || *src == '\t') && !in_quotes)
|
||||
{
|
||||
/* skip the remaining spaces */
|
||||
while (*src == ' ' || *src == '\t') src++;
|
||||
if (!*src) break;
|
||||
/* close the argument and copy it */
|
||||
*dst++ = 0;
|
||||
argv[argc++] = arg;
|
||||
/* start with a new argument */
|
||||
arg = dst;
|
||||
bcount = 0;
|
||||
}
|
||||
else if (*src == '\\')
|
||||
{
|
||||
*dst++ = *src++;
|
||||
bcount++;
|
||||
}
|
||||
else if (*src == '"')
|
||||
{
|
||||
if ((bcount & 1) == 0)
|
||||
{
|
||||
/* Preceded by an even number of '\', this is half that
|
||||
* number of '\', plus a '"' which we discard.
|
||||
*/
|
||||
dst -= bcount / 2;
|
||||
src++;
|
||||
if (in_quotes && *src == '"') *dst++ = *src++;
|
||||
else in_quotes = !in_quotes;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Preceded by an odd number of '\', this is half that
|
||||
* number of '\' followed by a '"'
|
||||
*/
|
||||
dst -= bcount / 2 + 1;
|
||||
*dst++ = *src++;
|
||||
}
|
||||
bcount = 0;
|
||||
}
|
||||
else /* a regular character */
|
||||
{
|
||||
*dst++ = *src++;
|
||||
bcount = 0;
|
||||
}
|
||||
}
|
||||
*dst = 0;
|
||||
argv[argc++] = arg;
|
||||
argv[argc] = NULL;
|
||||
return argv;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
/***********************************************************************
|
||||
* terminate_main_thread
|
||||
*
|
||||
* On some versions of Mac OS X, the execve system call fails with
|
||||
* ENOTSUP if the process has multiple threads. Wine is always multi-
|
||||
* threaded on Mac OS X because it specifically reserves the main thread
|
||||
* for use by the system frameworks (see apple_main_thread() in
|
||||
* libs/wine/loader.c). So, when we need to exec without first forking,
|
||||
* we need to terminate the main thread first. We do this by installing
|
||||
* a custom run loop source onto the main run loop and signaling it.
|
||||
* The source's "perform" callback is pthread_exit and it will be
|
||||
* executed on the main thread, terminating it.
|
||||
*
|
||||
* Returns TRUE if there's still hope the main thread has terminated or
|
||||
* will soon. Return FALSE if we've given up.
|
||||
*/
|
||||
static BOOL terminate_main_thread(void)
|
||||
{
|
||||
static int delayms;
|
||||
|
||||
if (!delayms)
|
||||
{
|
||||
CFRunLoopSourceContext source_context = { 0 };
|
||||
CFRunLoopSourceRef source;
|
||||
|
||||
source_context.perform = pthread_exit;
|
||||
if (!(source = CFRunLoopSourceCreate( NULL, 0, &source_context )))
|
||||
return FALSE;
|
||||
|
||||
CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
|
||||
CFRunLoopSourceSignal( source );
|
||||
CFRunLoopWakeUp( CFRunLoopGetMain() );
|
||||
CFRelease( source );
|
||||
|
||||
delayms = 20;
|
||||
}
|
||||
|
||||
if (delayms > 1000)
|
||||
return FALSE;
|
||||
|
||||
usleep(delayms * 1000);
|
||||
delayms *= 2;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* set_stdio_fd
|
||||
*/
|
||||
static void set_stdio_fd( int stdin_fd, int stdout_fd )
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
if (stdin_fd == -1 || stdout_fd == -1)
|
||||
{
|
||||
fd = open( "/dev/null", O_RDWR );
|
||||
if (stdin_fd == -1) stdin_fd = fd;
|
||||
if (stdout_fd == -1) stdout_fd = fd;
|
||||
}
|
||||
|
||||
dup2( stdin_fd, 0 );
|
||||
dup2( stdout_fd, 1 );
|
||||
if (fd != -1) close( fd );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* spawn_process
|
||||
*/
|
||||
NTSTATUS CDECL spawn_process( const RTL_USER_PROCESS_PARAMETERS *params, int socketfd,
|
||||
const char *unixdir, char *winedebug, const pe_image_info_t *pe_info )
|
||||
{
|
||||
const int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64);
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
int stdin_fd = -1, stdout_fd = -1;
|
||||
pid_t pid;
|
||||
char **argv;
|
||||
|
||||
server_handle_to_fd( params->hStdInput, FILE_READ_DATA, &stdin_fd, NULL );
|
||||
server_handle_to_fd( params->hStdOutput, FILE_WRITE_DATA, &stdout_fd, NULL );
|
||||
|
||||
if (!(pid = fork())) /* child */
|
||||
{
|
||||
if (!(pid = fork())) /* grandchild */
|
||||
{
|
||||
if (params->ConsoleFlags ||
|
||||
params->ConsoleHandle == (HANDLE)1 /* KERNEL32_CONSOLE_ALLOC */ ||
|
||||
(params->hStdInput == INVALID_HANDLE_VALUE && params->hStdOutput == INVALID_HANDLE_VALUE))
|
||||
{
|
||||
setsid();
|
||||
set_stdio_fd( -1, -1 ); /* close stdin and stdout */
|
||||
}
|
||||
else set_stdio_fd( stdin_fd, stdout_fd );
|
||||
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
|
||||
if (winedebug) putenv( winedebug );
|
||||
if (unixdir) chdir( unixdir );
|
||||
|
||||
argv = build_argv( ¶ms->CommandLine, 2 );
|
||||
|
||||
exec_wineloader( argv, socketfd, is_child_64bit,
|
||||
pe_info->base, pe_info->base + pe_info->map_size );
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
_exit(pid == -1);
|
||||
}
|
||||
|
||||
if (pid != -1)
|
||||
{
|
||||
/* reap child */
|
||||
pid_t wret;
|
||||
do {
|
||||
wret = waitpid(pid, NULL, 0);
|
||||
} while (wret < 0 && errno == EINTR);
|
||||
}
|
||||
else status = STATUS_NO_MEMORY;
|
||||
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* exec_process
|
||||
*/
|
||||
NTSTATUS CDECL exec_process( const UNICODE_STRING *cmdline, const pe_image_info_t *pe_info )
|
||||
{
|
||||
const int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64);
|
||||
NTSTATUS status;
|
||||
int socketfd[2];
|
||||
char **argv;
|
||||
|
||||
if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1) return STATUS_TOO_MANY_OPENED_FILES;
|
||||
#ifdef SO_PASSCRED
|
||||
else
|
||||
{
|
||||
int enable = 1;
|
||||
setsockopt( socketfd[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable) );
|
||||
}
|
||||
#endif
|
||||
server_send_fd( socketfd[1] );
|
||||
close( socketfd[1] );
|
||||
|
||||
SERVER_START_REQ( exec_process )
|
||||
{
|
||||
req->socket_fd = socketfd[1];
|
||||
req->cpu = pe_info->cpu;
|
||||
status = wine_server_call( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if (!status)
|
||||
{
|
||||
if (!(argv = build_argv( cmdline, 2 ))) return STATUS_NO_MEMORY;
|
||||
do
|
||||
{
|
||||
status = exec_wineloader( argv, socketfd[0], is_child_64bit,
|
||||
pe_info->base, pe_info->base + pe_info->map_size );
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
while (errno == ENOTSUP && terminate_main_thread());
|
||||
#else
|
||||
while (0);
|
||||
#endif
|
||||
free( argv );
|
||||
}
|
||||
close( socketfd[0] );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* fork_and_exec
|
||||
*
|
||||
* Fork and exec a new Unix binary, checking for errors.
|
||||
*/
|
||||
NTSTATUS CDECL fork_and_exec( const char *unix_name, const char *unix_dir,
|
||||
const RTL_USER_PROCESS_PARAMETERS *params )
|
||||
{
|
||||
pid_t pid;
|
||||
int fd[2], stdin_fd = -1, stdout_fd = -1;
|
||||
char **argv, **envp;
|
||||
NTSTATUS status;
|
||||
|
||||
#ifdef HAVE_PIPE2
|
||||
if (pipe2( fd, O_CLOEXEC ) == -1)
|
||||
#endif
|
||||
{
|
||||
if (pipe(fd) == -1) return STATUS_TOO_MANY_OPENED_FILES;
|
||||
fcntl( fd[0], F_SETFD, FD_CLOEXEC );
|
||||
fcntl( fd[1], F_SETFD, FD_CLOEXEC );
|
||||
}
|
||||
|
||||
server_handle_to_fd( params->hStdInput, FILE_READ_DATA, &stdin_fd, NULL );
|
||||
server_handle_to_fd( params->hStdOutput, FILE_WRITE_DATA, &stdout_fd, NULL );
|
||||
|
||||
if (!(pid = fork())) /* child */
|
||||
{
|
||||
if (!(pid = fork())) /* grandchild */
|
||||
{
|
||||
close( fd[0] );
|
||||
|
||||
if (params->ConsoleFlags ||
|
||||
params->ConsoleHandle == (HANDLE)1 /* KERNEL32_CONSOLE_ALLOC */ ||
|
||||
(params->hStdInput == INVALID_HANDLE_VALUE && params->hStdOutput == INVALID_HANDLE_VALUE))
|
||||
{
|
||||
setsid();
|
||||
set_stdio_fd( -1, -1 ); /* close stdin and stdout */
|
||||
}
|
||||
else set_stdio_fd( stdin_fd, stdout_fd );
|
||||
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
|
||||
/* Reset signals that we previously set to SIG_IGN */
|
||||
signal( SIGPIPE, SIG_DFL );
|
||||
|
||||
argv = build_argv( ¶ms->CommandLine, 0 );
|
||||
envp = build_envp( params->Environment );
|
||||
if (unix_dir) chdir( unix_dir );
|
||||
|
||||
execve( unix_name, argv, envp );
|
||||
}
|
||||
|
||||
if (pid <= 0) /* grandchild if exec failed or child if fork failed */
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EPERM:
|
||||
case EACCES: status = STATUS_ACCESS_DENIED; break;
|
||||
case ENOENT: status = STATUS_OBJECT_NAME_NOT_FOUND; break;
|
||||
case EMFILE:
|
||||
case ENFILE: status = STATUS_TOO_MANY_OPENED_FILES; break;
|
||||
case ENOEXEC:
|
||||
case EINVAL: status = STATUS_INVALID_IMAGE_FORMAT; break;
|
||||
default: status = STATUS_NO_MEMORY; break;
|
||||
}
|
||||
write( fd[1], &status, sizeof(status) );
|
||||
_exit(1);
|
||||
}
|
||||
_exit(0); /* child if fork succeeded */
|
||||
}
|
||||
close( fd[1] );
|
||||
|
||||
if (pid != -1)
|
||||
{
|
||||
/* reap child */
|
||||
pid_t wret;
|
||||
do {
|
||||
wret = waitpid(pid, NULL, 0);
|
||||
} while (wret < 0 && errno == EINTR);
|
||||
read( fd[0], &status, sizeof(status) ); /* if we read something, exec or second fork failed */
|
||||
}
|
||||
else status = STATUS_NO_MEMORY;
|
||||
|
||||
close( fd[0] );
|
||||
if (stdin_fd != -1) close( stdin_fd );
|
||||
if (stdout_fd != -1) close( stdout_fd );
|
||||
return status;
|
||||
}
|
|
@ -114,6 +114,11 @@ extern TEB * CDECL init_threading( int *nb_threads_ptr, struct ldt_copy **ldt_co
|
|||
extern void CDECL DECLSPEC_NORETURN exit_thread( int status ) DECLSPEC_HIDDEN;
|
||||
extern void CDECL DECLSPEC_NORETURN exit_process( int status ) DECLSPEC_HIDDEN;
|
||||
extern NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
|
||||
extern NTSTATUS CDECL spawn_process( const RTL_USER_PROCESS_PARAMETERS *params, int socketfd,
|
||||
const char *unixdir, char *winedebug, const pe_image_info_t *pe_info ) DECLSPEC_HIDDEN;
|
||||
extern NTSTATUS CDECL exec_process( const UNICODE_STRING *cmdline, const pe_image_info_t *pe_info ) DECLSPEC_HIDDEN;
|
||||
extern NTSTATUS CDECL fork_and_exec( const char *unix_name, const char *unix_dir,
|
||||
const RTL_USER_PROCESS_PARAMETERS *params ) DECLSPEC_HIDDEN;
|
||||
|
||||
extern const char *data_dir DECLSPEC_HIDDEN;
|
||||
extern const char *build_dir DECLSPEC_HIDDEN;
|
||||
|
@ -129,6 +134,10 @@ extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN;
|
|||
|
||||
extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN;
|
||||
extern DWORD ntdll_umbstowcs( const char *src, DWORD srclen, WCHAR *dst, DWORD dstlen ) DECLSPEC_HIDDEN;
|
||||
extern int ntdll_wcstoumbs( const WCHAR *src, DWORD srclen, char *dst, DWORD dstlen, BOOL strict ) DECLSPEC_HIDDEN;
|
||||
extern char **build_envp( const WCHAR *envW ) DECLSPEC_HIDDEN;
|
||||
extern NTSTATUS exec_wineloader( char **argv, int socketfd, int is_child_64bit,
|
||||
ULONGLONG res_start, ULONGLONG res_end ) DECLSPEC_HIDDEN;
|
||||
|
||||
extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN;
|
||||
extern void server_enter_uninterrupted_section( RTL_CRITICAL_SECTION *cs, sigset_t *sigset ) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -28,7 +28,7 @@ struct ldt_copy;
|
|||
struct msghdr;
|
||||
|
||||
/* increment this when you change the function table */
|
||||
#define NTDLL_UNIXLIB_VERSION 40
|
||||
#define NTDLL_UNIXLIB_VERSION 41
|
||||
|
||||
struct unix_funcs
|
||||
{
|
||||
|
@ -180,10 +180,6 @@ struct unix_funcs
|
|||
const char * (CDECL *get_build_id)(void);
|
||||
void (CDECL *get_host_version)( const char **sysname, const char **release );
|
||||
|
||||
/* loader functions */
|
||||
NTSTATUS (CDECL *exec_wineloader)( char **argv, int socketfd, int is_child_64bit,
|
||||
ULONGLONG res_start, ULONGLONG res_end );
|
||||
|
||||
/* virtual memory functions */
|
||||
NTSTATUS (CDECL *map_so_dll)( const IMAGE_NT_HEADERS *nt_descr, HMODULE module );
|
||||
NTSTATUS (CDECL *virtual_map_section)( HANDLE handle, PVOID *addr_ptr, unsigned short zero_bits_64, SIZE_T commit_size,
|
||||
|
@ -208,6 +204,12 @@ struct unix_funcs
|
|||
void (CDECL *exit_thread)( int status );
|
||||
void (CDECL *exit_process)( int status );
|
||||
NTSTATUS (CDECL *get_thread_ldt_entry)( HANDLE handle, void *data, ULONG len, ULONG *ret_len );
|
||||
NTSTATUS (CDECL *spawn_process)( const RTL_USER_PROCESS_PARAMETERS *params, int socketfd,
|
||||
const char *unixdir, char *winedebug,
|
||||
const pe_image_info_t *pe_info );
|
||||
NTSTATUS (CDECL *exec_process)( const UNICODE_STRING *cmdline, const pe_image_info_t *pe_info );
|
||||
NTSTATUS (CDECL *fork_and_exec)( const char *unix_name, const char *unix_dir,
|
||||
const RTL_USER_PROCESS_PARAMETERS *params );
|
||||
|
||||
/* server functions */
|
||||
unsigned int (CDECL *server_call)( void *req_ptr );
|
||||
|
|
Loading…
Reference in New Issue