2015-02-27 21:52:37 -08:00
|
|
|
From 91a03090c8aafda8f47004734a04a0ac8d83a901 Mon Sep 17 00:00:00 2001
|
2015-02-27 10:20:15 -08:00
|
|
|
From: Andrew Cook <ariscop@gmail.com>
|
|
|
|
Date: Thu, 26 Feb 2015 12:25:23 +1100
|
2015-02-27 21:52:37 -08:00
|
|
|
Subject: server: Basic implementation of job objects. (rev 2)
|
2015-02-27 10:20:15 -08:00
|
|
|
|
|
|
|
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.
|
2015-02-27 21:52:37 -08:00
|
|
|
|
|
|
|
Changes in revision 2:
|
|
|
|
* Fix a wineserver crash when terminated processes are added to a job.
|
2015-02-27 10:20:15 -08:00
|
|
|
---
|
2015-02-27 21:52:37 -08:00
|
|
|
dlls/kernel32/tests/process.c | 10 +-
|
2015-02-27 10:20:15 -08:00
|
|
|
dlls/ntdll/sync.c | 139 ++++++++++++++++++++---
|
|
|
|
include/winnt.h | 4 +
|
2015-02-27 21:52:37 -08:00
|
|
|
server/process.c | 259 ++++++++++++++++++++++++++++++++++++++++++
|
2015-02-27 10:20:15 -08:00
|
|
|
server/process.h | 3 +
|
|
|
|
server/protocol.def | 27 +++++
|
2015-02-27 21:52:37 -08:00
|
|
|
6 files changed, 420 insertions(+), 22 deletions(-)
|
2015-02-27 10:20:15 -08:00
|
|
|
|
|
|
|
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
|
2015-02-27 21:52:37 -08:00
|
|
|
index a464db2..e23d046 100644
|
2015-02-27 10:20:15 -08:00
|
|
|
--- 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);
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -2269,9 +2267,7 @@ static void test_IsProcessInJob(void)
|
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
ret = pAssignProcessToJobObject(job, pi.hProcess);
|
|
|
|
- todo_wine
|
|
|
|
ok(!ret, "AssignProcessToJobObject unexpectedly succeeded\n");
|
|
|
|
- todo_wine
|
|
|
|
expect_eq_d(ERROR_ACCESS_DENIED, GetLastError());
|
|
|
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
@@ -2299,13 +2295,11 @@ static void test_TerminateJobObject(void)
|
2015-02-27 10:20:15 -08:00
|
|
|
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);
|
|
|
|
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -2414,6 +2408,7 @@ static void test_CompletionPort(void)
|
2015-02-27 10:20:15 -08:00
|
|
|
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);
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -2581,7 +2576,6 @@ static HANDLE test_LimitActiveProcesses(void)
|
2015-02-27 10:20:15 -08:00
|
|
|
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);
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -2612,9 +2606,7 @@ static void test_BreakawayOk(HANDLE job)
|
2015-02-27 10:20:15 -08:00
|
|
|
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
|
2015-02-27 21:52:37 -08:00
|
|
|
index 0712a5b..6384203 100644
|
2015-02-27 10:20:15 -08:00
|
|
|
--- 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 =
|
|
|
|
{
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -134,6 +135,157 @@ static const struct object_ops startup_info_ops =
|
2015-02-27 10:20:15 -08:00
|
|
|
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 );
|
|
|
|
+
|
2015-02-27 21:52:37 -08:00
|
|
|
+ if (!process->running_threads)
|
|
|
|
+ {
|
|
|
|
+ set_error( STATUS_PROCESS_IS_TERMINATING );
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
2015-02-27 10:20:15 -08:00
|
|
|
+ if (process->job)
|
|
|
|
+ {
|
2015-02-27 21:52:37 -08:00
|
|
|
+ set_error( STATUS_ACCESS_DENIED );
|
2015-02-27 10:20:15 -08:00
|
|
|
+ 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
|
|
|
|
{
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -327,6 +479,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit
|
2015-02-27 10:20:15 -08:00
|
|
|
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;
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -425,6 +578,12 @@ static void process_destroy( struct object *obj )
|
2015-02-27 10:20:15 -08:00
|
|
|
|
|
|
|
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 );
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -709,6 +868,7 @@ static void process_killed( struct process *process )
|
2015-02-27 10:20:15 -08:00
|
|
|
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 );
|
|
|
|
}
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -954,6 +1114,14 @@ DECL_HANDLER(new_process)
|
2015-02-27 10:20:15 -08:00
|
|
|
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 );
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -1024,6 +1192,12 @@ DECL_HANDLER(new_process)
|
2015-02-27 10:20:15 -08:00
|
|
|
&& !(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 );
|
|
|
|
|
2015-02-27 21:52:37 -08:00
|
|
|
@@ -1361,3 +1535,88 @@ DECL_HANDLER(make_process_system)
|
2015-02-27 10:20:15 -08:00
|
|
|
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;
|
2015-02-27 16:20:47 -08:00
|
|
|
+ struct job *job = get_job_obj( current->process, req->job_handle, JOB_OBJECT_ASSIGN_PROCESS );
|
2015-02-27 10:20:15 -08:00
|
|
|
+
|
2015-02-27 16:20:47 -08:00
|
|
|
+ if (job)
|
2015-02-27 10:20:15 -08:00
|
|
|
+ {
|
2015-02-27 16:20:47 -08:00
|
|
|
+ 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);
|
2015-02-27 10:20:15 -08:00
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* check if a process is associated with a job */
|
|
|
|
+DECL_HANDLER(process_in_job)
|
|
|
|
+{
|
|
|
|
+ struct process *process;
|
2015-02-27 16:20:47 -08:00
|
|
|
+ struct job *job = get_job_obj( current->process, req->job_handle, JOB_OBJECT_ASSIGN_PROCESS );
|
2015-02-27 10:20:15 -08:00
|
|
|
+
|
2015-02-27 16:20:47 -08:00
|
|
|
+ if (job)
|
2015-02-27 10:20:15 -08:00
|
|
|
+ {
|
2015-02-27 16:20:47 -08:00
|
|
|
+ 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);
|
2015-02-27 10:20:15 -08:00
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* terminate all processes associated with the job */
|
|
|
|
+DECL_HANDLER(terminate_job)
|
|
|
|
+{
|
2015-02-27 16:20:47 -08:00
|
|
|
+ struct job *job = get_job_obj( current->process, req->handle, JOB_OBJECT_TERMINATE );
|
2015-02-27 10:20:15 -08:00
|
|
|
+
|
2015-02-27 16:20:47 -08:00
|
|
|
+ if (job)
|
2015-02-27 10:20:15 -08:00
|
|
|
+ {
|
2015-02-27 16:20:47 -08:00
|
|
|
+ terminate_job( job, req->status );
|
|
|
|
+ release_object( job );
|
2015-02-27 10:20:15 -08:00
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* update limits of the job object */
|
|
|
|
+DECL_HANDLER(job_set_limits)
|
|
|
|
+{
|
2015-02-27 16:20:47 -08:00
|
|
|
+ struct job *job = get_job_obj( current->process, req->handle, JOB_OBJECT_SET_ATTRIBUTES );
|
2015-02-27 10:20:15 -08:00
|
|
|
+
|
2015-02-27 16:20:47 -08:00
|
|
|
+ if (job)
|
2015-02-27 10:20:15 -08:00
|
|
|
+ {
|
|
|
|
+ job->limit_flags = req->limit_flags;
|
2015-02-27 16:20:47 -08:00
|
|
|
+ release_object( job );
|
2015-02-27 10:20:15 -08:00
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
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
|
|
|
|
|