Commit 21f5597d authored by Paul Gofman's avatar Paul Gofman Committed by Alexandre Julliard

server: Support nested jobs.

parent d7ce5bdd
...@@ -71,6 +71,7 @@ static BOOL (WINAPI *pQueryFullProcessImageNameA)(HANDLE hProcess, DWORD dwFla ...@@ -71,6 +71,7 @@ static BOOL (WINAPI *pQueryFullProcessImageNameA)(HANDLE hProcess, DWORD dwFla
static BOOL (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize); static BOOL (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize);
static DWORD (WINAPI *pK32GetProcessImageFileNameA)(HANDLE,LPSTR,DWORD); static DWORD (WINAPI *pK32GetProcessImageFileNameA)(HANDLE,LPSTR,DWORD);
static HANDLE (WINAPI *pCreateJobObjectW)(LPSECURITY_ATTRIBUTES sa, LPCWSTR name); static HANDLE (WINAPI *pCreateJobObjectW)(LPSECURITY_ATTRIBUTES sa, LPCWSTR name);
static HANDLE (WINAPI *pOpenJobObjectA)(DWORD access, BOOL inherit, LPCSTR name);
static BOOL (WINAPI *pAssignProcessToJobObject)(HANDLE job, HANDLE process); static BOOL (WINAPI *pAssignProcessToJobObject)(HANDLE job, HANDLE process);
static BOOL (WINAPI *pIsProcessInJob)(HANDLE process, HANDLE job, PBOOL result); static BOOL (WINAPI *pIsProcessInJob)(HANDLE process, HANDLE job, PBOOL result);
static BOOL (WINAPI *pTerminateJobObject)(HANDLE job, UINT exit_code); static BOOL (WINAPI *pTerminateJobObject)(HANDLE job, UINT exit_code);
...@@ -257,6 +258,7 @@ static BOOL init(void) ...@@ -257,6 +258,7 @@ static BOOL init(void)
pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW"); pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW");
pK32GetProcessImageFileNameA = (void *) GetProcAddress(hkernel32, "K32GetProcessImageFileNameA"); pK32GetProcessImageFileNameA = (void *) GetProcAddress(hkernel32, "K32GetProcessImageFileNameA");
pCreateJobObjectW = (void *)GetProcAddress(hkernel32, "CreateJobObjectW"); pCreateJobObjectW = (void *)GetProcAddress(hkernel32, "CreateJobObjectW");
pOpenJobObjectA = (void *)GetProcAddress(hkernel32, "OpenJobObjectA");
pAssignProcessToJobObject = (void *)GetProcAddress(hkernel32, "AssignProcessToJobObject"); pAssignProcessToJobObject = (void *)GetProcAddress(hkernel32, "AssignProcessToJobObject");
pIsProcessInJob = (void *)GetProcAddress(hkernel32, "IsProcessInJob"); pIsProcessInJob = (void *)GetProcAddress(hkernel32, "IsProcessInJob");
pTerminateJobObject = (void *)GetProcAddress(hkernel32, "TerminateJobObject"); pTerminateJobObject = (void *)GetProcAddress(hkernel32, "TerminateJobObject");
...@@ -2976,13 +2978,14 @@ static void test_jobInheritance(HANDLE job) ...@@ -2976,13 +2978,14 @@ static void test_jobInheritance(HANDLE job)
wait_and_close_child_process(&pi); wait_and_close_child_process(&pi);
} }
static void test_BreakawayOk(HANDLE job) static void test_BreakawayOk(HANDLE parent_job)
{ {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
STARTUPINFOA si = {0}; STARTUPINFOA si = {0};
char buffer[MAX_PATH + 23]; char buffer[MAX_PATH + 23];
BOOL ret, out; BOOL ret, out, nested_jobs;
HANDLE job;
if (!pIsProcessInJob) if (!pIsProcessInJob)
{ {
...@@ -2990,6 +2993,16 @@ static void test_BreakawayOk(HANDLE job) ...@@ -2990,6 +2993,16 @@ static void test_BreakawayOk(HANDLE job)
return; return;
} }
job = pCreateJobObjectW(NULL, NULL);
ok(!!job, "CreateJobObjectW error %u\n", GetLastError());
ret = pAssignProcessToJobObject(job, GetCurrentProcess());
ok(ret || broken(!ret && GetLastError() == ERROR_ACCESS_DENIED) /* before Win 8. */,
"AssignProcessToJobObject error %u\n", GetLastError());
nested_jobs = ret;
if (!ret)
win_skip("Nested jobs are not supported.\n");
sprintf(buffer, "\"%s\" process exit", selfname); sprintf(buffer, "\"%s\" process exit", selfname);
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi); ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
ok(!ret, "CreateProcessA expected failure\n"); ok(!ret, "CreateProcessA expected failure\n");
...@@ -3001,10 +3014,32 @@ static void test_BreakawayOk(HANDLE job) ...@@ -3001,10 +3014,32 @@ static void test_BreakawayOk(HANDLE job)
wait_and_close_child_process(&pi); wait_and_close_child_process(&pi);
} }
if (nested_jobs)
{
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK; limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info)); ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
ok(ret, "SetInformationJobObject error %u\n", GetLastError()); ok(ret, "SetInformationJobObject error %u\n", GetLastError());
sprintf(buffer, "\"%s\" process exit", selfname);
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
ok(ret, "CreateProcessA error %u\n", GetLastError());
ret = pIsProcessInJob(pi.hProcess, job, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(!out, "IsProcessInJob returned out=%u\n", out);
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);
}
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());
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi); ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
ok(ret, "CreateProcessA error %u\n", GetLastError()); ok(ret, "CreateProcessA error %u\n", GetLastError());
...@@ -3012,6 +3047,10 @@ static void test_BreakawayOk(HANDLE job) ...@@ -3012,6 +3047,10 @@ static void test_BreakawayOk(HANDLE job)
ok(ret, "IsProcessInJob error %u\n", GetLastError()); ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(!out, "IsProcessInJob returned out=%u\n", out); ok(!out, "IsProcessInJob returned out=%u\n", out);
ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(!out, "IsProcessInJob returned out=%u\n", out);
wait_and_close_child_process(&pi); wait_and_close_child_process(&pi);
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
...@@ -4309,6 +4348,135 @@ static void test_dead_process(void) ...@@ -4309,6 +4348,135 @@ static void test_dead_process(void)
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
} }
static void test_nested_jobs_child(unsigned int index)
{
HANDLE job, job_parent, job_other;
PROCESS_INFORMATION pi;
char job_name[32];
BOOL ret, out;
sprintf(job_name, "test_nested_jobs_%u", index);
job = pOpenJobObjectA(JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_QUERY
| JOB_OBJECT_TERMINATE, FALSE, job_name);
ok(!!job, "OpenJobObjectA error %u\n", GetLastError());
sprintf(job_name, "test_nested_jobs_%u", !index);
job_other = pOpenJobObjectA(JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_QUERY
| JOB_OBJECT_TERMINATE, FALSE, job_name);
ok(!!job_other, "OpenJobObjectA error %u\n", GetLastError());
job_parent = pCreateJobObjectW(NULL, NULL);
ok(!!job_parent, "CreateJobObjectA error %u\n", GetLastError());
ret = pAssignProcessToJobObject(job_parent, GetCurrentProcess());
ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
create_process("wait", &pi);
ret = pAssignProcessToJobObject(job_parent, pi.hProcess);
ok(ret || broken(!ret && GetLastError() == ERROR_ACCESS_DENIED) /* Supported since Windows 8. */,
"AssignProcessToJobObject error %u\n", GetLastError());
if (!ret)
{
win_skip("Nested jobs are not supported.\n");
goto done;
}
ret = pAssignProcessToJobObject(job, pi.hProcess);
ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
out = FALSE;
ret = pIsProcessInJob(pi.hProcess, NULL, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(out, "IsProcessInJob returned out=%u\n", out);
out = FALSE;
ret = pIsProcessInJob(pi.hProcess, job, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(out, "IsProcessInJob returned out=%u\n", out);
out = TRUE;
ret = pIsProcessInJob(GetCurrentProcess(), job, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(!out, "IsProcessInJob returned out=%u\n", out);
out = FALSE;
ret = pIsProcessInJob(pi.hProcess, job, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(out, "IsProcessInJob returned out=%u\n", out);
ret = pAssignProcessToJobObject(job, GetCurrentProcess());
ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
TerminateProcess(pi.hProcess, 0);
wait_child_process(pi.hProcess);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
create_process("wait", &pi);
out = FALSE;
ret = pIsProcessInJob(pi.hProcess, job, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(out, "IsProcessInJob returned out=%u\n", out);
out = FALSE;
ret = pIsProcessInJob(pi.hProcess, job_parent, &out);
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(out, "IsProcessInJob returned out=%u\n", out);
if (index)
{
ret = pAssignProcessToJobObject(job_other, GetCurrentProcess());
ok(!ret, "AssignProcessToJobObject succeded\n");
ok(GetLastError() == ERROR_ACCESS_DENIED, "Got unexpected error %u.\n", GetLastError());
}
done:
TerminateProcess(pi.hProcess, 0);
wait_child_process(pi.hProcess);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(job_parent);
CloseHandle(job);
CloseHandle(job_other);
}
static void test_nested_jobs(void)
{
PROCESS_INFORMATION info[2];
char buffer[MAX_PATH + 26];
STARTUPINFOA si = {0};
HANDLE job1, job2;
unsigned int i;
if (!pIsProcessInJob)
{
win_skip("IsProcessInJob not available.\n");
return;
}
job1 = pCreateJobObjectW(NULL, L"test_nested_jobs_0");
ok(!!job1, "CreateJobObjectW failed, error %u.\n", GetLastError());
job2 = pCreateJobObjectW(NULL, L"test_nested_jobs_1");
ok(!!job2, "CreateJobObjectW failed, error %u.\n", GetLastError());
sprintf(buffer, "\"%s\" process nested_jobs 0", selfname);
ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info[0]),
"CreateProcess failed\n");
wait_child_process(info[0].hProcess);
sprintf(buffer, "\"%s\" process nested_jobs 1", selfname);
ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info[1]),
"CreateProcess failed\n");
wait_child_process(info[1].hProcess);
for (i = 0; i < 2; ++i)
{
CloseHandle(info[i].hProcess);
CloseHandle(info[i].hThread);
}
CloseHandle(job1);
CloseHandle(job2);
}
START_TEST(process) START_TEST(process)
{ {
HANDLE job, hproc, h, h2; HANDLE job, hproc, h, h2;
...@@ -4384,6 +4552,11 @@ START_TEST(process) ...@@ -4384,6 +4552,11 @@ START_TEST(process)
test_handle_list_attribute(TRUE, h, h2); test_handle_list_attribute(TRUE, h, h2);
return; return;
} }
else if (!strcmp(myARGV[2], "nested_jobs") && myARGC >= 4)
{
test_nested_jobs_child(atoi(myARGV[3]));
return;
}
ok(0, "Unexpected command %s\n", myARGV[2]); ok(0, "Unexpected command %s\n", myARGV[2]);
return; return;
...@@ -4452,6 +4625,7 @@ START_TEST(process) ...@@ -4452,6 +4625,7 @@ START_TEST(process)
test_CompletionPort(); test_CompletionPort();
test_KillOnJobClose(); test_KillOnJobClose();
test_WaitForJobObject(); test_WaitForJobObject();
test_nested_jobs();
job = test_AddSelfToJob(); job = test_AddSelfToJob();
test_jobInheritance(job); test_jobInheritance(job);
test_BreakawayOk(job); test_BreakawayOk(job);
......
...@@ -182,7 +182,7 @@ static void job_destroy( struct object *obj ); ...@@ -182,7 +182,7 @@ static void job_destroy( struct object *obj );
struct job struct job
{ {
struct object obj; /* object header */ struct object obj; /* object header */
struct list process_list; /* list of all processes */ struct list process_list; /* list of processes */
int num_processes; /* count of running processes */ int num_processes; /* count of running processes */
int total_processes; /* count of processes which have been assigned */ int total_processes; /* count of processes which have been assigned */
unsigned int limit_flags; /* limit flags */ unsigned int limit_flags; /* limit flags */
...@@ -190,6 +190,9 @@ struct job ...@@ -190,6 +190,9 @@ struct job
int signaled; /* job is signaled */ int signaled; /* job is signaled */
struct completion *completion_port; /* associated completion port */ struct completion *completion_port; /* associated completion port */
apc_param_t completion_key; /* key to send with completion messages */ apc_param_t completion_key; /* key to send with completion messages */
struct job *parent;
struct list parent_job_entry; /* list entry for parent job */
struct list child_job_list; /* list of child jobs */
}; };
static const struct object_ops job_ops = static const struct object_ops job_ops =
...@@ -227,6 +230,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ ...@@ -227,6 +230,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
{ {
/* initialize it if it didn't already exist */ /* initialize it if it didn't already exist */
list_init( &job->process_list ); list_init( &job->process_list );
list_init( &job->child_job_list );
job->num_processes = 0; job->num_processes = 0;
job->total_processes = 0; job->total_processes = 0;
job->limit_flags = 0; job->limit_flags = 0;
...@@ -234,6 +238,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ ...@@ -234,6 +238,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
job->signaled = 0; job->signaled = 0;
job->completion_port = NULL; job->completion_port = NULL;
job->completion_key = 0; job->completion_key = 0;
job->parent = NULL;
} }
} }
return job; return job;
...@@ -250,14 +255,59 @@ static void add_job_completion( struct job *job, apc_param_t msg, apc_param_t pi ...@@ -250,14 +255,59 @@ static void add_job_completion( struct job *job, apc_param_t msg, apc_param_t pi
add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg ); add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg );
} }
static int process_in_job( struct job *job, struct process *process )
{
struct job *j;
LIST_FOR_EACH_ENTRY( j, &job->child_job_list, struct job, parent_job_entry )
{
assert( j->parent == job );
if (process_in_job( j, process )) return 1;
}
return process->job == job;
}
static void add_job_process( struct job *job, struct process *process ) static void add_job_process( struct job *job, struct process *process )
{ {
struct job *j, *common_parent;
process_id_t pid;
if (job == process->job) return;
if ((common_parent = process->job))
{
if (job->parent)
{
for (j = job->parent; j; j = j->parent)
if (j == common_parent) break;
if (j != common_parent)
{
/* Job already has parent and the process is not in the job's chain. */
set_error( STATUS_ACCESS_DENIED );
return;
}
/* process->job is referenced in the job->parent chain. */
release_object( process->job );
}
else
{
/* transfer reference. */
job->parent = process->job;
list_add_tail( &job->parent->child_job_list, &job->parent_job_entry );
}
list_remove( &process->job_entry );
}
process->job = (struct job *)grab_object( job ); process->job = (struct job *)grab_object( job );
list_add_tail( &job->process_list, &process->job_entry ); list_add_tail( &job->process_list, &process->job_entry );
job->num_processes++;
job->total_processes++;
add_job_completion( job, JOB_OBJECT_MSG_NEW_PROCESS, get_process_id(process) ); pid = get_process_id( process );
for (j = job; j != common_parent; j = j->parent)
{
j->num_processes++;
j->total_processes++;
add_job_completion( j, JOB_OBJECT_MSG_NEW_PROCESS, pid );
}
} }
/* called when a process has terminated, allow one additional process */ /* called when a process has terminated, allow one additional process */
...@@ -265,8 +315,8 @@ static void release_job_process( struct process *process ) ...@@ -265,8 +315,8 @@ static void release_job_process( struct process *process )
{ {
struct job *job = process->job; struct job *job = process->job;
if (!job) return; while (job)
{
assert( job->num_processes ); assert( job->num_processes );
job->num_processes--; job->num_processes--;
...@@ -275,27 +325,31 @@ static void release_job_process( struct process *process ) ...@@ -275,27 +325,31 @@ static void release_job_process( struct process *process )
if (!job->num_processes) if (!job->num_processes)
add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 ); add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 );
job = job->parent;
}
} }
static void terminate_job( struct job *job, int exit_code ) static void terminate_job( struct job *job, int exit_code )
{ {
/* don't report completion events for terminated processes */ struct process *process, *next_process;
job->terminating = 1; struct job *j, *next_job;
for (;;) /* restart from the beginning of the list every time */ LIST_FOR_EACH_ENTRY_SAFE( j, next_job, &job->child_job_list, struct job, parent_job_entry )
{ {
struct process *process; assert( j->parent == job );
/* find the first process associated with this job and still running */ grab_object( j );
LIST_FOR_EACH_ENTRY( process, &job->process_list, struct process, job_entry ) terminate_job( j, exit_code );
{ release_object( j );
if (process->running_threads) break;
} }
if (&process->job_entry == &job->process_list) break; /* no process found */
job->terminating = 1;
LIST_FOR_EACH_ENTRY_SAFE( process, next_process, &job->process_list, struct process, job_entry )
{
assert( process->job == job ); assert( process->job == job );
terminate_process( process, NULL, exit_code ); if (process->running_threads) terminate_process( process, NULL, exit_code );
} }
job->terminating = 0; job->terminating = 0;
job->signaled = 1; job->signaled = 1;
wake_up( &job->obj, 0 ); wake_up( &job->obj, 0 );
...@@ -320,16 +374,23 @@ static void job_destroy( struct object *obj ) ...@@ -320,16 +374,23 @@ static void job_destroy( struct object *obj )
assert( obj->ops == &job_ops ); assert( obj->ops == &job_ops );
assert( !job->num_processes ); assert( !job->num_processes );
assert( list_empty(&job->process_list) ); assert( list_empty( &job->process_list ));
assert( list_empty( &job->child_job_list ));
if (job->completion_port) release_object( job->completion_port ); if (job->completion_port) release_object( job->completion_port );
if (job->parent)
{
list_remove( &job->parent_job_entry );
release_object( job->parent );
}
} }
static void job_dump( struct object *obj, int verbose ) static void job_dump( struct object *obj, int verbose )
{ {
struct job *job = (struct job *)obj; struct job *job = (struct job *)obj;
assert( obj->ops == &job_ops ); assert( obj->ops == &job_ops );
fprintf( stderr, "Job processes=%d\n", list_count(&job->process_list) ); fprintf( stderr, "Job processes=%d child_jobs=%d parent=%p\n",
list_count(&job->process_list), list_count(&job->child_job_list), job->parent );
} }
static int job_signaled( struct object *obj, struct wait_queue_entry *entry ) static int job_signaled( struct object *obj, struct wait_queue_entry *entry )
...@@ -1026,6 +1087,7 @@ DECL_HANDLER(new_process) ...@@ -1026,6 +1087,7 @@ DECL_HANDLER(new_process)
struct thread *parent_thread = current; struct thread *parent_thread = current;
int socket_fd = thread_get_inflight_fd( current, req->socket_fd ); int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
const obj_handle_t *handles = NULL; const obj_handle_t *handles = NULL;
struct job *job;
if (socket_fd == -1) if (socket_fd == -1)
{ {
...@@ -1062,6 +1124,8 @@ DECL_HANDLER(new_process) ...@@ -1062,6 +1124,8 @@ DECL_HANDLER(new_process)
} }
else parent = (struct process *)grab_object( current->process ); else parent = (struct process *)grab_object( current->process );
/* If a job further in the job chain does not permit breakaway process creation
* succeeds and the process which is trying to breakaway is assigned to that job. */
if (parent->job && (req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY) && if (parent->job && (req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY) &&
!(parent->job->limit_flags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))) !(parent->job->limit_flags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
{ {
...@@ -1152,11 +1216,18 @@ DECL_HANDLER(new_process) ...@@ -1152,11 +1216,18 @@ DECL_HANDLER(new_process)
process->startup_info = (struct startup_info *)grab_object( info ); process->startup_info = (struct startup_info *)grab_object( info );
if (parent->job job = parent->job;
&& !(req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY) while (job)
&& !(parent->job->limit_flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))
{ {
add_job_process( parent->job, process ); if (!(job->limit_flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)
&& !(req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY
&& job->limit_flags & JOB_OBJECT_LIMIT_BREAKAWAY_OK))
{
add_job_process( job, process );
assert( !get_error() );
break;
}
job = job->parent;
} }
/* connect to the window station */ /* connect to the window station */
...@@ -1570,12 +1641,8 @@ DECL_HANDLER(assign_job) ...@@ -1570,12 +1641,8 @@ DECL_HANDLER(assign_job)
if ((process = get_process_from_handle( req->process, PROCESS_SET_QUOTA | PROCESS_TERMINATE ))) if ((process = get_process_from_handle( req->process, PROCESS_SET_QUOTA | PROCESS_TERMINATE )))
{ {
if (!process->running_threads) if (!process->running_threads) set_error( STATUS_PROCESS_IS_TERMINATING );
set_error( STATUS_PROCESS_IS_TERMINATING ); else add_job_process( job, process );
else if (process->job)
set_error( STATUS_ACCESS_DENIED );
else
add_job_process( job, process );
release_object( process ); release_object( process );
} }
release_object( job ); release_object( job );
...@@ -1597,7 +1664,7 @@ DECL_HANDLER(process_in_job) ...@@ -1597,7 +1664,7 @@ DECL_HANDLER(process_in_job)
} }
else if ((job = get_job_obj( current->process, req->job, JOB_OBJECT_QUERY ))) else if ((job = get_job_obj( current->process, req->job, JOB_OBJECT_QUERY )))
{ {
set_error( process->job == job ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB ); set_error( process_in_job( job, process ) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB );
release_object( job ); release_object( job );
} }
release_object( process ); release_object( process );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment