server: Support nested jobs.
Signed-off-by: Paul Gofman <pgofman@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
d7ce5bddf9
commit
21f5597de4
|
@ -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 DWORD (WINAPI *pK32GetProcessImageFileNameA)(HANDLE,LPSTR,DWORD);
|
||||
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 *pIsProcessInJob)(HANDLE process, HANDLE job, PBOOL result);
|
||||
static BOOL (WINAPI *pTerminateJobObject)(HANDLE job, UINT exit_code);
|
||||
|
@ -257,6 +258,7 @@ static BOOL init(void)
|
|||
pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW");
|
||||
pK32GetProcessImageFileNameA = (void *) GetProcAddress(hkernel32, "K32GetProcessImageFileNameA");
|
||||
pCreateJobObjectW = (void *)GetProcAddress(hkernel32, "CreateJobObjectW");
|
||||
pOpenJobObjectA = (void *)GetProcAddress(hkernel32, "OpenJobObjectA");
|
||||
pAssignProcessToJobObject = (void *)GetProcAddress(hkernel32, "AssignProcessToJobObject");
|
||||
pIsProcessInJob = (void *)GetProcAddress(hkernel32, "IsProcessInJob");
|
||||
pTerminateJobObject = (void *)GetProcAddress(hkernel32, "TerminateJobObject");
|
||||
|
@ -2976,13 +2978,14 @@ static void test_jobInheritance(HANDLE job)
|
|||
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;
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFOA si = {0};
|
||||
char buffer[MAX_PATH + 23];
|
||||
BOOL ret, out;
|
||||
BOOL ret, out, nested_jobs;
|
||||
HANDLE job;
|
||||
|
||||
if (!pIsProcessInJob)
|
||||
{
|
||||
|
@ -2990,6 +2993,16 @@ static void test_BreakawayOk(HANDLE job)
|
|||
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);
|
||||
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
|
||||
ok(!ret, "CreateProcessA expected failure\n");
|
||||
|
@ -3001,8 +3014,30 @@ static void test_BreakawayOk(HANDLE job)
|
|||
wait_and_close_child_process(&pi);
|
||||
}
|
||||
|
||||
if (nested_jobs)
|
||||
{
|
||||
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
|
||||
ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
|
||||
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(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
|
||||
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);
|
||||
|
@ -3012,6 +3047,10 @@ static void test_BreakawayOk(HANDLE job)
|
|||
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);
|
||||
|
||||
wait_and_close_child_process(&pi);
|
||||
|
||||
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
|
||||
|
@ -4309,6 +4348,135 @@ static void test_dead_process(void)
|
|||
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)
|
||||
{
|
||||
HANDLE job, hproc, h, h2;
|
||||
|
@ -4384,6 +4552,11 @@ START_TEST(process)
|
|||
test_handle_list_attribute(TRUE, h, h2);
|
||||
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]);
|
||||
return;
|
||||
|
@ -4452,6 +4625,7 @@ START_TEST(process)
|
|||
test_CompletionPort();
|
||||
test_KillOnJobClose();
|
||||
test_WaitForJobObject();
|
||||
test_nested_jobs();
|
||||
job = test_AddSelfToJob();
|
||||
test_jobInheritance(job);
|
||||
test_BreakawayOk(job);
|
||||
|
|
139
server/process.c
139
server/process.c
|
@ -182,7 +182,7 @@ static void job_destroy( struct object *obj );
|
|||
struct job
|
||||
{
|
||||
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 total_processes; /* count of processes which have been assigned */
|
||||
unsigned int limit_flags; /* limit flags */
|
||||
|
@ -190,6 +190,9 @@ struct job
|
|||
int signaled; /* job is signaled */
|
||||
struct completion *completion_port; /* associated completion port */
|
||||
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 =
|
||||
|
@ -227,6 +230,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
|
|||
{
|
||||
/* initialize it if it didn't already exist */
|
||||
list_init( &job->process_list );
|
||||
list_init( &job->child_job_list );
|
||||
job->num_processes = 0;
|
||||
job->total_processes = 0;
|
||||
job->limit_flags = 0;
|
||||
|
@ -234,6 +238,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
|
|||
job->signaled = 0;
|
||||
job->completion_port = NULL;
|
||||
job->completion_key = 0;
|
||||
job->parent = NULL;
|
||||
}
|
||||
}
|
||||
return job;
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
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 */
|
||||
|
@ -265,37 +315,41 @@ static void release_job_process( struct process *process )
|
|||
{
|
||||
struct job *job = process->job;
|
||||
|
||||
if (!job) return;
|
||||
while (job)
|
||||
{
|
||||
assert( job->num_processes );
|
||||
job->num_processes--;
|
||||
|
||||
assert( job->num_processes );
|
||||
job->num_processes--;
|
||||
if (!job->terminating)
|
||||
add_job_completion( job, JOB_OBJECT_MSG_EXIT_PROCESS, get_process_id(process) );
|
||||
|
||||
if (!job->terminating)
|
||||
add_job_completion( job, JOB_OBJECT_MSG_EXIT_PROCESS, get_process_id(process) );
|
||||
if (!job->num_processes)
|
||||
add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 );
|
||||
|
||||
if (!job->num_processes)
|
||||
add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 );
|
||||
job = job->parent;
|
||||
}
|
||||
}
|
||||
|
||||
static void terminate_job( struct job *job, int exit_code )
|
||||
{
|
||||
/* don't report completion events for terminated processes */
|
||||
job->terminating = 1;
|
||||
struct process *process, *next_process;
|
||||
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 */
|
||||
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 );
|
||||
grab_object( j );
|
||||
terminate_job( j, exit_code );
|
||||
release_object( j );
|
||||
}
|
||||
|
||||
job->terminating = 1;
|
||||
LIST_FOR_EACH_ENTRY_SAFE( process, next_process, &job->process_list, struct process, job_entry )
|
||||
{
|
||||
assert( process->job == job );
|
||||
if (process->running_threads) terminate_process( process, NULL, exit_code );
|
||||
}
|
||||
job->terminating = 0;
|
||||
job->signaled = 1;
|
||||
wake_up( &job->obj, 0 );
|
||||
|
@ -320,16 +374,23 @@ static void job_destroy( struct object *obj )
|
|||
assert( obj->ops == &job_ops );
|
||||
|
||||
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->parent)
|
||||
{
|
||||
list_remove( &job->parent_job_entry );
|
||||
release_object( job->parent );
|
||||
}
|
||||
}
|
||||
|
||||
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) );
|
||||
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 )
|
||||
|
@ -1026,6 +1087,7 @@ DECL_HANDLER(new_process)
|
|||
struct thread *parent_thread = current;
|
||||
int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
|
||||
const obj_handle_t *handles = NULL;
|
||||
struct job *job;
|
||||
|
||||
if (socket_fd == -1)
|
||||
{
|
||||
|
@ -1062,6 +1124,8 @@ DECL_HANDLER(new_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) &&
|
||||
!(parent->job->limit_flags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
|
||||
{
|
||||
|
@ -1152,11 +1216,18 @@ DECL_HANDLER(new_process)
|
|||
|
||||
process->startup_info = (struct startup_info *)grab_object( info );
|
||||
|
||||
if (parent->job
|
||||
&& !(req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY)
|
||||
&& !(parent->job->limit_flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))
|
||||
job = parent->job;
|
||||
while (job)
|
||||
{
|
||||
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 */
|
||||
|
@ -1570,12 +1641,8 @@ DECL_HANDLER(assign_job)
|
|||
|
||||
if ((process = get_process_from_handle( req->process, PROCESS_SET_QUOTA | PROCESS_TERMINATE )))
|
||||
{
|
||||
if (!process->running_threads)
|
||||
set_error( STATUS_PROCESS_IS_TERMINATING );
|
||||
else if (process->job)
|
||||
set_error( STATUS_ACCESS_DENIED );
|
||||
else
|
||||
add_job_process( job, process );
|
||||
if (!process->running_threads) set_error( STATUS_PROCESS_IS_TERMINATING );
|
||||
else add_job_process( job, process );
|
||||
release_object( process );
|
||||
}
|
||||
release_object( job );
|
||||
|
@ -1597,7 +1664,7 @@ DECL_HANDLER(process_in_job)
|
|||
}
|
||||
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( process );
|
||||
|
|
Loading…
Reference in New Issue