Module: wine Branch: master Commit: 1d359a5827159fb0572d598ed02054ff3d700f44 URL: http://source.winehq.org/git/wine.git/?a=commit;h=1d359a5827159fb0572d598ed0...
Author: Andrew Cook ariscop@gmail.com Date: Fri Apr 3 17:19:45 2015 +1100
server: Implement TerminateJobObject.
---
dlls/kernel32/tests/process.c | 2 -- dlls/ntdll/sync.c | 15 +++++++++++++-- include/wine/server_protocol.h | 19 ++++++++++++++++++- server/process.c | 39 ++++++++++++++++++++++++++++++++++++++- server/process.h | 1 + server/protocol.def | 7 +++++++ server/request.h | 5 +++++ server/trace.c | 9 +++++++++ 8 files changed, 91 insertions(+), 6 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 11c9253..d981ec9 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -2255,13 +2255,11 @@ static void test_TerminateJobObject(void) ok(ret, "TerminateJobObject error %u\n", GetLastError());
dwret = WaitForSingleObject(pi.hProcess, 1000); - 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);
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 5db736e..04c6de30 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -613,8 +613,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; }
/****************************************************************************** diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index de6302c..d27914a 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5156,6 +5156,20 @@ struct set_job_completion_port_reply };
+ +struct terminate_job_request +{ + struct request_header __header; + obj_handle_t handle; + int status; + char __pad_20[4]; +}; +struct terminate_job_reply +{ + struct reply_header __header; +}; + + enum request { REQ_new_process, @@ -5419,6 +5433,7 @@ enum request REQ_process_in_job, REQ_set_job_limits, REQ_set_job_completion_port, + REQ_terminate_job, REQ_NB_REQUESTS };
@@ -5687,6 +5702,7 @@ union generic_request struct process_in_job_request process_in_job_request; struct set_job_limits_request set_job_limits_request; struct set_job_completion_port_request set_job_completion_port_request; + struct terminate_job_request terminate_job_request; }; union generic_reply { @@ -5953,8 +5969,9 @@ union generic_reply struct process_in_job_reply process_in_job_reply; struct set_job_limits_reply set_job_limits_reply; struct set_job_completion_port_reply set_job_completion_port_reply; + struct terminate_job_reply terminate_job_reply; };
-#define SERVER_PROTOCOL_VERSION 466 +#define SERVER_PROTOCOL_VERSION 467
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/process.c b/server/process.c index eba47f7..d912739 100644 --- a/server/process.c +++ b/server/process.c @@ -64,6 +64,7 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry static unsigned int process_map_access( struct object *obj, unsigned int access ); 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 = { @@ -147,6 +148,7 @@ struct job struct list process_list; /* list of all processes */ int num_processes; /* count of running processes */ unsigned int limit_flags; /* limit flags */ + int terminating; /* job is terminating */ struct completion *completion_port; /* associated completion port */ apc_param_t completion_key; /* key to send with completion messages */ }; @@ -188,6 +190,7 @@ static struct job *create_job_object( struct directory *root, const struct unico list_init( &job->process_list ); job->num_processes = 0; job->limit_flags = 0; + job->terminating = 0; job->completion_port = NULL; job->completion_key = 0; } @@ -251,12 +254,35 @@ static void release_job_process( struct process *process ) assert( job->num_processes ); job->num_processes--;
- 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 ); }
+static void terminate_job( struct job *job, int exit_code ) +{ + /* don't report completion events for terminated processes */ + job->terminating = 1; + + for (;;) /* restart from the beginning of the list every time */ + { + struct process *process; + + /* 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 ); + } + + job->terminating = 0; +} + static void job_destroy( struct object *obj ) { struct job *job = (struct job *)obj; @@ -1543,6 +1569,17 @@ DECL_HANDLER(process_in_job) release_object( process ); }
+/* terminate all processes associated with the job */ +DECL_HANDLER(terminate_job) +{ + struct job *job = get_job_obj( current->process, req->handle, JOB_OBJECT_TERMINATE ); + + if (!job) return; + + terminate_job( job, req->status ); + release_object( job ); +} + /* update limits of the job object */ DECL_HANDLER(set_job_limits) { diff --git a/server/process.h b/server/process.h index 58c313a..59625f0 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 }; diff --git a/server/protocol.def b/server/protocol.def index 625b3c9..54501e8 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3544,3 +3544,10 @@ enum coords_relative obj_handle_t port; /* handle to the completion port */ client_ptr_t key; /* key to send with completion messages */ @END + + +/* Terminate all processes associated with the job */ +@REQ(terminate_job) + obj_handle_t handle; /* handle to the job */ + int status; /* process exit code */ +@END diff --git a/server/request.h b/server/request.h index dd6e4e2..f94235f 100644 --- a/server/request.h +++ b/server/request.h @@ -367,6 +367,7 @@ DECL_HANDLER(assign_job); DECL_HANDLER(process_in_job); DECL_HANDLER(set_job_limits); DECL_HANDLER(set_job_completion_port); +DECL_HANDLER(terminate_job);
#ifdef WANT_REQUEST_HANDLERS
@@ -634,6 +635,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_process_in_job, (req_handler)req_set_job_limits, (req_handler)req_set_job_completion_port, + (req_handler)req_terminate_job, };
C_ASSERT( sizeof(affinity_t) == 8 ); @@ -2233,6 +2235,9 @@ C_ASSERT( FIELD_OFFSET(struct set_job_completion_port_request, job) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_job_completion_port_request, port) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_job_completion_port_request, key) == 24 ); C_ASSERT( sizeof(struct set_job_completion_port_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct terminate_job_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct terminate_job_request, status) == 16 ); +C_ASSERT( sizeof(struct terminate_job_request) == 24 );
#endif /* WANT_REQUEST_HANDLERS */
diff --git a/server/trace.c b/server/trace.c index 0e2e7e2..236c9ab 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4133,6 +4133,12 @@ static void dump_set_job_completion_port_request( const struct set_job_completio dump_uint64( ", key=", &req->key ); }
+static void dump_terminate_job_request( const struct terminate_job_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", status=%d", req->status ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -4395,6 +4401,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_process_in_job_request, (dump_func)dump_set_job_limits_request, (dump_func)dump_set_job_completion_port_request, + (dump_func)dump_terminate_job_request, };
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -4659,6 +4666,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, NULL, + NULL, };
static const char * const req_names[REQ_NB_REQUESTS] = { @@ -4923,6 +4931,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "process_in_job", "set_job_limits", "set_job_completion_port", + "terminate_job", };
static const struct