blob: 49b9f5a972b587d37421e0e15b316ba4aee8e38a [file] [log] [blame]
/*
* Server-side request handling
*
* Copyright (C) 1998 Alexandre Julliard
*/
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#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 process */
DECL_HANDLER(new_process)
{
struct new_process_reply reply;
struct process *process;
if ((process = create_process( req )))
{
reply.pid = process;
reply.handle = alloc_handle( current->process, process,
PROCESS_ALL_ACCESS, req->inherit );
release_object( process );
}
else
{
reply.handle = -1;
reply.pid = NULL;
}
send_reply( current, -1, 1, &reply, sizeof(reply) );
}
/* create a new thread */
DECL_HANDLER(new_thread)
{
struct new_thread_reply reply;
int new_fd;
if ((new_fd = dup(fd)) != -1)
{
reply.tid = create_thread( new_fd, req->pid, req->suspend,
req->inherit, &reply.handle );
if (!reply.tid) close( new_fd );
}
else
SET_ERROR( ERROR_TOO_MANY_OPEN_FILES );
send_reply( current, -1, 1, &reply, sizeof(reply) );
}
/* initialize a new process */
DECL_HANDLER(init_process)
{
struct init_process_reply reply;
if (current->state != RUNNING)
{
fatal_protocol_error( "init_process: init_thread not called yet\n" );
return;
}
if (!get_process_init_info( current->process, &reply ))
{
fatal_protocol_error( "init_process: called twice\n" );
return;
}
send_reply( current, -1, 1, &reply, sizeof(reply) );
}
/* initialize a new thread */
DECL_HANDLER(init_thread)
{
struct init_thread_reply reply;
if (current->state != STARTING)
{
fatal_protocol_error( "init_thread: already running\n" );
return;
}
current->state = RUNNING;
current->unix_pid = req->unix_pid;
if (current->suspend > 0)
kill( current->unix_pid, SIGSTOP );
reply.pid = current->process;
reply.tid = current;
send_reply( current, -1, 1, &reply, sizeof(reply) );
}
/* 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 );
}
/* get information about a handle */
DECL_HANDLER(get_handle_info)
{
struct get_handle_info_reply reply;
reply.flags = set_handle_info( current->process, req->handle, 0, 0 );
send_reply( current, -1, 1, &reply, sizeof(reply) );
}
/* set a handle information */
DECL_HANDLER(set_handle_info)
{
set_handle_info( current->process, req->handle, req->mask, req->flags );
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,
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->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 );
}
/* debugger support operations */
DECL_HANDLER(debugger)
{
switch ( req->op )
{
case DEBUGGER_FREEZE_ALL:
suspend_all_threads();
break;
case DEBUGGER_UNFREEZE_ALL:
resume_all_threads();
break;
}
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;
reply.low = req->low;
reply.high = 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, req->inherit );
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) );
}