[PATCH 4/4] server: Support process job lists.
Paul Gofman
pgofman at codeweavers.com
Wed May 19 19:29:04 CDT 2021
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
dlls/kernel32/tests/process.c | 260 +++++++++++++++++++++++++++++++++-
server/process.c | 12 ++
2 files changed, 268 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
index 44c37c668fc..beb69352e4d 100644
--- a/dlls/kernel32/tests/process.c
+++ b/dlls/kernel32/tests/process.c
@@ -4537,15 +4537,22 @@ static void test_nested_jobs(void)
CloseHandle(job2);
}
-static void test_job_list_attribute(void)
+static void test_job_list_attribute(HANDLE parent_job)
{
+ JOBOBJECT_BASIC_ACCOUNTING_INFORMATION job_info;
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
+ JOBOBJECT_ASSOCIATE_COMPLETION_PORT port_info;
PPROC_THREAD_ATTRIBUTE_LIST attrs;
char buffer[MAX_PATH + 19];
PROCESS_INFORMATION pi;
+ OVERLAPPED *overlapped;
+ HANDLE jobs[2], port;
STARTUPINFOEXA si;
- HANDLE jobs[2];
+ ULONG_PTR value;
+ BOOL ret, out;
+ HANDLE tmp;
SIZE_T size;
- BOOL ret;
+ DWORD key;
if (!pInitializeProcThreadAttributeList)
{
@@ -4613,8 +4620,253 @@ static void test_job_list_attribute(void)
ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected ret %#x, GetLastError() %u.\n",
ret, GetLastError());
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &parent_job,
+ sizeof(parent_job), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
+ (STARTUPINFOA *)&si, &pi);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+
+ jobs[0] = pCreateJobObjectW(NULL, NULL);
+ ok(!!jobs[0], "CreateJobObjectA error %u\n", GetLastError());
+ jobs[1] = pCreateJobObjectW(NULL, NULL);
+ ok(!!jobs[1], "CreateJobObjectA error %u\n", GetLastError());
+
+ /* Breakaway works for the inherited job only. */
+ limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+ ret = pSetInformationJobObject(parent_job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+ ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+ limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK
+ | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
+ ret = pSetInformationJobObject(jobs[1], JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+ ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1,
+ sizeof(*jobs), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT
+ | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, (STARTUPINFOA *)&si, &pi);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[1], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[0], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+
+ CloseHandle(jobs[1]);
+ jobs[1] = pCreateJobObjectW(NULL, NULL);
+ ok(!!jobs[1], "CreateJobObjectA error %u\n", GetLastError());
+
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1,
+ sizeof(*jobs), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT,
+ NULL, NULL, (STARTUPINFOA *)&si, &pi);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[1], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[0], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+
+ ret = pQueryInformationJobObject(jobs[0], JobObjectBasicAccountingInformation, &job_info,
+ sizeof(job_info), NULL);
+ ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+ ok(!job_info.TotalProcesses, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses);
+ ok(!job_info.ActiveProcesses, "Got unexpected ActiveProcesses %u.\n", job_info.ActiveProcesses);
+
+ ret = pQueryInformationJobObject(jobs[1], JobObjectBasicAccountingInformation, &job_info,
+ sizeof(job_info), NULL);
+ ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+ ok(job_info.TotalProcesses == 1, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses);
+ ok(!job_info.ActiveProcesses || job_info.ActiveProcesses == 1, "Got unexpected ActiveProcesses %u.\n",
+ job_info.ActiveProcesses);
+
+ /* Fails due to the second job already has the parent other than the first job in the list. */
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs,
+ 2 * sizeof(*jobs), NULL, NULL);
+
+ port = pCreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
+ ok(!!port, "CreateIoCompletionPort error %u\n", GetLastError());
+
+ port_info.CompletionPort = port;
+ port_info.CompletionKey = jobs[0];
+ ret = pSetInformationJobObject(jobs[0], JobObjectAssociateCompletionPortInformation, &port_info, sizeof(port_info));
+ ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+ ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 0);
+ ok(!ret, "GetQueuedCompletionStatus succeeded.\n");
+
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
+ (STARTUPINFOA *)&si, &pi);
+ ok(!ret && GetLastError() == ERROR_ACCESS_DENIED, "Got unexpected ret %#x, GetLastError() %u.\n",
+ ret, GetLastError());
+
+ ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 100);
+ ok(ret, "GetQueuedCompletionStatus: %x\n", GetLastError());
+ ok(key == JOB_OBJECT_MSG_NEW_PROCESS, "unexpected key %x\n", key);
+ ok((HANDLE)value == jobs[0], "unexpected value %p\n", (void *)value);
+ ok(!!overlapped, "Got zero pid.\n");
+
+ ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 100);
+ ok(ret, "GetQueuedCompletionStatus: %x\n", GetLastError());
+ ok(key == JOB_OBJECT_MSG_EXIT_PROCESS, "unexpected key %x\n", key);
+ ok((HANDLE)value == jobs[0], "unexpected value %p\n", (void *)value);
+ ok(!!overlapped, "Got zero pid.\n");
+
+ ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 100);
+ ok(ret, "GetQueuedCompletionStatus: %x\n", GetLastError());
+ ok(key == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, "unexpected key %x\n", key);
+ ok((HANDLE)value == jobs[0], "unexpected value %p\n", (void *)value);
+ ok(!overlapped, "Got unexpected overlapped %p.\n", overlapped);
+
+ ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 0);
+ ok(!ret, "GetQueuedCompletionStatus succeeded.\n");
+
+ CloseHandle(port);
+
+ /* The first job got updated even though the process creation failed. */
+ ret = pQueryInformationJobObject(jobs[0], JobObjectBasicAccountingInformation, &job_info,
+ sizeof(job_info), NULL);
+ ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+ ok(job_info.TotalProcesses == 1, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses);
+ ok(!job_info.ActiveProcesses, "Got unexpected ActiveProcesses %u.\n", job_info.ActiveProcesses);
+
+ ret = pQueryInformationJobObject(jobs[1], JobObjectBasicAccountingInformation, &job_info,
+ sizeof(job_info), NULL);
+ ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+ ok(job_info.TotalProcesses == 1, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses);
+ ok(!job_info.ActiveProcesses, "Got unexpected ActiveProcesses %u.\n", job_info.ActiveProcesses);
+
+ /* Check that the first job actually got the job_parent as parent. */
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs,
+ sizeof(*jobs), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT
+ | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, (STARTUPINFOA *)&si, &pi);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[0], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+
+ tmp = jobs[0];
+ jobs[0] = jobs[1];
+ jobs[1] = tmp;
+
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs,
+ 2 * sizeof(*jobs), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
+ (STARTUPINFOA *)&si, &pi);
+ ok(!ret && GetLastError() == ERROR_ACCESS_DENIED, "Got unexpected ret %#x, GetLastError() %u.\n",
+ ret, GetLastError());
+
+ CloseHandle(jobs[0]);
+ CloseHandle(jobs[1]);
+
+ jobs[0] = pCreateJobObjectW(NULL, NULL);
+ ok(!!jobs[0], "CreateJobObjectA error %u\n", GetLastError());
+ jobs[1] = pCreateJobObjectW(NULL, NULL);
+ ok(!!jobs[1], "CreateJobObjectA error %u\n", GetLastError());
+
+ /* Create the job chain successfully and check the job chain. */
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs,
+ 2 * sizeof(*jobs), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT,
+ NULL, NULL, (STARTUPINFOA *)&si, &pi);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[0], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[1], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1,
+ sizeof(*jobs), NULL, NULL);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT
+ | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, (STARTUPINFOA *)&si, &pi);
+ ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[0], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, jobs[1], &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+
+ CloseHandle(jobs[0]);
+ CloseHandle(jobs[1]);
+
pDeleteProcThreadAttributeList(attrs);
heap_free(attrs);
+
+ limit_info.BasicLimitInformation.LimitFlags = 0;
+ ret = pSetInformationJobObject(parent_job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+ ok(ret, "SetInformationJobObject error %u\n", GetLastError());
}
START_TEST(process)
@@ -4768,7 +5020,7 @@ START_TEST(process)
test_nested_jobs();
job = test_AddSelfToJob();
test_jobInheritance(job);
- test_job_list_attribute();
+ test_job_list_attribute(job);
test_BreakawayOk(job);
CloseHandle(job);
}
diff --git a/server/process.c b/server/process.c
index 35b0f9d20fb..e91a63bed08 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1276,6 +1276,18 @@ DECL_HANDLER(new_process)
job = job->parent;
}
+ for (i = 0; i < job_handle_count; ++i)
+ {
+ job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS );
+ add_job_process( job, process );
+ release_object( job );
+ if (get_error())
+ {
+ release_job_process( process );
+ goto done;
+ }
+ }
+
/* connect to the window station */
connect_process_winstation( process, parent_thread, parent );
--
2.31.1
More information about the wine-devel
mailing list