mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-09-13 09:17:20 -07:00
Added patch for basic implementation of job objects.
This commit is contained in:
parent
2de8f84fb0
commit
480bab002e
1
debian/changelog
vendored
1
debian/changelog
vendored
@ -14,6 +14,7 @@ wine-staging (1.7.38) UNRELEASED; urgency=low
|
||||
* Added patch for stub of gdiplus.GdipCreateEffect.
|
||||
* Added patches for ntoskrnl.ProbeFor{Read,Write}.
|
||||
* Added patch for support of shell32 file operation progress dialog.
|
||||
* Added patch for basic implementation of job objects.
|
||||
* Removed patch to properly call DriverUnload when unloading device drivers (accepted upstream).
|
||||
* Removed patch to allow Accept-Encoding for HTTP/1.0 in wininet (accepted upstream).
|
||||
* Removed patch to declare pDirectInputCreateEx in a MSVC compatible way (accepted upstream).
|
||||
|
@ -1 +0,0 @@
|
||||
Depends: kernel32-Console_Handles
|
@ -103,7 +103,6 @@ patch_enable_all ()
|
||||
enable_kernel32_GetStringTypeW="$1"
|
||||
enable_kernel32_GetSystemTimes="$1"
|
||||
enable_kernel32_GetVolumePathName="$1"
|
||||
enable_kernel32_JobObjects="$1"
|
||||
enable_kernel32_Named_Pipe="$1"
|
||||
enable_kernel32_NeedCurrentDirectoryForExePath="$1"
|
||||
enable_kernel32_Profile="$1"
|
||||
@ -157,6 +156,7 @@ patch_enable_all ()
|
||||
enable_server_ClipCursor="$1"
|
||||
enable_server_CreateProcess_ACLs="$1"
|
||||
enable_server_Inherited_ACLs="$1"
|
||||
enable_server_JobObjects="$1"
|
||||
enable_server_Key_State="$1"
|
||||
enable_server_Misc_ACL="$1"
|
||||
enable_server_OpenProcess="$1"
|
||||
@ -341,9 +341,6 @@ patch_enable ()
|
||||
kernel32-GetVolumePathName)
|
||||
enable_kernel32_GetVolumePathName="$2"
|
||||
;;
|
||||
kernel32-JobObjects)
|
||||
enable_kernel32_JobObjects="$2"
|
||||
;;
|
||||
kernel32-Named_Pipe)
|
||||
enable_kernel32_Named_Pipe="$2"
|
||||
;;
|
||||
@ -503,6 +500,9 @@ patch_enable ()
|
||||
server-Inherited_ACLs)
|
||||
enable_server_Inherited_ACLs="$2"
|
||||
;;
|
||||
server-JobObjects)
|
||||
enable_server_JobObjects="$2"
|
||||
;;
|
||||
server-Key_State)
|
||||
enable_server_Key_State="$2"
|
||||
;;
|
||||
@ -899,6 +899,21 @@ if test "$enable_shell32_Progress_Dialog" -eq 1; then
|
||||
enable_kernel32_CopyFileEx=1
|
||||
fi
|
||||
|
||||
if test "$enable_server_JobObjects" -eq 1; then
|
||||
if test "$enable_kernel32_Console_Handles" -gt 1; then
|
||||
abort "Patchset kernel32-Console_Handles disabled, but server-JobObjects depends on that."
|
||||
fi
|
||||
if test "$enable_server_Misc_ACL" -gt 1; then
|
||||
abort "Patchset server-Misc_ACL disabled, but server-JobObjects depends on that."
|
||||
fi
|
||||
if test "$enable_server_OpenProcess" -gt 1; then
|
||||
abort "Patchset server-OpenProcess disabled, but server-JobObjects depends on that."
|
||||
fi
|
||||
enable_kernel32_Console_Handles=1
|
||||
enable_server_Misc_ACL=1
|
||||
enable_server_OpenProcess=1
|
||||
fi
|
||||
|
||||
if test "$enable_server_ACL_Compat" -eq 1; then
|
||||
if test "$enable_server_Inherited_ACLs" -gt 1; then
|
||||
abort "Patchset server-Inherited_ACLs disabled, but server-ACL_Compat depends on that."
|
||||
@ -966,13 +981,6 @@ if test "$enable_ntdll_Junction_Points" -eq 1; then
|
||||
enable_ntdll_Fix_Free=1
|
||||
fi
|
||||
|
||||
if test "$enable_kernel32_JobObjects" -eq 1; then
|
||||
if test "$enable_kernel32_Console_Handles" -gt 1; then
|
||||
abort "Patchset kernel32-Console_Handles disabled, but kernel32-JobObjects depends on that."
|
||||
fi
|
||||
enable_kernel32_Console_Handles=1
|
||||
fi
|
||||
|
||||
if test "$enable_kernel32_CopyFileEx" -eq 1; then
|
||||
if test "$enable_kernel32_SetFileInformationByHandle" -gt 1; then
|
||||
abort "Patchset kernel32-SetFileInformationByHandle disabled, but kernel32-CopyFileEx depends on that."
|
||||
@ -1538,18 +1546,6 @@ if test "$enable_dxgi_GetDesc" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset makedep-PARENTSPEC
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * tools/makedep.c
|
||||
# |
|
||||
if test "$enable_makedep_PARENTSPEC" -eq 1; then
|
||||
patch_apply makedep-PARENTSPEC/0001-makedep-Add-support-for-PARENTSPEC-Makefile-variable.patch
|
||||
(
|
||||
echo '+ { "Sebastian Lackner", "makedep: Add support for PARENTSPEC Makefile variable.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset ntdll-DllRedirects
|
||||
# |
|
||||
# | Modified files:
|
||||
@ -1570,6 +1566,18 @@ if test "$enable_ntdll_DllRedirects" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset makedep-PARENTSPEC
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * tools/makedep.c
|
||||
# |
|
||||
if test "$enable_makedep_PARENTSPEC" -eq 1; then
|
||||
patch_apply makedep-PARENTSPEC/0001-makedep-Add-support-for-PARENTSPEC-Makefile-variable.patch
|
||||
(
|
||||
echo '+ { "Sebastian Lackner", "makedep: Add support for PARENTSPEC Makefile variable.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset wined3d-CSMT_Helper
|
||||
# |
|
||||
# | Modified files:
|
||||
@ -2311,34 +2319,6 @@ if test "$enable_kernel32_GetVolumePathName" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset kernel32-JobObjects
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * dlls/kernel32/tests/process.c
|
||||
# |
|
||||
if test "$enable_kernel32_JobObjects" -eq 1; then
|
||||
patch_apply kernel32-JobObjects/0001-kernel32-tests-Allow-multiple-subprocess-commands-in.patch
|
||||
patch_apply kernel32-JobObjects/0002-kernel32-tests-Add-tests-for-IsProcessInJob.patch
|
||||
patch_apply kernel32-JobObjects/0003-kernel32-tests-Add-tests-for-TerminateJobObject.patch
|
||||
patch_apply kernel32-JobObjects/0004-kernel32-tests-Add-tests-for-QueryInformationJobObje.patch
|
||||
patch_apply kernel32-JobObjects/0005-kernel32-tests-Add-tests-for-job-object-completion-p.patch
|
||||
patch_apply kernel32-JobObjects/0006-kernel32-tests-Add-tests-for-JOB_OBJECT_LIMIT_KILL_O.patch
|
||||
patch_apply kernel32-JobObjects/0007-kernel32-tests-Add-tests-for-JOB_OBJECT_LIMIT_ACTIVE.patch
|
||||
patch_apply kernel32-JobObjects/0008-kernel32-tests-Add-tests-for-JOB_OBJECT_LIMIT_BREAKA.patch
|
||||
patch_apply kernel32-JobObjects/0009-kernel32-tests-Add-tests-for-job-inheritance.patch
|
||||
(
|
||||
echo '+ { "Sebastian Lackner", "kernel32/tests: Allow multiple subprocess commands in process tests.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for IsProcessInJob.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for TerminateJobObject.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for QueryInformationJobObject.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for job object completion ports.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for JOB_OBJECT_LIMIT_ACTIVE_PROCESS.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for JOB_OBJECT_LIMIT_BREAKAWAY_OK.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for job inheritance.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset kernel32-Named_Pipe
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
@ -3285,18 +3265,18 @@ if test "$enable_server_CreateProcess_ACLs" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset server-Key_State
|
||||
# Patchset server-OpenProcess
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
# | * [#27238] Fallback to global key state for threads without a queue
|
||||
# | * [#37087] Return an error when trying to open a terminated process
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * server/queue.c
|
||||
# | * server/process.c, server/process.h
|
||||
# |
|
||||
if test "$enable_server_Key_State" -eq 1; then
|
||||
patch_apply server-Key_State/0001-server-Fall-back-to-global-key-state-when-thread-doe.patch
|
||||
if test "$enable_server_OpenProcess" -eq 1; then
|
||||
patch_apply server-OpenProcess/0001-server-Return-error-when-opening-a-terminating-proce.patch
|
||||
(
|
||||
echo '+ { "Sebastian Lackner", "server: Fall back to global key state when thread doesn'\''t have a queue.", 1 },';
|
||||
echo '+ { "Michael Müller", "server: Return error when opening a terminating process.", 3 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
@ -3317,18 +3297,49 @@ if test "$enable_server_Misc_ACL" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset server-OpenProcess
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
# | * [#37087] Return an error when trying to open a terminated process
|
||||
# Patchset server-JobObjects
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * server/process.c, server/process.h
|
||||
# | * dlls/kernel32/tests/process.c, dlls/ntdll/sync.c, include/winnt.h, server/process.c, server/process.h,
|
||||
# | server/protocol.def
|
||||
# |
|
||||
if test "$enable_server_OpenProcess" -eq 1; then
|
||||
patch_apply server-OpenProcess/0001-server-Return-error-when-opening-a-terminating-proce.patch
|
||||
if test "$enable_server_JobObjects" -eq 1; then
|
||||
patch_apply server-JobObjects/0001-kernel32-tests-Allow-multiple-subprocess-commands-in.patch
|
||||
patch_apply server-JobObjects/0002-kernel32-tests-Add-tests-for-IsProcessInJob.patch
|
||||
patch_apply server-JobObjects/0003-kernel32-tests-Add-tests-for-TerminateJobObject.patch
|
||||
patch_apply server-JobObjects/0004-kernel32-tests-Add-tests-for-QueryInformationJobObje.patch
|
||||
patch_apply server-JobObjects/0005-kernel32-tests-Add-tests-for-job-object-completion-p.patch
|
||||
patch_apply server-JobObjects/0006-kernel32-tests-Add-tests-for-JOB_OBJECT_LIMIT_KILL_O.patch
|
||||
patch_apply server-JobObjects/0007-kernel32-tests-Add-tests-for-JOB_OBJECT_LIMIT_ACTIVE.patch
|
||||
patch_apply server-JobObjects/0008-kernel32-tests-Add-tests-for-JOB_OBJECT_LIMIT_BREAKA.patch
|
||||
patch_apply server-JobObjects/0009-kernel32-tests-Add-tests-for-job-inheritance.patch
|
||||
patch_apply server-JobObjects/0010-server-Basic-implementation-of-job-objects.patch
|
||||
(
|
||||
echo '+ { "Michael Müller", "server: Return error when opening a terminating process.", 3 },';
|
||||
echo '+ { "Sebastian Lackner", "kernel32/tests: Allow multiple subprocess commands in process tests.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for IsProcessInJob.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for TerminateJobObject.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for QueryInformationJobObject.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for job object completion ports.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for JOB_OBJECT_LIMIT_ACTIVE_PROCESS.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for JOB_OBJECT_LIMIT_BREAKAWAY_OK.", 1 },';
|
||||
echo '+ { "Andrew Cook", "kernel32/tests: Add tests for job inheritance.", 1 },';
|
||||
echo '+ { "Andrew Cook", "server: Basic implementation of job objects.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset server-Key_State
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
# | * [#27238] Fallback to global key state for threads without a queue
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * server/queue.c
|
||||
# |
|
||||
if test "$enable_server_Key_State" -eq 1; then
|
||||
patch_apply server-Key_State/0001-server-Fall-back-to-global-key-state-when-thread-doe.patch
|
||||
(
|
||||
echo '+ { "Sebastian Lackner", "server: Fall back to global key state when thread doesn'\''t have a queue.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
|
@ -0,0 +1,664 @@
|
||||
From 42af9f44c92dbe151b3ec5b4f57e941133349648 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Cook <ariscop@gmail.com>
|
||||
Date: Thu, 26 Feb 2015 12:25:23 +1100
|
||||
Subject: server: Basic implementation of job objects.
|
||||
|
||||
This patch includes a (hopefully) complete implementation of process
|
||||
tracking in job objects, but no limits or events outside of those.
|
||||
|
||||
This does not implement nested jobs as found in windows 8.
|
||||
|
||||
Changes by Sebastian Lackner <sebastian@fds-team.de>:
|
||||
* Only use a single list instead of two for active / all processes.
|
||||
* Various cleanups to fit with the rest of the wineserver coding style.
|
||||
---
|
||||
dlls/kernel32/tests/process.c | 8 +-
|
||||
dlls/ntdll/sync.c | 139 ++++++++++++++++++++---
|
||||
include/winnt.h | 4 +
|
||||
server/process.c | 256 ++++++++++++++++++++++++++++++++++++++++++
|
||||
server/process.h | 3 +
|
||||
server/protocol.def | 27 +++++
|
||||
6 files changed, 417 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
|
||||
index 9d7d8b8..3f78d74 100644
|
||||
--- a/dlls/kernel32/tests/process.c
|
||||
+++ b/dlls/kernel32/tests/process.c
|
||||
@@ -2244,7 +2244,6 @@ static void test_IsProcessInJob(void)
|
||||
out = FALSE;
|
||||
ret = pIsProcessInJob(pi.hProcess, job, &out);
|
||||
ok(ret, "IsProcessInJob error %u\n", GetLastError());
|
||||
- todo_wine
|
||||
ok(out, "IsProcessInJob returned out=%u\n", out);
|
||||
|
||||
TerminateProcess(pi.hProcess, 0);
|
||||
@@ -2255,7 +2254,6 @@ static void test_IsProcessInJob(void)
|
||||
out = FALSE;
|
||||
ret = pIsProcessInJob(pi.hProcess, job, &out);
|
||||
ok(ret, "IsProcessInJob error %u\n", GetLastError());
|
||||
- todo_wine
|
||||
ok(out, "IsProcessInJob returned out=%u\n", out);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
@@ -2282,13 +2280,11 @@ static void test_TerminateJobObject(void)
|
||||
ok(ret, "TerminateJobObject error %u\n", GetLastError());
|
||||
|
||||
dwret = WaitForSingleObject(pi.hProcess, 500);
|
||||
- todo_wine
|
||||
ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
|
||||
if (dwret == WAIT_TIMEOUT) TerminateProcess(pi.hProcess, 0);
|
||||
|
||||
ret = GetExitCodeProcess(pi.hProcess, &dwret);
|
||||
ok(ret, "GetExitCodeProcess error %u\n", GetLastError());
|
||||
- todo_wine
|
||||
ok(dwret == 123 || broken(dwret == 0) /* randomly fails on Win 2000 / XP */,
|
||||
"wrong exitcode %u\n", dwret);
|
||||
|
||||
@@ -2397,6 +2393,7 @@ static void test_CompletionPort(void)
|
||||
port_info.CompletionKey = job;
|
||||
port_info.CompletionPort = port;
|
||||
ret = pSetInformationJobObject(job, JobObjectAssociateCompletionPortInformation, &port_info, sizeof(port_info));
|
||||
+ todo_wine
|
||||
ok(ret, "SetInformationJobObject error %u\n", GetLastError());
|
||||
|
||||
create_process("wait", &pi);
|
||||
@@ -2564,7 +2561,6 @@ static HANDLE test_LimitActiveProcesses(void)
|
||||
out = FALSE;
|
||||
ret = pIsProcessInJob(pi[0].hProcess, job, &out);
|
||||
ok(ret, "IsProcessInJob error %u\n", GetLastError());
|
||||
- todo_wine
|
||||
ok(out, "IsProcessInJob returned out=%u\n", out);
|
||||
|
||||
dwret = WaitForSingleObject(pi[0].hProcess, 500);
|
||||
@@ -2595,9 +2591,7 @@ static void test_BreakawayOk(HANDLE job)
|
||||
snprintf(buffer, MAX_PATH, "\"%s\" tests/process.c %s", selfname, "exit");
|
||||
|
||||
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
|
||||
- todo_wine
|
||||
ok(!ret, "CreateProcessA expected failure\n");
|
||||
- todo_wine
|
||||
expect_eq_d(ERROR_ACCESS_DENIED, GetLastError());
|
||||
|
||||
if (ret)
|
||||
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
|
||||
index 08fab44..56714ef 100644
|
||||
--- a/dlls/ntdll/sync.c
|
||||
+++ b/dlls/ntdll/sync.c
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "wine/server.h"
|
||||
#include "wine/debug.h"
|
||||
#include "ntdll_misc.h"
|
||||
+#include "winnt.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
|
||||
|
||||
@@ -567,9 +568,36 @@ NTSTATUS WINAPI NtQueryMutant(IN HANDLE handle,
|
||||
*/
|
||||
NTSTATUS WINAPI NtCreateJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
||||
{
|
||||
- FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" );
|
||||
- *handle = (HANDLE)0xdead;
|
||||
- return STATUS_SUCCESS;
|
||||
+ DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
|
||||
+ NTSTATUS ret;
|
||||
+ struct security_descriptor *sd = NULL;
|
||||
+ struct object_attributes objattr;
|
||||
+
|
||||
+ if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
|
||||
+
|
||||
+ objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
|
||||
+ objattr.sd_len = 0;
|
||||
+ objattr.name_len = len;
|
||||
+ if (attr)
|
||||
+ {
|
||||
+ ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len );
|
||||
+ if (ret != STATUS_SUCCESS) return ret;
|
||||
+ }
|
||||
+
|
||||
+ SERVER_START_REQ( create_job )
|
||||
+ {
|
||||
+ req->access = access;
|
||||
+ req->attributes = attr ? attr->Attributes : 0;
|
||||
+ wine_server_add_data( req, &objattr, sizeof(objattr) );
|
||||
+ if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len );
|
||||
+ if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
|
||||
+ ret = wine_server_call( req );
|
||||
+ *handle = wine_server_ptr_handle( reply->handle );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+
|
||||
+ NTDLL_free_struct_sd( sd );
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -588,8 +616,19 @@ NTSTATUS WINAPI NtOpenJobObject( PHANDLE handle, ACCESS_MASK access, const OBJEC
|
||||
*/
|
||||
NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status )
|
||||
{
|
||||
- FIXME( "stub: %p %x\n", handle, status );
|
||||
- return STATUS_SUCCESS;
|
||||
+ NTSTATUS ret;
|
||||
+
|
||||
+ TRACE( "(%p, %d)\n", handle, status );
|
||||
+
|
||||
+ SERVER_START_REQ( terminate_job )
|
||||
+ {
|
||||
+ req->handle = wine_server_obj_handle( handle );
|
||||
+ req->status = status;
|
||||
+ ret = wine_server_call( req );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -599,8 +638,17 @@ NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status )
|
||||
NTSTATUS WINAPI NtQueryInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info,
|
||||
ULONG len, PULONG ret_len )
|
||||
{
|
||||
- FIXME( "stub: %p %u %p %u %p\n", handle, class, info, len, ret_len );
|
||||
- return STATUS_NOT_IMPLEMENTED;
|
||||
+ TRACE( "%p %u %p %u %p\n", handle, class, info, len, ret_len );
|
||||
+
|
||||
+ if (class >= MaxJobObjectInfoClass)
|
||||
+ return STATUS_INVALID_PARAMETER;
|
||||
+
|
||||
+ switch (class)
|
||||
+ {
|
||||
+ default:
|
||||
+ FIXME( "stub: %p %u %p %u %p\n", handle, class, info, len, ret_len );
|
||||
+ return STATUS_NOT_IMPLEMENTED;
|
||||
+ }
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -609,8 +657,51 @@ NTSTATUS WINAPI NtQueryInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS c
|
||||
*/
|
||||
NTSTATUS WINAPI NtSetInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info, ULONG len )
|
||||
{
|
||||
- FIXME( "stub: %p %u %p %u\n", handle, class, info, len );
|
||||
- return STATUS_SUCCESS;
|
||||
+ JOBOBJECT_BASIC_LIMIT_INFORMATION *basic_limit;
|
||||
+ NTSTATUS status = STATUS_SUCCESS;
|
||||
+
|
||||
+ TRACE( "(%p, %u, %p, %u)\n", handle, class, info, len );
|
||||
+
|
||||
+ if (class >= MaxJobObjectInfoClass)
|
||||
+ return STATUS_INVALID_PARAMETER;
|
||||
+
|
||||
+ switch (class)
|
||||
+ {
|
||||
+
|
||||
+ case JobObjectExtendedLimitInformation:
|
||||
+ if (len != sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION))
|
||||
+ return STATUS_INVALID_PARAMETER;
|
||||
+
|
||||
+ basic_limit = &(((JOBOBJECT_EXTENDED_LIMIT_INFORMATION *)info)->BasicLimitInformation);
|
||||
+ if (basic_limit->LimitFlags & ~JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS)
|
||||
+ return STATUS_INVALID_PARAMETER;
|
||||
+
|
||||
+ goto set_basic_limits;
|
||||
+
|
||||
+ case JobObjectBasicLimitInformation:
|
||||
+ if (len != sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION))
|
||||
+ return STATUS_INVALID_PARAMETER;
|
||||
+
|
||||
+ basic_limit = info;
|
||||
+ if (basic_limit->LimitFlags & ~JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS)
|
||||
+ return STATUS_INVALID_PARAMETER;
|
||||
+
|
||||
+ set_basic_limits:
|
||||
+ SERVER_START_REQ( job_set_limits )
|
||||
+ {
|
||||
+ req->handle = wine_server_obj_handle( handle );
|
||||
+ req->limit_flags = basic_limit->LimitFlags;
|
||||
+ status = wine_server_call( req );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ FIXME( "stub: %p %u %p %u\n", handle, class, info, len );
|
||||
+ return STATUS_NOT_IMPLEMENTED;
|
||||
+ }
|
||||
+
|
||||
+ return status;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -619,8 +710,19 @@ NTSTATUS WINAPI NtSetInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS cla
|
||||
*/
|
||||
NTSTATUS WINAPI NtIsProcessInJob( HANDLE process, HANDLE job )
|
||||
{
|
||||
- FIXME( "stub: %p %p\n", process, job );
|
||||
- return STATUS_PROCESS_NOT_IN_JOB;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE( "(%p %p)\n", job, process );
|
||||
+
|
||||
+ SERVER_START_REQ( process_in_job )
|
||||
+ {
|
||||
+ req->process_handle = wine_server_obj_handle( process );
|
||||
+ req->job_handle = wine_server_obj_handle( job );
|
||||
+ status = wine_server_call( req );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+
|
||||
+ return status;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -629,8 +731,19 @@ NTSTATUS WINAPI NtIsProcessInJob( HANDLE process, HANDLE job )
|
||||
*/
|
||||
NTSTATUS WINAPI NtAssignProcessToJobObject( HANDLE job, HANDLE process )
|
||||
{
|
||||
- FIXME( "stub: %p %p\n", job, process );
|
||||
- return STATUS_SUCCESS;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE( "(%p %p)\n", job, process );
|
||||
+
|
||||
+ SERVER_START_REQ( job_assign )
|
||||
+ {
|
||||
+ req->job_handle = wine_server_obj_handle( job );
|
||||
+ req->process_handle = wine_server_obj_handle( process );
|
||||
+ status = wine_server_call( req );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+
|
||||
+ return status;
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/include/winnt.h b/include/winnt.h
|
||||
index 4b06b2c..c2aa50e 100644
|
||||
--- a/include/winnt.h
|
||||
+++ b/include/winnt.h
|
||||
@@ -5610,6 +5610,10 @@ typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
|
||||
#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000
|
||||
#define JOB_OBJECT_LIMIT_SUBSET_AFFINITY 0x00004000
|
||||
|
||||
+#define JOB_OBJECT_LIMIT_VALID_FLAGS 0x0007ffff
|
||||
+#define JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS 0x000000ff
|
||||
+#define JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS 0x00007fff
|
||||
+
|
||||
typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP
|
||||
{
|
||||
RelationProcessorCore = 0,
|
||||
diff --git a/server/process.c b/server/process.c
|
||||
index 0712a5b..850a9c8 100644
|
||||
--- a/server/process.c
|
||||
+++ b/server/process.c
|
||||
@@ -65,6 +65,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access
|
||||
static struct security_descriptor *process_get_sd( struct object *obj );
|
||||
static void process_poll_event( struct fd *fd, int event );
|
||||
static void process_destroy( struct object *obj );
|
||||
+static void terminate_process( struct process *process, struct thread *skip, int exit_code );
|
||||
|
||||
static const struct object_ops process_ops =
|
||||
{
|
||||
@@ -134,6 +135,152 @@ static const struct object_ops startup_info_ops =
|
||||
startup_info_destroy /* destroy */
|
||||
};
|
||||
|
||||
+/* job object */
|
||||
+
|
||||
+static void job_dump( struct object *obj, int verbose );
|
||||
+static struct object_type *job_get_type( struct object *obj );
|
||||
+static int job_signaled( struct object *obj, struct wait_queue_entry *entry );
|
||||
+static unsigned int job_map_access( struct object *obj, unsigned int access );
|
||||
+static void job_destroy( struct object *obj );
|
||||
+
|
||||
+struct job
|
||||
+{
|
||||
+ struct object obj; /* object header */
|
||||
+ struct list process_list; /* list of all processes */
|
||||
+ int num_processes; /* count of running processes */
|
||||
+ int limit_flags; /* limit flags */
|
||||
+};
|
||||
+
|
||||
+static const struct object_ops job_ops =
|
||||
+{
|
||||
+ sizeof(struct job), /* size */
|
||||
+ job_dump, /* dump */
|
||||
+ job_get_type, /* get_type */
|
||||
+ add_queue, /* add_queue */
|
||||
+ remove_queue, /* remove_queue */
|
||||
+ job_signaled, /* signaled */
|
||||
+ no_satisfied, /* satisfied */
|
||||
+ no_signal, /* signal */
|
||||
+ no_get_fd, /* get_fd */
|
||||
+ job_map_access, /* map_access */
|
||||
+ default_get_sd, /* get_sd */
|
||||
+ default_set_sd, /* set_sd */
|
||||
+ no_lookup_name, /* lookup_name */
|
||||
+ no_open_file, /* open_file */
|
||||
+ no_close_handle, /* close_handle */
|
||||
+ job_destroy /* destroy */
|
||||
+};
|
||||
+
|
||||
+static struct job *create_job_object( struct directory *root, const struct unicode_str *name,
|
||||
+ unsigned int attr, const struct security_descriptor *sd )
|
||||
+{
|
||||
+ struct job *job;
|
||||
+
|
||||
+ if ((job = create_named_object_dir( root, name, attr, &job_ops )))
|
||||
+ {
|
||||
+ if (get_error() != STATUS_OBJECT_NAME_EXISTS)
|
||||
+ {
|
||||
+ /* initialize it if it didn't already exist */
|
||||
+ if (sd) default_set_sd( &job->obj, sd, OWNER_SECURITY_INFORMATION |
|
||||
+ GROUP_SECURITY_INFORMATION |
|
||||
+ DACL_SECURITY_INFORMATION |
|
||||
+ SACL_SECURITY_INFORMATION );
|
||||
+ list_init( &job->process_list );
|
||||
+ job->num_processes = 0;
|
||||
+ job->limit_flags = 0;
|
||||
+ }
|
||||
+ }
|
||||
+ return job;
|
||||
+}
|
||||
+
|
||||
+static struct job *get_job_obj( struct process *process, obj_handle_t handle, unsigned int access )
|
||||
+{
|
||||
+ return (struct job *)get_handle_obj( process, handle, access, &job_ops );
|
||||
+}
|
||||
+
|
||||
+static struct object_type *job_get_type( struct object *obj )
|
||||
+{
|
||||
+ static const WCHAR name[] = {'J','o','b'};
|
||||
+ static const struct unicode_str str = { name, sizeof(name) };
|
||||
+ return get_object_type( &str );
|
||||
+};
|
||||
+
|
||||
+static unsigned int job_map_access( struct object *obj, unsigned int access )
|
||||
+{
|
||||
+ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ;
|
||||
+ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE;
|
||||
+ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE;
|
||||
+ if (access & GENERIC_ALL) access |= JOB_OBJECT_ALL_ACCESS;
|
||||
+ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
|
||||
+}
|
||||
+
|
||||
+static int add_job_process( struct job *job, struct process *process )
|
||||
+{
|
||||
+ assert( job->obj.ops == &job_ops );
|
||||
+
|
||||
+ if (process->job)
|
||||
+ {
|
||||
+ set_error(STATUS_ACCESS_DENIED);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ process->job = (struct job *)grab_object( job );
|
||||
+ list_add_tail( &job->process_list, &process->job_entry );
|
||||
+ job->num_processes++;
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+/* called when a process has terminated, allow one additional process */
|
||||
+static void release_job_process( struct process *process )
|
||||
+{
|
||||
+ struct job *job = process->job;
|
||||
+
|
||||
+ if (!job)
|
||||
+ return;
|
||||
+
|
||||
+ assert( job->obj.ops == &job_ops );
|
||||
+ assert( job->num_processes );
|
||||
+ job->num_processes--;
|
||||
+}
|
||||
+
|
||||
+static void terminate_job( struct job *job, int exit_code )
|
||||
+{
|
||||
+ for (;;) /* restart from the beginning of the list every time */
|
||||
+ {
|
||||
+ struct process *process;
|
||||
+
|
||||
+ /* find the first process associcated with this job and still running */
|
||||
+ LIST_FOR_EACH_ENTRY( process, &job->process_list, struct process, job_entry )
|
||||
+ {
|
||||
+ if (process->running_threads) break;
|
||||
+ }
|
||||
+ if (&process->job_entry == &job->process_list) break; /* no process found */
|
||||
+ assert( process->job == job );
|
||||
+ terminate_process( process, NULL, exit_code );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void job_destroy( struct object *obj )
|
||||
+{
|
||||
+ struct job *job = (struct job *)obj;
|
||||
+ assert( obj->ops == &job_ops );
|
||||
+
|
||||
+ assert( !job->num_processes );
|
||||
+ assert( list_empty(&job->process_list) );
|
||||
+}
|
||||
+
|
||||
+static void job_dump( struct object *obj, int verbose )
|
||||
+{
|
||||
+ struct job *job = (struct job *)obj;
|
||||
+ assert( obj->ops == &job_ops );
|
||||
+ fprintf( stderr, "Job processes=%d\n", list_count(&job->process_list) );
|
||||
+}
|
||||
+
|
||||
+static int job_signaled( struct object *obj, struct wait_queue_entry *entry )
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
struct ptid_entry
|
||||
{
|
||||
@@ -327,6 +474,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit
|
||||
process->debug_children = 0;
|
||||
process->is_terminating = 0;
|
||||
process->is_terminated = 0;
|
||||
+ process->job = NULL;
|
||||
process->console = NULL;
|
||||
process->startup_state = STARTUP_IN_PROGRESS;
|
||||
process->startup_info = NULL;
|
||||
@@ -425,6 +573,12 @@ static void process_destroy( struct object *obj )
|
||||
|
||||
close_process_handles( process );
|
||||
set_process_startup_state( process, STARTUP_ABORTED );
|
||||
+
|
||||
+ if (process->job)
|
||||
+ {
|
||||
+ list_remove( &process->job_entry );
|
||||
+ release_object( process->job );
|
||||
+ }
|
||||
if (process->console) release_object( process->console );
|
||||
if (process->parent) release_object( process->parent );
|
||||
if (process->msg_fd) release_object( process->msg_fd );
|
||||
@@ -709,6 +863,7 @@ static void process_killed( struct process *process )
|
||||
remove_process_locks( process );
|
||||
set_process_startup_state( process, STARTUP_ABORTED );
|
||||
finish_process_tracing( process );
|
||||
+ release_job_process( process );
|
||||
start_sigkill_timer( process );
|
||||
wake_up( &process->obj, 0 );
|
||||
}
|
||||
@@ -954,6 +1109,14 @@ DECL_HANDLER(new_process)
|
||||
return;
|
||||
}
|
||||
|
||||
+ if (parent->job && (req->create_flags & CREATE_BREAKAWAY_FROM_JOB) &&
|
||||
+ !(parent->job->limit_flags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
|
||||
+ {
|
||||
+ set_error( STATUS_ACCESS_DENIED );
|
||||
+ close( socket_fd );
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (!req->info_size) /* create an orphaned process */
|
||||
{
|
||||
create_process( socket_fd, NULL, 0 );
|
||||
@@ -1024,6 +1187,12 @@ DECL_HANDLER(new_process)
|
||||
&& !(req->create_flags & DEBUG_ONLY_THIS_PROCESS);
|
||||
process->startup_info = (struct startup_info *)grab_object( info );
|
||||
|
||||
+ if (parent->job && !((req->create_flags & CREATE_BREAKAWAY_FROM_JOB) ||
|
||||
+ (parent->job->limit_flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
|
||||
+ {
|
||||
+ add_job_process( parent->job, process );
|
||||
+ }
|
||||
+
|
||||
/* connect to the window station */
|
||||
connect_process_winstation( process, current );
|
||||
|
||||
@@ -1361,3 +1530,90 @@ DECL_HANDLER(make_process_system)
|
||||
shutdown_timeout = add_timeout_user( master_socket_timeout, server_shutdown_timeout, NULL );
|
||||
}
|
||||
}
|
||||
+
|
||||
+/* create a new job object */
|
||||
+DECL_HANDLER(create_job)
|
||||
+{
|
||||
+ struct job *job;
|
||||
+ struct unicode_str name;
|
||||
+ struct directory *root = NULL;
|
||||
+ const struct object_attributes *objattr = get_req_data();
|
||||
+ const struct security_descriptor *sd;
|
||||
+
|
||||
+ if (!objattr_is_valid( objattr, get_req_data_size() )) return;
|
||||
+
|
||||
+ sd = objattr->sd_len ? (const struct security_descriptor *)(objattr + 1) : NULL;
|
||||
+ objattr_get_name( objattr, &name );
|
||||
+
|
||||
+ if (objattr->rootdir && !(root = get_directory_obj( current->process, objattr->rootdir, 0 ))) return;
|
||||
+
|
||||
+ if ((job = create_job_object( root, &name, req->attributes, sd )))
|
||||
+ {
|
||||
+ if (get_error() == STATUS_OBJECT_NAME_EXISTS)
|
||||
+ reply->handle = alloc_handle( current->process, job, req->access, req->attributes );
|
||||
+ else
|
||||
+ reply->handle = alloc_handle_no_access_check( current->process, job, req->access, req->attributes );
|
||||
+ release_object( job );
|
||||
+ }
|
||||
+ if (root) release_object( root );
|
||||
+}
|
||||
+
|
||||
+/* assign a job object to a process */
|
||||
+DECL_HANDLER(job_assign)
|
||||
+{
|
||||
+ struct process *process;
|
||||
+ struct job *job;
|
||||
+
|
||||
+ if (!(job = get_job_obj( current->process, req->job_handle, JOB_OBJECT_ASSIGN_PROCESS )))
|
||||
+ return;
|
||||
+
|
||||
+ if ((process = get_process_from_handle( req->process_handle, PROCESS_SET_QUOTA | PROCESS_TERMINATE )))
|
||||
+ {
|
||||
+ add_job_process( job, process );
|
||||
+ release_object(process);
|
||||
+ }
|
||||
+
|
||||
+ release_object(job);
|
||||
+}
|
||||
+
|
||||
+/* check if a process is associated with a job */
|
||||
+DECL_HANDLER(process_in_job)
|
||||
+{
|
||||
+ struct process *process;
|
||||
+ struct job *job;
|
||||
+
|
||||
+ if (!(job = get_job_obj( current->process, req->job_handle, JOB_OBJECT_ASSIGN_PROCESS )))
|
||||
+ return;
|
||||
+
|
||||
+ if ((process = get_process_from_handle( req->process_handle, PROCESS_QUERY_INFORMATION )))
|
||||
+ {
|
||||
+ set_error((process->job == job) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB);
|
||||
+ release_object(process);
|
||||
+ }
|
||||
+
|
||||
+ release_object(job);
|
||||
+}
|
||||
+
|
||||
+/* terminate all processes associated with the job */
|
||||
+DECL_HANDLER(terminate_job)
|
||||
+{
|
||||
+ struct job *job;
|
||||
+
|
||||
+ if ((job = get_job_obj( current->process, req->handle, JOB_OBJECT_TERMINATE )))
|
||||
+ {
|
||||
+ terminate_job(job, req->status);
|
||||
+ release_object(job);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* update limits of the job object */
|
||||
+DECL_HANDLER(job_set_limits)
|
||||
+{
|
||||
+ struct job *job;
|
||||
+
|
||||
+ if ((job = get_job_obj( current->process, req->handle, JOB_OBJECT_SET_ATTRIBUTES )))
|
||||
+ {
|
||||
+ job->limit_flags = req->limit_flags;
|
||||
+ release_object(job);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/server/process.h b/server/process.h
|
||||
index da6d3da..ae83b0e 100644
|
||||
--- a/server/process.h
|
||||
+++ b/server/process.h
|
||||
@@ -26,6 +26,7 @@
|
||||
struct atom_table;
|
||||
struct handle_table;
|
||||
struct startup_info;
|
||||
+struct job;
|
||||
|
||||
/* process startup state */
|
||||
enum startup_state { STARTUP_IN_PROGRESS, STARTUP_DONE, STARTUP_ABORTED };
|
||||
@@ -76,6 +77,8 @@ struct process
|
||||
unsigned int debug_children:1;/* also debug all child processes */
|
||||
unsigned int is_terminating:1;/* is process terminating? */
|
||||
unsigned int is_terminated:1; /* is process terminated? */
|
||||
+ struct job *job; /* job object ascoicated with this process */
|
||||
+ struct list job_entry; /* list entry for job object */
|
||||
struct list locks; /* list of file locks owned by the process */
|
||||
struct list classes; /* window classes owned by the process */
|
||||
struct console_input*console; /* console input */
|
||||
diff --git a/server/protocol.def b/server/protocol.def
|
||||
index fc6bec5..cc39ef1 100644
|
||||
--- a/server/protocol.def
|
||||
+++ b/server/protocol.def
|
||||
@@ -681,6 +681,33 @@ struct rawinput_device
|
||||
obj_handle_t thandle; /* thread handle (in the current process) */
|
||||
@END
|
||||
|
||||
+@REQ(create_job)
|
||||
+ unsigned int access; /* wanted access rights */
|
||||
+ unsigned int attributes; /* object attributes */
|
||||
+ VARARG(objattr,object_attributes); /* object attributes */
|
||||
+@REPLY
|
||||
+ obj_handle_t handle; /* handle to the job */
|
||||
+@END
|
||||
+
|
||||
+@REQ(terminate_job)
|
||||
+ obj_handle_t handle;
|
||||
+ int status;
|
||||
+@END
|
||||
+
|
||||
+@REQ(process_in_job)
|
||||
+ obj_handle_t job_handle;
|
||||
+ obj_handle_t process_handle;
|
||||
+@END
|
||||
+
|
||||
+@REQ(job_assign)
|
||||
+ obj_handle_t job_handle;
|
||||
+ obj_handle_t process_handle;
|
||||
+@END
|
||||
+
|
||||
+@REQ(job_set_limits)
|
||||
+ obj_handle_t handle;
|
||||
+ unsigned int limit_flags;
|
||||
+@END
|
||||
|
||||
/* Retrieve information about a newly started process */
|
||||
@REQ(get_new_process_info)
|
||||
--
|
||||
2.3.0
|
||||
|
3
patches/server-JobObjects/definition
Normal file
3
patches/server-JobObjects/definition
Normal file
@ -0,0 +1,3 @@
|
||||
Depends: kernel32-Console_Handles
|
||||
Depends: server-OpenProcess
|
||||
Depends: server-Misc_ACL
|
Loading…
Reference in New Issue
Block a user