[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