Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
412555e0
Commit
412555e0
authored
Jun 12, 2020
by
Alexandre Julliard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ntdll: Move fork and exec support to the Unix library.
Signed-off-by:
Alexandre Julliard
<
julliard@winehq.org
>
parent
22970932
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
552 additions
and
384 deletions
+552
-384
Makefile.in
dlls/ntdll/Makefile.in
+1
-0
process.c
dlls/ntdll/process.c
+4
-377
env.c
dlls/ntdll/unix/env.c
+124
-0
loader.c
dlls/ntdll/unix/loader.c
+4
-2
process.c
dlls/ntdll/unix/process.c
+403
-0
unix_private.h
dlls/ntdll/unix/unix_private.h
+9
-0
unixlib.h
dlls/ntdll/unixlib.h
+7
-5
No files found.
dlls/ntdll/Makefile.in
View file @
412555e0
...
...
@@ -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
\
...
...
dlls/ntdll/process.c
View file @
412555e0
...
...
@@ -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
(
&
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
*/
...
...
@@ -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
(
&
params
->
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
;
...
...
dlls/ntdll/unix/env.c
View file @
412555e0
...
...
@@ -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
*
...
...
dlls/ntdll/unix/loader.c
View file @
412555e0
...
...
@@ -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.
*/
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
)
{
const
char
*
loader
=
argv0
;
...
...
@@ -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
,
...
...
dlls/ntdll/unix/process.c
0 → 100644
View file @
412555e0
/*
* 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
;
}
dlls/ntdll/unix/unix_private.h
View file @
412555e0
...
...
@@ -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
;
...
...
dlls/ntdll/unixlib.h
View file @
412555e0
...
...
@@ -28,7 +28,7 @@ struct ldt_copy;
struct
msghdr
;
/* increment this when you change the function table */
#define NTDLL_UNIXLIB_VERSION 4
0
#define NTDLL_UNIXLIB_VERSION 4
1
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
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment