Commit 412555e0 authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Move fork and exec support to the Unix library.

parent 22970932
...@@ -53,6 +53,7 @@ C_SRCS = \ ...@@ -53,6 +53,7 @@ C_SRCS = \
unix/debug.c \ unix/debug.c \
unix/env.c \ unix/env.c \
unix/loader.c \ unix/loader.c \
unix/process.c \
unix/server.c \ unix/server.c \
unix/signal_arm.c \ unix/signal_arm.c \
unix/signal_arm64.c \ unix/signal_arm64.c \
......
...@@ -840,133 +840,6 @@ NTSTATUS WINAPI NtSuspendProcess( HANDLE handle ) ...@@ -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, static inline const WCHAR *get_params_string( const RTL_USER_PROCESS_PARAMETERS *params,
const UNICODE_STRING *str ) const UNICODE_STRING *str )
{ {
...@@ -1035,136 +908,6 @@ static startup_info_t *create_startup_info( const RTL_USER_PROCESS_PARAMETERS *p ...@@ -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( &params->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 * is_builtin_path
*/ */
...@@ -1404,9 +1147,6 @@ static char *get_unix_curdir( const RTL_USER_PROCESS_PARAMETERS *params ) ...@@ -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 ) 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; char *unixdir;
ANSI_STRING unix_name; ANSI_STRING unix_name;
NTSTATUS status; NTSTATUS status;
...@@ -1414,79 +1154,8 @@ static NTSTATUS fork_and_exec( UNICODE_STRING *path, const RTL_USER_PROCESS_PARA ...@@ -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 ); status = wine_nt_to_unix_file_name( path, &unix_name, FILE_OPEN, FALSE );
if (status) return status; 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( &params->CommandLine, 0 );
envp = build_envp( params->Environment );
unixdir = get_unix_curdir( params ); unixdir = get_unix_curdir( params );
status = unix_funcs->fork_and_exec( unix_name.Buffer, unixdir, 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 );
RtlFreeHeap( GetProcessHeap(), 0, unixdir ); RtlFreeHeap( GetProcessHeap(), 0, unixdir );
RtlFreeAnsiString( &unix_name ); RtlFreeAnsiString( &unix_name );
return status; return status;
...@@ -1503,7 +1172,6 @@ NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS 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 comW[] = {'.','c','o','m',0};
static const WCHAR pifW[] = {'.','p','i','f',0}; static const WCHAR pifW[] = {'.','p','i','f',0};
int socketfd[2];
WCHAR *p, *cmdline; WCHAR *p, *cmdline;
UNICODE_STRING strW; UNICODE_STRING strW;
pe_image_info_t pe_info; pe_image_info_t pe_info;
...@@ -1548,49 +1216,7 @@ NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status ) ...@@ -1548,49 +1216,7 @@ NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status )
return status; return status;
} }
/* exec the new process */ return unix_funcs->exec_process( &strW, &pe_info );
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;
} }
...@@ -1743,7 +1369,8 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ ...@@ -1743,7 +1369,8 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
/* create the child process */ /* 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] ); close( socketfd[0] );
socketfd[0] = -1; socketfd[0] = -1;
......
...@@ -408,6 +408,14 @@ static void init_unix_codepage(void) ...@@ -408,6 +408,14 @@ static void init_unix_codepage(void)
#endif /* __APPLE__ || __ANDROID__ */ #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 * is_special_env_var
* *
...@@ -446,6 +454,122 @@ DWORD ntdll_umbstowcs( const char *src, DWORD srclen, WCHAR *dst, DWORD dstlen ) ...@@ -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 * set_process_name
* *
......
...@@ -424,7 +424,7 @@ static NTSTATUS loader_exec( const char *loader, char **argv, int is_child_64bit ...@@ -424,7 +424,7 @@ 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. * 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, NTSTATUS exec_wineloader( char **argv, int socketfd, int is_child_64bit,
ULONGLONG res_start, ULONGLONG res_end ) ULONGLONG res_start, ULONGLONG res_end )
{ {
const char *loader = argv0; const char *loader = argv0;
...@@ -906,7 +906,6 @@ static struct unix_funcs unix_funcs = ...@@ -906,7 +906,6 @@ static struct unix_funcs unix_funcs =
get_version, get_version,
get_build_id, get_build_id,
get_host_version, get_host_version,
exec_wineloader,
map_so_dll, map_so_dll,
virtual_map_section, virtual_map_section,
virtual_get_system_info, virtual_get_system_info,
...@@ -925,6 +924,9 @@ static struct unix_funcs unix_funcs = ...@@ -925,6 +924,9 @@ static struct unix_funcs unix_funcs =
exit_thread, exit_thread,
exit_process, exit_process,
get_thread_ldt_entry, get_thread_ldt_entry,
spawn_process,
exec_process,
fork_and_exec,
wine_server_call, wine_server_call,
server_send_fd, server_send_fd,
server_get_unix_fd, server_get_unix_fd,
......
/*
* 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( &params->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( &params->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 ...@@ -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_thread( int status ) DECLSPEC_HIDDEN;
extern void CDECL DECLSPEC_NORETURN exit_process( 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 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 *data_dir DECLSPEC_HIDDEN;
extern const char *build_dir DECLSPEC_HIDDEN; extern const char *build_dir DECLSPEC_HIDDEN;
...@@ -129,6 +134,10 @@ extern struct _KUSER_SHARED_DATA *user_shared_data 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 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 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 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; extern void server_enter_uninterrupted_section( RTL_CRITICAL_SECTION *cs, sigset_t *sigset ) DECLSPEC_HIDDEN;
......
...@@ -28,7 +28,7 @@ struct ldt_copy; ...@@ -28,7 +28,7 @@ struct ldt_copy;
struct msghdr; struct msghdr;
/* increment this when you change the function table */ /* increment this when you change the function table */
#define NTDLL_UNIXLIB_VERSION 40 #define NTDLL_UNIXLIB_VERSION 41
struct unix_funcs struct unix_funcs
{ {
...@@ -180,10 +180,6 @@ struct unix_funcs ...@@ -180,10 +180,6 @@ struct unix_funcs
const char * (CDECL *get_build_id)(void); const char * (CDECL *get_build_id)(void);
void (CDECL *get_host_version)( const char **sysname, const char **release ); 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 */ /* virtual memory functions */
NTSTATUS (CDECL *map_so_dll)( const IMAGE_NT_HEADERS *nt_descr, HMODULE module ); 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, 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 ...@@ -208,6 +204,12 @@ struct unix_funcs
void (CDECL *exit_thread)( int status ); void (CDECL *exit_thread)( int status );
void (CDECL *exit_process)( 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 *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 */ /* server functions */
unsigned int (CDECL *server_call)( void *req_ptr ); unsigned int (CDECL *server_call)( void *req_ptr );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment