Added patch for basic implementation of job objects.

This commit is contained in:
Sebastian Lackner 2015-02-27 19:20:15 +01:00
parent 2de8f84fb0
commit 480bab002e
14 changed files with 744 additions and 66 deletions

1
debian/changelog vendored
View File

@ -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).

View File

@ -1 +0,0 @@
Depends: kernel32-Console_Handles

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
Depends: kernel32-Console_Handles
Depends: server-OpenProcess
Depends: server-Misc_ACL