/* * Server-side request handling * * Copyright (C) 1998 Alexandre Julliard */ #include #include #include #include #include #include #include #include "winerror.h" #include "winnt.h" #include "winbase.h" #include "wincon.h" #define WANT_REQUEST_HANDLERS #include "server.h" #include "server/request.h" #include "server/process.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; /* Make sure last_req is initialized */ current->last_req = REQ_SET_DEBUG; 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 (req->options & DUP_HANDLE_MAKE_GLOBAL) { reply.handle = duplicate_handle( src, req->src_handle, NULL, -1, req->access, req->inherit, req->options ); } else 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 & DUP_HANDLE_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, 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) ); } /* set information about a process */ DECL_HANDLER(set_process_info) { struct process *process; if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION ))) { set_process_info( process, req ); release_object( process ); } send_reply( current, -1, 0 ); } /* 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) ); } /* set information about a thread */ DECL_HANDLER(set_thread_info) { struct thread *thread; if ((thread = get_thread_from_handle( req->handle, THREAD_SET_INFORMATION ))) { set_thread_info( thread, req ); release_object( thread ); } send_reply( current, -1, 0 ); } /* suspend a thread */ DECL_HANDLER(suspend_thread) { struct thread *thread; struct suspend_thread_reply reply = { -1 }; if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) { reply.count = suspend_thread( thread ); release_object( thread ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* resume a thread */ DECL_HANDLER(resume_thread) { struct thread *thread; struct resume_thread_reply reply = { -1 }; if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) { reply.count = resume_thread( thread ); release_object( thread ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* queue an APC for a thread */ DECL_HANDLER(queue_apc) { struct thread *thread; if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ))) { thread_queue_apc( thread, req->func, req->param ); release_object( thread ); } send_reply( current, -1, 0 ); } /* 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; case OPEN_MAPPING: reply.handle = open_mapping( 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; char *name = (char *)data; if (!len) name = NULL; else CHECK_STRING( "create_file", name, len ); if ((obj = create_file( fd, name, req->access, req->sharing, req->create, req->attrs )) != NULL) { reply.handle = alloc_handle( current->process, obj, req->access, req->inherit ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* get a Unix fd to read from a file */ DECL_HANDLER(get_read_fd) { struct object *obj; int read_fd; if ((obj = get_handle_obj( current->process, req->handle, GENERIC_READ, NULL ))) { read_fd = obj->ops->get_read_fd( obj ); release_object( obj ); } else read_fd = -1; send_reply( current, read_fd, 0 ); } /* get a Unix fd to write to a file */ DECL_HANDLER(get_write_fd) { struct object *obj; int write_fd; if ((obj = get_handle_obj( current->process, req->handle, GENERIC_WRITE, NULL ))) { write_fd = obj->ops->get_write_fd( obj ); release_object( obj ); } else write_fd = -1; send_reply( current, write_fd, 0 ); } /* set a file current position */ DECL_HANDLER(set_file_pointer) { struct set_file_pointer_reply reply = { req->low, req->high }; set_file_pointer( req->handle, &reply.low, &reply.high, req->whence ); send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* truncate (or extend) a file */ DECL_HANDLER(truncate_file) { truncate_file( req->handle ); send_reply( current, -1, 0 ); } /* flush a file buffers */ DECL_HANDLER(flush_file) { struct object *obj; if ((obj = get_handle_obj( current->process, req->handle, GENERIC_WRITE, NULL ))) { obj->ops->flush( obj ); release_object( obj ); } send_reply( current, -1, 0 ); } /* set a file access and modification times */ DECL_HANDLER(set_file_time) { set_file_time( req->handle, req->access_time, req->write_time ); send_reply( current, -1, 0 ); } /* get a file information */ DECL_HANDLER(get_file_info) { struct object *obj; struct get_file_info_reply reply; if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) { obj->ops->get_file_info( obj, &reply ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* lock a region of a file */ DECL_HANDLER(lock_file) { struct file *file; if ((file = get_file_obj( current->process, req->handle, 0 ))) { file_lock( file, req->offset_high, req->offset_low, req->count_high, req->count_low ); release_object( file ); } send_reply( current, -1, 0 ); } /* unlock a region of a file */ DECL_HANDLER(unlock_file) { struct file *file; if ((file = get_file_obj( current->process, req->handle, 0 ))) { file_unlock( file, req->offset_high, req->offset_low, req->count_high, req->count_low ); release_object( file ); } send_reply( current, -1, 0 ); } /* create an anonymous pipe */ DECL_HANDLER(create_pipe) { struct create_pipe_reply reply = { -1, -1 }; struct object *obj[2]; if (create_pipe( obj )) { reply.handle_read = alloc_handle( current->process, obj[0], STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_READ, req->inherit ); if (reply.handle_read != -1) { reply.handle_write = alloc_handle( current->process, obj[1], STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_WRITE, req->inherit ); if (reply.handle_write == -1) close_handle( current->process, reply.handle_read ); } release_object( obj[0] ); release_object( obj[1] ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* allocate a console for the current process */ DECL_HANDLER(alloc_console) { alloc_console( current->process ); send_reply( current, -1, 0 ); } /* free the console of the current process */ DECL_HANDLER(free_console) { free_console( current->process ); send_reply( current, -1, 0 ); } /* open a handle to the process console */ DECL_HANDLER(open_console) { struct object *obj; struct open_console_reply reply = { -1 }; if ((obj = get_console( current->process, req->output ))) { reply.handle = alloc_handle( current->process, obj, req->access, req->inherit ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* set info about a console (output only) */ DECL_HANDLER(set_console_info) { char *name = (char *)data; if (!len) name = NULL; else CHECK_STRING( "set_console_info", name, len ); set_console_info( req->handle, req, name ); send_reply( current, -1, 0 ); } /* get info about a console (output only) */ DECL_HANDLER(get_console_info) { struct get_console_info_reply reply; const char *title; get_console_info( req->handle, &reply, &title ); send_reply( current, -1, 2, &reply, sizeof(reply), title, title ? strlen(title)+1 : 0 ); } /* set a console fd */ DECL_HANDLER(set_console_fd) { set_console_fd( req->handle, fd, req->pid ); send_reply( current, -1, 0 ); } /* get a console mode (input or output) */ DECL_HANDLER(get_console_mode) { struct get_console_mode_reply reply; get_console_mode( req->handle, &reply.mode ); send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* set a console mode (input or output) */ DECL_HANDLER(set_console_mode) { set_console_mode( req->handle, req->mode ); send_reply( current, -1, 0 ); } /* add input records to a console input queue */ DECL_HANDLER(write_console_input) { struct write_console_input_reply reply; INPUT_RECORD *records = (INPUT_RECORD *)data; if (len != req->count * sizeof(INPUT_RECORD)) fatal_protocol_error( "write_console_input: bad length %d for %d records\n", len, req->count ); reply.written = write_console_input( req->handle, req->count, records ); send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* fetch input records from a console input queue */ DECL_HANDLER(read_console_input) { read_console_input( req->handle, req->count, req->flush ); } /* create a change notification */ DECL_HANDLER(create_change_notification) { struct object *obj; struct create_change_notification_reply reply = { -1 }; if ((obj = create_change_notification( req->subtree, req->filter ))) { reply.handle = alloc_handle( current->process, obj, STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE, 0 ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* create a file mapping */ DECL_HANDLER(create_mapping) { struct object *obj; struct create_mapping_reply reply = { -1 }; char *name = (char *)data; if (!len) name = NULL; else CHECK_STRING( "create_mapping", name, len ); if ((obj = create_mapping( req->size_high, req->size_low, req->protect, req->handle, name ))) { int access = FILE_MAP_ALL_ACCESS; if (!(req->protect & VPROT_WRITE)) access &= ~FILE_MAP_WRITE; reply.handle = alloc_handle( current->process, obj, access, 0 ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* get a mapping information */ DECL_HANDLER(get_mapping_info) { struct get_mapping_info_reply reply; int map_fd = get_mapping_info( req->handle, &reply ); send_reply( current, map_fd, 1, &reply, sizeof(reply) ); } /* create a device */ DECL_HANDLER(create_device) { struct object *obj; struct create_device_reply reply = { -1 }; if ((obj = create_device( req->id ))) { reply.handle = alloc_handle( current->process, obj, req->access, req->inherit ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* create a snapshot */ DECL_HANDLER(create_snapshot) { struct object *obj; struct create_snapshot_reply reply = { -1 }; if ((obj = create_snapshot( req->flags ))) { reply.handle = alloc_handle( current->process, obj, 0, req->inherit ); release_object( obj ); } send_reply( current, -1, 1, &reply, sizeof(reply) ); } /* get the next process from a snapshot */ DECL_HANDLER(next_process) { struct next_process_reply reply; snapshot_next_process( req->handle, req->reset, &reply ); send_reply( current, -1, 1, &reply, sizeof(reply) ); }