wine/server/request.c
Alexandre Julliard 62a8b43301 Hacked server-side device support
Added user APCs support.
Many changes to the xterm console to make use of the server (not
finished yet).
Moved some other small stuff to the server.
1999-01-19 17:48:23 +00:00

737 lines
19 KiB
C

/*
* Server-side request handling
*
* Copyright (C) 1998 Alexandre Julliard
*/
#include <assert.h>
#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;
/* 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 );
}
/* 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) );
}