| /* |
| * Server-side request handling |
| * |
| * Copyright (C) 1998 Alexandre Julliard |
| */ |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| #include <unistd.h> |
| |
| #include "winerror.h" |
| #include "winnt.h" |
| #include "winbase.h" |
| #define WANT_REQUEST_HANDLERS |
| #include "server.h" |
| #include "server/request.h" |
| #include "server/thread.h" |
| |
| /* check that the string is NULL-terminated and that the len is correct */ |
| #define CHECK_STRING(func,str,len) \ |
| do { if (((str)[(len)-1] || strlen(str) != (len)-1)) \ |
| fatal_protocol_error( "%s: invalid string '.*s'\n", (func), (len), (str) ); \ |
| } while(0) |
| |
| struct thread *current = NULL; /* thread handling the current request */ |
| |
| /* complain about a protocol error and terminate the client connection */ |
| static void fatal_protocol_error( const char *err, ... ) |
| { |
| va_list args; |
| |
| va_start( args, err ); |
| fprintf( stderr, "Protocol error:%p: ", current ); |
| vfprintf( stderr, err, args ); |
| va_end( args ); |
| remove_client( current->client_fd, -2 ); |
| } |
| |
| /* call a request handler */ |
| void call_req_handler( struct thread *thread, enum request req, |
| void *data, int len, int fd ) |
| { |
| const struct handler *handler = &req_handlers[req]; |
| char *ptr; |
| |
| current = thread; |
| if ((req < 0) || (req >= REQ_NB_REQUESTS)) |
| { |
| fatal_protocol_error( "unknown request %d\n", req ); |
| return; |
| } |
| |
| if (len < handler->min_size) |
| { |
| fatal_protocol_error( "req %d bad length %d < %d)\n", req, len, handler->min_size ); |
| return; |
| } |
| |
| /* now call the handler */ |
| if (current) |
| { |
| CLEAR_ERROR(); |
| if (debug_level) trace_request( req, data, len, fd ); |
| } |
| len -= handler->min_size; |
| ptr = (char *)data + handler->min_size; |
| handler->handler( data, ptr, len, fd ); |
| current = NULL; |
| } |
| |
| /* handle a client timeout (unused for now) */ |
| void call_timeout_handler( struct thread *thread ) |
| { |
| current = thread; |
| if (debug_level) trace_timeout(); |
| CLEAR_ERROR(); |
| thread_timeout(); |
| current = NULL; |
| } |
| |
| /* a thread has been killed */ |
| void call_kill_handler( struct thread *thread, int exit_code ) |
| { |
| /* must be reentrant WRT call_req_handler */ |
| struct thread *old_current = current; |
| current = thread; |
| if (current) |
| { |
| if (debug_level) trace_kill( exit_code ); |
| thread_killed( current, exit_code ); |
| } |
| current = (old_current != thread) ? old_current : NULL; |
| } |
| |
| |
| /* create a new thread */ |
| DECL_HANDLER(new_thread) |
| { |
| struct new_thread_reply reply; |
| struct thread *new_thread; |
| int new_fd, err; |
| |
| if ((new_fd = dup(fd)) == -1) |
| { |
| new_thread = NULL; |
| err = ERROR_TOO_MANY_OPEN_FILES; |
| goto done; |
| } |
| if (!(new_thread = create_thread( new_fd, req->pid, &reply.thandle, |
| &reply.phandle ))) |
| { |
| close( new_fd ); |
| err = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| reply.tid = new_thread; |
| reply.pid = new_thread->process; |
| err = ERROR_SUCCESS; |
| |
| done: |
| if (!current) |
| { |
| /* first client doesn't have a current */ |
| struct iovec vec = { &reply, sizeof(reply) }; |
| send_reply_v( get_initial_client_fd(), err, -1, &vec, 1 ); |
| } |
| else |
| { |
| SET_ERROR( err ); |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| } |
| |
| /* create a new thread */ |
| DECL_HANDLER(init_thread) |
| { |
| if (current->state != STARTING) |
| { |
| fatal_protocol_error( "init_thread: already running\n" ); |
| return; |
| } |
| current->state = RUNNING; |
| current->unix_pid = req->unix_pid; |
| if (!(current->name = mem_alloc( len + 1 ))) goto done; |
| memcpy( current->name, data, len ); |
| current->name[len] = '\0'; |
| CLEAR_ERROR(); |
| done: |
| send_reply( current, -1, 0 ); |
| } |
| |
| /* set the debug level */ |
| DECL_HANDLER(set_debug) |
| { |
| debug_level = req->level; |
| CLEAR_ERROR(); |
| send_reply( current, -1, 0 ); |
| } |
| |
| /* terminate a process */ |
| DECL_HANDLER(terminate_process) |
| { |
| struct process *process; |
| |
| if ((process = get_process_from_handle( req->handle, PROCESS_TERMINATE ))) |
| { |
| kill_process( process, req->exit_code ); |
| release_object( process ); |
| } |
| if (current) send_reply( current, -1, 0 ); |
| } |
| |
| /* terminate a thread */ |
| DECL_HANDLER(terminate_thread) |
| { |
| struct thread *thread; |
| |
| if ((thread = get_thread_from_handle( req->handle, THREAD_TERMINATE ))) |
| { |
| kill_thread( thread, req->exit_code ); |
| release_object( thread ); |
| } |
| if (current) send_reply( current, -1, 0 ); |
| } |
| |
| /* close a handle */ |
| DECL_HANDLER(close_handle) |
| { |
| close_handle( current->process, req->handle ); |
| send_reply( current, -1, 0 ); |
| } |
| |
| /* duplicate a handle */ |
| DECL_HANDLER(dup_handle) |
| { |
| struct dup_handle_reply reply = { -1 }; |
| struct process *src, *dst; |
| |
| if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE ))) |
| { |
| if ((dst = get_process_from_handle( req->dst_process, PROCESS_DUP_HANDLE ))) |
| { |
| reply.handle = duplicate_handle( src, req->src_handle, dst, req->dst_handle, |
| req->access, req->inherit, req->options ); |
| release_object( dst ); |
| } |
| /* close the handle no matter what happened */ |
| if (req->options & DUPLICATE_CLOSE_SOURCE) |
| close_handle( src, req->src_handle ); |
| release_object( src ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* fetch information about a process */ |
| DECL_HANDLER(get_process_info) |
| { |
| struct process *process; |
| struct get_process_info_reply reply = { 0, 0 }; |
| |
| if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION ))) |
| { |
| get_process_info( process, &reply ); |
| release_object( process ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* fetch information about a thread */ |
| DECL_HANDLER(get_thread_info) |
| { |
| struct thread *thread; |
| struct get_thread_info_reply reply = { 0, 0 }; |
| |
| if ((thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION ))) |
| { |
| get_thread_info( thread, &reply ); |
| release_object( thread ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* open a handle to a process */ |
| DECL_HANDLER(open_process) |
| { |
| struct open_process_reply reply = { -1 }; |
| struct process *process = get_process_from_id( req->pid ); |
| if (process) |
| { |
| reply.handle = alloc_handle( current->process, process, |
| req->access, req->inherit ); |
| release_object( process ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* select on a handle list */ |
| DECL_HANDLER(select) |
| { |
| if (len != req->count * sizeof(int)) |
| fatal_protocol_error( "select: bad length %d for %d handles\n", |
| len, req->count ); |
| sleep_on( current, req->count, (int *)data, req->flags, req->timeout ); |
| } |
| |
| /* create an event */ |
| DECL_HANDLER(create_event) |
| { |
| struct create_event_reply reply = { -1 }; |
| struct object *obj; |
| char *name = (char *)data; |
| if (!len) name = NULL; |
| else CHECK_STRING( "create_event", name, len ); |
| |
| obj = create_event( name, req->manual_reset, req->initial_state ); |
| if (obj) |
| { |
| reply.handle = alloc_handle( current->process, obj, EVENT_ALL_ACCESS, req->inherit ); |
| release_object( obj ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* do an event operation */ |
| DECL_HANDLER(event_op) |
| { |
| switch(req->op) |
| { |
| case PULSE_EVENT: |
| pulse_event( req->handle ); |
| break; |
| case SET_EVENT: |
| set_event( req->handle ); |
| break; |
| case RESET_EVENT: |
| reset_event( req->handle ); |
| break; |
| default: |
| fatal_protocol_error( "event_op: invalid operation %d\n", req->op ); |
| } |
| send_reply( current, -1, 0 ); |
| } |
| |
| /* create a mutex */ |
| DECL_HANDLER(create_mutex) |
| { |
| struct create_mutex_reply reply = { -1 }; |
| struct object *obj; |
| char *name = (char *)data; |
| if (!len) name = NULL; |
| else CHECK_STRING( "create_mutex", name, len ); |
| |
| obj = create_mutex( name, req->owned ); |
| if (obj) |
| { |
| reply.handle = alloc_handle( current->process, obj, MUTEX_ALL_ACCESS, req->inherit ); |
| release_object( obj ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* release a mutex */ |
| DECL_HANDLER(release_mutex) |
| { |
| if (release_mutex( req->handle )) CLEAR_ERROR(); |
| send_reply( current, -1, 0 ); |
| } |
| |
| /* create a semaphore */ |
| DECL_HANDLER(create_semaphore) |
| { |
| struct create_semaphore_reply reply = { -1 }; |
| struct object *obj; |
| char *name = (char *)data; |
| if (!len) name = NULL; |
| else CHECK_STRING( "create_semaphore", name, len ); |
| |
| obj = create_semaphore( name, req->initial, req->max ); |
| if (obj) |
| { |
| reply.handle = alloc_handle( current->process, obj, SEMAPHORE_ALL_ACCESS, req->inherit ); |
| release_object( obj ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* release a semaphore */ |
| DECL_HANDLER(release_semaphore) |
| { |
| struct release_semaphore_reply reply; |
| if (release_semaphore( req->handle, req->count, &reply.prev_count )) CLEAR_ERROR(); |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* open a handle to a named object (event, mutex, semaphore) */ |
| DECL_HANDLER(open_named_obj) |
| { |
| struct open_named_obj_reply reply; |
| char *name = (char *)data; |
| if (!len) name = NULL; |
| else CHECK_STRING( "open_named_obj", name, len ); |
| |
| switch(req->type) |
| { |
| case OPEN_EVENT: |
| reply.handle = open_event( req->access, req->inherit, name ); |
| break; |
| case OPEN_MUTEX: |
| reply.handle = open_mutex( req->access, req->inherit, name ); |
| break; |
| case OPEN_SEMAPHORE: |
| reply.handle = open_semaphore( req->access, req->inherit, name ); |
| break; |
| default: |
| fatal_protocol_error( "open_named_obj: invalid type %d\n", req->type ); |
| } |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* create a file */ |
| DECL_HANDLER(create_file) |
| { |
| struct create_file_reply reply = { -1 }; |
| struct object *obj; |
| int new_fd; |
| |
| if ((new_fd = dup(fd)) == -1) |
| { |
| SET_ERROR( ERROR_TOO_MANY_OPEN_FILES ); |
| goto done; |
| } |
| if ((obj = create_file( new_fd )) != NULL) |
| { |
| reply.handle = alloc_handle( current->process, obj, req->access, req->inherit ); |
| release_object( obj ); |
| } |
| done: |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |
| |
| /* get a Unix handle to a file */ |
| DECL_HANDLER(get_unix_handle) |
| { |
| int handle = file_get_unix_handle( req->handle, req->access ); |
| send_reply( current, handle, 0 ); |
| } |
| |
| /* get a file information */ |
| DECL_HANDLER(get_file_info) |
| { |
| struct get_file_info_reply reply; |
| get_file_info( req->handle, &reply ); |
| send_reply( current, -1, 1, &reply, sizeof(reply) ); |
| } |