/*
 * Server-side process management
 *
 * Copyright (C) 1998 Alexandre Julliard
 */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#include <unistd.h>

#include "winbase.h"
#include "winnt.h"

#include "handle.h"
#include "process.h"
#include "thread.h"
#include "request.h"
#include "console.h"
#include "gdbremote.h"
#include "server_memory.h"

/* process structure */

START_FILE_STATIC_VARIABLES(process)
  struct process *first_process;
  int running_processes;
END_FILE_STATIC_VARIABLES(process)

/* process operations */

static void process_dump( struct object *obj, int verbose );
static int process_signaled( struct object *obj, struct wait_queue_entry *entry );
static void process_poll_event( struct object *obj, int event );
static void process_destroy( struct object *obj );

static const struct object_ops process_ops =
{
    sizeof(struct process),      /* size */
    process_dump,                /* dump */
    add_queue,                   /* add_queue */
    remove_queue,                /* remove_queue */
    process_signaled,            /* signaled */
    no_satisfied,                /* satisfied */
    NULL,                        /* get_poll_events */
    process_poll_event,          /* poll_event */
    no_get_fd,                   /* get_fd */
#if defined( CONTEXTUAL_FD )
    no_get_ctx_fd,               /* get_ctx_fd */
#endif
    no_flush,                    /* flush */
    no_get_file_info,            /* get_file_info */
    NULL,                        /* queue_async */
    process_destroy              /* destroy */
};
const struct object_ops* get_this_process_process_ops(void) { return &process_ops; }

/* process startup info */

struct startup_info
{
    struct object       obj;          /* object header */
    int                 inherit_all;  /* inherit all handles from parent */
    int                 create_flags; /* creation flags */
    int                 start_flags;  /* flags from startup info */
    handle_t            hstdin;       /* handle for stdin */
    handle_t            hstdout;      /* handle for stdout */
    handle_t            hstderr;      /* handle for stderr */
    int                 cmd_show;     /* main window show mode */
    struct file        *exe_file;     /* file handle for main exe */
    char               *filename;     /* file name for main exe */
    struct thread      *owner;        /* owner thread (the one that created the new process) */
    struct process     *process;      /* created process */
    struct thread      *thread;       /* created thread */
    pid_t               unix_pid;     /* unix pid of the new process */
};

static void startup_info_dump( struct object *obj, int verbose );
static int startup_info_signaled( struct object *obj, struct wait_queue_entry *entrys );
static void startup_info_destroy( struct object *obj );

static const struct object_ops startup_info_ops =
{
    sizeof(struct startup_info),   /* size */
    startup_info_dump,             /* dump */
    add_queue,                     /* add_queue */
    remove_queue,                  /* remove_queue */
    startup_info_signaled,         /* signaled */
    no_satisfied,                  /* satisfied */
    NULL,                          /* get_poll_events */
    NULL,                          /* poll_event */
    no_get_fd,                     /* get_fd */
#if defined( CONTEXTUAL_FD )
    no_get_ctx_fd,                 /* get_ctx_fd */
#endif
    no_flush,                      /* flush */
    no_get_file_info,              /* get_file_info */
    NULL,                          /* queue_async */
    startup_info_destroy           /* destroy */
};
const struct object_ops* get_this_process_startup_info_ops(void) { return &startup_info_ops; }

struct thread* get_parent_thread_of_created_pid( int pid )
{
  struct thread* thread;
  struct thread* found = NULL;
  
  /* Enumerate all the threads in the system (perhaps not efficient) for the creator
   * of this thread. It cannot be the current thread.
   */
  for( thread = SERVER_GLOBAL_VARIABLE( first_thread ); thread ; thread = thread->next )
  {
    if( thread->info && thread->info->unix_pid == pid )
    {
      found = thread;
      break;
    }
  }

  return found;
}

/* set the console and stdio handles for a newly created process */
static int set_process_console( struct process *process, struct thread *parent_thread,
                                struct startup_info *info, struct init_process_reply *reply )
{
    if (process->create_flags & CREATE_NEW_CONSOLE)
    {
        /* let the process init do the allocation */
        return 1;
    }
    else if (parent_thread && !(process->create_flags & DETACHED_PROCESS))
    {
        /* FIXME: some better error checking should be done...
         * like if hConOut and hConIn are console handles, then they should be on the same
         * physical console
         */
        inherit_console( parent_thread, process,
                         (info->inherit_all || (info->start_flags & STARTF_USESTDHANDLES)) ?
                         info->hstdin : 0 );
    }
    if (parent_thread)
    {
        if (!info->inherit_all && !(info->start_flags & STARTF_USESTDHANDLES))
        {
            /* duplicate the handle from the parent into this process */
            reply->hstdin  = duplicate_handle( parent_thread->process, info->hstdin, process,
                                               0, TRUE, DUPLICATE_SAME_ACCESS );
            reply->hstdout = duplicate_handle( parent_thread->process, info->hstdout, process,
                                               0, TRUE, DUPLICATE_SAME_ACCESS );
            reply->hstderr = duplicate_handle( parent_thread->process, info->hstderr, process,
                                               0, TRUE, DUPLICATE_SAME_ACCESS );
        }
        else
        {
            reply->hstdin  = info->hstdin;
            reply->hstdout = info->hstdout;
            reply->hstderr = info->hstderr;
        }
    }
    else
    {
        if (process->console)
        {
            reply->hstdin  = alloc_handle( process, process->console,
                                           GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
            reply->hstdout = alloc_handle( process, process->console->active,
                                           GENERIC_READ | GENERIC_WRITE, 1 );
            reply->hstderr = alloc_handle( process, process->console->active,
                                           GENERIC_READ | GENERIC_WRITE, 1 );
        }
        else
        {
            /* no parent, let the caller decide what to do */
            reply->hstdin = reply->hstdout = reply->hstderr = 0;
        }
    }
    /* some handles above may have been invalid; this is not an error */
    if (get_error() == STATUS_INVALID_HANDLE) clear_error();
    return 1;
}

/* create a new process and its main thread */
struct thread *create_process( int fd )
{
    struct process *process;
    struct thread *thread = NULL;
    int request_pipe[2];

    if (!(process = alloc_object( process_ops_tag, fd ))) return NULL;
    process->next            = NULL;
    process->prev            = NULL;
    process->parent          = NULL;
    process->thread_list     = NULL;
    process->debugger        = NULL;
    process->handles         = NULL;
    process->exit_code       = STILL_ACTIVE;
    process->running_threads = 0;
    process->priority        = NORMAL_PRIORITY_CLASS;
    process->affinity        = 1;
    process->suspend         = 0;
    process->create_flags    = 0;
    process->console         = NULL;
    process->init_event      = NULL;
    process->idle_event      = NULL;
    process->queue           = NULL;
    process->atom_table      = NULL;
    process->ldt_copy        = NULL;
    process->group_id        = 0;
    process->id              = 0;
    process->exe.next          = NULL;
    process->exe.prev          = NULL;
    process->exe.file          = NULL;
    process->exe.file_name_len = 0;
    process->exe.file_name     = NULL;
    process->exe.dbg_offset    = 0;
    process->exe.dbg_size      = 0;
    process->token             = create_token( &process->obj );

#if defined( CONTEXTUAL_FD )
    process->context_fd_list = NULL;
    process->context_fd_list_size = 0;
#endif    


    gettimeofday( &process->start_time, NULL );
    if ((process->next = SERVER_STATIC_VARIABLE( process, first_process )) != NULL) process->next->prev = process;
    SERVER_STATIC_VARIABLE( process, first_process ) = process;

    process->id = create_process_id( process );

    /* create the init done event */
    if (!(process->init_event = create_event( NULL, 0, 1, 0 ))) goto error;

    /* create the main thread */
    if (pipe( request_pipe ) == -1)
    {
        file_set_error();
        goto error;
    }
    send_client_fd( process, request_pipe[1], 0 );
    close( request_pipe[1] );
    if (!(thread = create_thread( request_pipe[0], process ))) goto error;

#ifdef ENABLE_GDB_REMOTE
    process->gdb_socket = open_gdb_socket( process );
    process->gdb_ctx = NULL;
#endif

    set_select_events( &process->obj, POLLIN, NULL );  /* start listening to events */
    release_object( process );
    return thread;

 error:
    if (process->token) release_object(process->token);
    if (process->id) remove_process_id( process->id );
    if (thread) release_object( thread );
    release_object( process );
    return NULL;
}

/* initialize the current process and fill in the request */
static void init_process( struct init_process_reply *reply )
{
    struct process *process = current->process;

    /* current thread is the boot thread so tid == pid */
    struct thread *parent_thread = get_parent_thread_of_created_pid( current->unix_tid );

    struct process *parent = NULL;
    struct startup_info *info = NULL;
    
    if (parent_thread)
    {
        parent = parent_thread->process;
        info = parent_thread->info;
        if (!info)
        {
            fatal_protocol_error( current, "init_process: parent but no info\n" );
            return;
        }
        if (info->thread)
        {
            fatal_protocol_error( current, "init_process: called twice?\n" );
            return;
        }
        process->parent = (struct process *)grab_object( parent );
    }

    /* set the process flags */
    process->create_flags = info ? info->create_flags : 0;

    /* create the handle table */
    if (parent && info->inherit_all)
        process->handles = copy_handle_table( process, parent );
    else
        process->handles = alloc_handle_table( process, 0 );
    if (!process->handles) return;

    /* retrieve the main exe file */
    reply->exe_file = 0;
    if (parent && info->exe_file)
    {
        process->exe.file = (struct file *)grab_object( info->exe_file );
        if (!(reply->exe_file = alloc_handle( process, process->exe.file, GENERIC_READ, 0 )))
            return;
    }

    /* set the process console */
    if (!set_process_console( process, parent_thread, info, reply )) return;

    if (parent)
    {
        /* attach to the debugger if requested */
        if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
            set_process_debugger( process, parent_thread );
        else if (parent && parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
            set_process_debugger( process, parent->debugger );
        if (!(process->create_flags & CREATE_NEW_PROCESS_GROUP))
            process->group_id = parent->group_id;
    }

    /* thread will be actually suspended in init_done */
    if (process->create_flags & CREATE_SUSPENDED) current->suspend++;

    if (info)
    {
        size_t size = strlen(info->filename);
        if (size > get_reply_max_size()) size = get_reply_max_size();
        reply->start_flags = info->start_flags;
        reply->cmd_show    = info->cmd_show;
        set_reply_data( info->filename, size );
        info->process = (struct process *)grab_object( process );
        info->thread  = (struct thread *)grab_object( current );
        wake_up( &info->obj, 0 );
    }
    else
    {
        reply->start_flags  = STARTF_USESTDHANDLES;
        reply->cmd_show     = 0;
    }
    reply->create_flags = process->create_flags;
    reply->server_start = SERVER_GLOBAL_VARIABLE( server_start_ticks );
}

/* destroy a process when its refcount is 0 */
static void process_destroy( struct object *obj )
{
    struct process *process = (struct process *)obj;
    process_id_t proc_id = get_process_id(process);
    assert( obj->ops == process_ops_tag );

    /* we can't have a thread remaining */
    assert( !process->thread_list );
    if (proc_id > 0)
    {
       remove_process_id ( proc_id );
    }
    if (process->token) release_object(process->token);
    if (process->console) release_object( process->console );
    if (process->parent) release_object( process->parent );
    if (process->next) process->next->prev = process->prev;
    if (process->prev) process->prev->next = process->next;
    else SERVER_STATIC_VARIABLE( process, first_process ) = process->next;
    if (process->init_event) release_object( process->init_event );
    if (process->idle_event) release_object( process->idle_event );
    if (process->queue) release_object( process->queue );
    if (process->atom_table) release_object( process->atom_table );
    if (process->exe.file) release_object( process->exe.file );
    if (process->exe.file_name) mem_free( process->exe.file_name );

#if defined( CONTEXTUAL_FD )
    if (process->context_fd_list) mem_free( process->context_fd_list );
#endif    
}

/* dump a process on stdout for debugging purposes */
static void process_dump( struct object *obj, int verbose )
{
    struct process *process = (struct process *)obj;
    assert( obj->ops == process_ops_tag );

    fprintf( stderr, "Process %04x next=%p prev=%p handles=%p\n",
             get_process_id( process ), process->next, process->prev, process->handles );
}

static int process_signaled( struct object *obj, struct wait_queue_entry *entry )
{
    struct process *process = (struct process *)obj;
    return !process->running_threads;
}


static void process_poll_event( struct object *obj, int event )
{
    struct process *process = (struct process *)obj;
    assert( obj->ops == process_ops_tag );

    if (event & (POLLERR | POLLHUP)) set_select_events( obj, -1, NULL );
    else if (event & POLLIN) receive_fd( process );
}

static void startup_info_destroy( struct object *obj )
{
    struct startup_info *info = (struct startup_info *)obj;
    assert( obj->ops == startup_info_ops_tag );
    if (info->filename) mem_free( info->filename );
    if (info->exe_file) release_object( info->exe_file );
    if (info->process) release_object( info->process );
    if (info->thread) release_object( info->thread );
    if (info->owner)
    {
        info->owner->info = NULL;
        release_object( info->owner );
    }
}

static void startup_info_dump( struct object *obj, int verbose )
{
    struct startup_info *info = (struct startup_info *)obj;
    assert( obj->ops == startup_info_ops_tag );

    fprintf( stderr, "Startup info flags=%x in=%d out=%d err=%d name='%s'\n",
             info->start_flags, info->hstdin, info->hstdout, info->hstderr, info->filename );
}

static int startup_info_signaled( struct object *obj, struct wait_queue_entry *entry )
{
    struct startup_info *info = (struct startup_info *)obj;
    return (info->thread != NULL);
}

/* get a process from a handle (and increment the refcount) */
struct process *get_process_from_handle( handle_t handle, unsigned int access )
{
    return (struct process *)get_handle_obj( current->process, handle,
                                             access, process_ops_tag );
}

/* add a dll to a process list */
static struct process_dll *process_load_dll( struct process *process, struct file *file,
                                             void *base )
{
    struct process_dll *dll;

    /* make sure we don't already have one with the same base address */
    for (dll = process->exe.next; dll; dll = dll->next) if (dll->base == base)
    {
        set_error( STATUS_INVALID_PARAMETER );
        return NULL;
    }

    if ((dll = mem_alloc( sizeof(*dll) )))
    {
        dll->prev = &process->exe;
        dll->file = NULL;
        dll->base = base;
        if (file) dll->file = (struct file *)grab_object( file );
        if ((dll->next = process->exe.next)) dll->next->prev = dll;
        process->exe.next = dll;
    }
    return dll;
}

/* remove a dll from a process list */
static void process_unload_dll( struct process *process, void *base )
{
    struct process_dll *dll;

    for (dll = process->exe.next; dll; dll = dll->next)
    {
        if (dll->base == base)
        {
            if (dll->file) release_object( dll->file );
            if (dll->file_name) mem_free( dll->file_name );
            if (dll->next) dll->next->prev = dll->prev;
            if (dll->prev) dll->prev->next = dll->next;
            mem_free( dll );
            generate_debug_event( current, UNLOAD_DLL_DEBUG_EVENT, base );
            return;
        }
    }
    set_error( STATUS_INVALID_PARAMETER );
}

/* kill all processes being attached to a console renderer */
void kill_console_processes( struct thread *renderer, int exit_code )
{
    for (;;)  /* restart from the beginning of the list every time */
    {
        struct process *process = SERVER_STATIC_VARIABLE( process, first_process );

        /* find the first process being attached to 'renderer' and still running */
        while (process &&
               (process == renderer->process || !process->console ||
                process->console->renderer != renderer || !process->running_threads))
        {
            process = process->next;
        }
        if (!process) break;
        kill_process( process, NULL, exit_code );
    }
}

/* a process has been killed (i.e. its last thread died) */
static void process_killed( struct process *process )
{
    assert( !process->thread_list );
    gettimeofday( &process->end_time, NULL );
#ifdef ENABLE_GDB_REMOTE
    if (process->gdb_socket) close_gdb_socket( process->gdb_socket );
#endif
    if (process->handles) release_object( process->handles );
    process->handles = NULL;

    /* close the console attached to this process, if any */
    free_console( process );

    while (process->exe.next)
    {
        struct process_dll *dll = process->exe.next;
        process->exe.next = dll->next;
        if (dll->file) release_object( dll->file );
        if (dll->file_name) mem_free( dll->file_name );
        mem_free( dll );
    }
    if (process->exe.file) release_object( process->exe.file );
    process->exe.file = NULL;
    wake_up( &process->obj, 0 );
    if (!--SERVER_STATIC_VARIABLE( process, running_processes ))
    {
        /* last process died, close global handles */
        close_global_handles();
        /* this will cause the select loop to terminate */
        if (!persistent_server) close_master_socket();
    }
}

/* add a thread to a process running threads list */
void add_process_thread( struct process *process, struct thread *thread )
{
    thread->proc_next = process->thread_list;
    thread->proc_prev = NULL;
    if (thread->proc_next) thread->proc_next->proc_prev = thread;
    process->thread_list = thread;
    if (!process->running_threads++) SERVER_STATIC_VARIABLE( process, running_processes )++;
    grab_object( thread );
}

/* remove a thread from a process running threads list */
void remove_process_thread( struct process *process, struct thread *thread )
{
    assert( process->running_threads > 0 );
    assert( process->thread_list );

    if (thread->proc_next) thread->proc_next->proc_prev = thread->proc_prev;
    if (thread->proc_prev) thread->proc_prev->proc_next = thread->proc_next;
    else process->thread_list = thread->proc_next;

    if (!--process->running_threads)
    {
        /* we have removed the last running thread, exit the process */
        process->exit_code = thread->exit_code;
        generate_debug_event( thread, EXIT_PROCESS_DEBUG_EVENT, process );
        process_killed( process );
    }
    else generate_debug_event( thread, EXIT_THREAD_DEBUG_EVENT, thread );
    release_object( thread );
}

/* suspend all the threads of a process */
void suspend_process( struct process *process )
{
    if (!process->suspend++)
    {
        struct thread *thread = process->thread_list;
        for (; thread; thread = thread->proc_next)
        {
            if (!thread->suspend) stop_thread( thread );
        }
    }
}

/* resume all the threads of a process */
void resume_process( struct process *process )
{
    assert (process->suspend > 0);
    if (!--process->suspend)
    {
        struct thread *thread = process->thread_list;
        for (; thread; thread = thread->proc_next)
        {
            if (!thread->suspend)
            {
                continue_thread( thread );
                wake_thread( thread );
            }
        }
    }
}

/* kill a process on the spot */
void kill_process( struct process *process, struct thread *skip, int exit_code )
{
    struct thread *thread = process->thread_list;

    while (thread)
    {
        struct thread *next = thread->proc_next;
        thread->exit_code = exit_code;
        if (thread != skip) kill_thread( thread, 1 );
        thread = next;
    }
}

/* kill all processes being debugged by a given thread */
void kill_debugged_processes( struct thread *debugger, int exit_code )
{
    for (;;)  /* restart from the beginning of the list every time */
    {
        struct process *process = SERVER_STATIC_VARIABLE( process, first_process );
        /* find the first process being debugged by 'debugger' and still running */
        while (process && (process->debugger != debugger || !process->running_threads))
            process = process->next;
        if (!process) return;
        process->debugger = NULL;
        kill_process( process, NULL, exit_code );
    }
}


/* detach a debugger from all its debuggees */
void detach_debugged_processes( struct thread *debugger )
{
    struct process *process;
    for (process = SERVER_STATIC_VARIABLE( process, first_process ); process; process = process->next)
    {
        if (process->debugger == debugger && process->running_threads)
        {
            debugger_detach( process, debugger );
        }
    }
}


void enum_processes( int (*cb)(struct process*, void*), void *user )
{
    struct process *process;
    for (process = SERVER_STATIC_VARIABLE( process, first_process ); process; process = process->next)
    {
        if ((cb)(process, user)) break;
    }
}


/* get all information about a process */
static void get_process_info( struct process *process, struct get_process_info_reply *reply )
{
    reply->pid              = get_process_id( process );
    reply->debugged         = (process->debugger != 0);
    reply->exit_code        = process->exit_code;
    reply->priority         = process->priority;
    reply->process_affinity = process->affinity;
    reply->system_affinity  = 1;
}

/* set all information about a process */
static void set_process_info( struct process *process,
                              const struct set_process_info_request *req )
{
    if (req->mask & SET_PROCESS_INFO_PRIORITY)
        process->priority = req->priority;
    if (req->mask & SET_PROCESS_INFO_AFFINITY)
    {
        if (req->affinity != 1) set_error( STATUS_INVALID_PARAMETER );
        else process->affinity = req->affinity;
    }
}

/* Stopping all the threads and reading /proc/pid/maps would be better,
 * but this will do. */
static int check_process_read_access( struct thread *thread, uintptr_t addr,
				      size_t len )
{
    char buf;
    size_t num_read;

    size_t page_size = get_page_size();

    /* The range to check is [addr,end). */
    uintptr_t end = addr + len;

    /* Make [addr,end) cover all the pages in the range to check. */
    addr -= addr % page_size;
    if (end % page_size != 0) end += page_size - (end % page_size);

    for (; addr < end; addr += page_size)
    {
	if (read_thread_data( thread, addr, 1, &buf, &num_read) == -1)
	    return 0;
    }

    return 1;
}

/* read data from a process memory space
 * addr is the virtual address in that process, len is the number of bytes.
 *
 * check_len (if nonzero) is the length of the range that needs to be
 * checked for read permissions.
 */
static void read_process_memory( struct process *process, uintptr_t addr,
                                 size_t len, void *dest, size_t check_len )
{
    struct thread *thread = process->thread_list;

    if (!thread)  /* process is dead */
    {
        set_error( STATUS_ACCESS_DENIED );
        return;
    }

    if (suspend_for_ptrace( thread ))
    {
	size_t bytes_read;

	if (!check_process_read_access( thread, addr, check_len ))
	{
            resume_thread( thread );  /* resume thread even if error */
	    set_error( STATUS_ACCESS_DENIED );
	    return;
	}

	read_thread_data( thread, addr, len, dest, &bytes_read);
        resume_thread( thread );
    }
}

/* write data to a process memory space
 * addr is the virtual address in that process, len is the number of bytes.
 *
 * check_len (if nonzero) is the size of the range that should be checked
 * for write access. We don't actually do this yet. (We'd have to stop
 * all the threads and look through /proc/pid/maps.)
 */
static void write_process_memory( struct process *process, uintptr_t addr,
				  size_t len, const void *src,
				  size_t check_len )
{
    struct thread *thread = process->thread_list;

    if (!thread)  /* process is dead */
    {
        set_error( STATUS_ACCESS_DENIED );
        return;
    }

    if (suspend_for_ptrace( thread ))
    {
	size_t bytes_written;
	write_thread_data( thread, addr, len, src, &bytes_written);
        resume_thread( thread );
    }
}

/* take a snapshot of currently running processes */
struct process_snapshot *process_snap( int *count )
{
    struct process_snapshot *snapshot, *ptr;
    struct process *process;
    if (!SERVER_STATIC_VARIABLE( process, running_processes )) return NULL;
    if (!(snapshot = mem_alloc( sizeof(*snapshot) * SERVER_STATIC_VARIABLE( process, running_processes ) )))
        return NULL;
    ptr = snapshot;
    for (process = SERVER_STATIC_VARIABLE( process, first_process ); process; process = process->next)
    {
        if (!process->running_threads) continue;
        ptr->process  = process;
        ptr->threads  = process->running_threads;
        ptr->count    = process->obj.refcount;
        ptr->priority = process->priority;
        grab_object( process );
        ptr++;
    }
    *count = SERVER_STATIC_VARIABLE( process, running_processes );
    return snapshot;
}

/* take a snapshot of the modules of a process */
struct module_snapshot *module_snap( struct process *process, int *count )
{
    struct module_snapshot *snapshot, *ptr;
    struct process_dll *dll;
    int total = 0;

    for (dll = &process->exe; dll; dll = dll->next) total++;
    if (!(snapshot = mem_alloc( sizeof(*snapshot) * total ))) return NULL;

    for (ptr = snapshot, dll = &process->exe; dll; dll = dll->next, ptr++)
    {
        ptr->base = dll->base;
        ptr->base_size = dll->base_size;
        ptr->name_len = dll->file_name_len;
        ptr->name = mem_dup( dll->file_name, ptr->name_len );
    }
    *count = total;
    return snapshot;
}


/* create a new process */
DECL_HANDLER(new_process)
{
    size_t len = get_req_data_size();
    struct startup_info *info;

    if (current->info)
    {
        fatal_protocol_error( current, "new_process: another process is being created\n" );
        return;
    }

    /* build the startup info for a new process */
    if (!(info = alloc_object( startup_info_ops_tag, -1 ))) return;
    info->inherit_all  = req->inherit_all;
    info->create_flags = req->create_flags;
    info->start_flags  = req->start_flags;
    info->hstdin       = req->hstdin;
    info->hstdout      = req->hstdout;
    info->hstderr      = req->hstderr;
    info->cmd_show     = req->cmd_show;
    info->unix_pid     = req->unix_pid;

    info->exe_file     = NULL;
    info->filename     = NULL;
    info->owner        = (struct thread *)grab_object( current );
    info->process      = NULL;
    info->thread       = NULL;

    if (req->exe_file &&
        !(info->exe_file = get_file_obj( current->process, req->exe_file, GENERIC_READ )))
        goto done;

    if (!(info->filename = mem_alloc( len + 1 ))) goto done;

    memcpy( info->filename, get_req_data(), len );
    info->filename[len] = 0;
    current->info = info;
    reply->info = alloc_handle( current->process, info, SYNCHRONIZE, FALSE );

 done:
    release_object( info );
}

/* Retrieve information about a newly started process */
DECL_HANDLER(get_new_process_info)
{
    struct startup_info *info;

    reply->event = 0;

    if ((info = (struct startup_info *)get_handle_obj( current->process, req->info,
                                                       0, startup_info_ops_tag )))
    {
        reply->pid = get_process_id( info->process );
        reply->tid = get_thread_id( info->thread );
        reply->phandle = alloc_handle( current->process, info->process,
                                       PROCESS_ALL_ACCESS, req->pinherit );
        reply->thandle = alloc_handle( current->process, info->thread,
                                       THREAD_ALL_ACCESS, req->tinherit );
        if (info->process->init_event)
            reply->event = alloc_handle( current->process, info->process->init_event,
                                       EVENT_ALL_ACCESS, 0 );
        release_object( info );
    }
    else
    {
        reply->pid     = 0;
        reply->tid     = 0;
        reply->phandle = 0;
        reply->thandle = 0;
    }
}

/* initialize a new process */
DECL_HANDLER(init_process)
{
    if (!current->unix_tid)
    {
        fatal_protocol_error( current, "init_process: init_thread not called yet\n" );
        return;
    }
    current->process->ldt_copy  = req->ldt_copy;
    init_process( reply );
}

/* signal the end of the process initialization */
DECL_HANDLER(init_process_done)
{
    struct file *file = NULL;
    struct process *process = current->process;

    if (!process->init_event)
    {
        fatal_protocol_error( current, "init_process_done: no event\n" );
        return;
    }
    process->exe.base = req->module;
    process->exe.base_size = req->base_size;
    process->exe.name = req->name;

    if (req->exe_file) file = get_file_obj( current->process, req->exe_file, GENERIC_READ );
    if (process->exe.file) release_object( process->exe.file );
    process->exe.file = file;
    process->exe.file_name_len = get_req_data_size();
    if (process->exe.file_name_len)
        process->exe.file_name = mem_dup( get_req_data(), process->exe.file_name_len );

    generate_startup_debug_events( current->process, req->entry );
    set_event( process->init_event );
    release_object( process->init_event );
    process->init_event = NULL;
    if (req->gui) process->idle_event = create_event( NULL, 0, 1, 0 );
    if (current->suspend + current->process->suspend > 0) stop_thread( current );
    reply->debugged = (current->process->debugger != 0);
    reply->shm_server = SERVER_GLOBAL_VARIABLE( shared_memory_server );
    reply->shm_size = SERVER_GLOBAL_VARIABLE( shared_memory_server_size );
    reply->shm_addr = SERVER_GLOBAL_VARIABLE( shared_memory_server_addr );
}

/* open a handle to a process */
DECL_HANDLER(open_process)
{
    struct process *process = get_process_from_id( req->pid );
    reply->handle = 0;
    if (process)
    {
        reply->handle = alloc_handle( current->process, process, req->access, req->inherit );
        release_object( process );
    }
}

/* terminate a process */
DECL_HANDLER(terminate_process)
{
    struct process *process;

    if ((process = get_process_from_handle( req->handle, PROCESS_TERMINATE )))
    {
        reply->self = (current->process == process);
        kill_process( process, current, req->exit_code );
        release_object( process );
    }
}

/* fetch information about a process */
DECL_HANDLER(get_process_info)
{
    struct process *process;

    if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION )))
    {
        get_process_info( process, reply );
        release_object( process );
    }
}

/* 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 );
    }
}

/* read data from a process address space */
DECL_HANDLER(read_process_memory)
{
    struct process *process;
    size_t len = get_reply_max_size();

    if (!(process = get_process_from_handle( req->handle, PROCESS_VM_READ ))) return;

    if (len)
    {
        read_process_memory( process, (uintptr_t)req->addr,
			     len, set_reply_data_size(len),
			     req->check_len );
    }

    release_object( process );
}

/* write data to a process address space */
DECL_HANDLER(write_process_memory)
{
    struct process *process;

    if ((process = get_process_from_handle( req->handle, PROCESS_VM_WRITE )))
    {
	write_process_memory( process, (uintptr_t)req->addr,
			      get_req_data_size(), get_req_data(),
			      req->check_len );
        release_object( process );
    }
}

/* notify the server that a dll has been loaded */
DECL_HANDLER(load_dll)
{
    struct process_dll *dll;
    struct file *file = NULL;

    if (req->handle &&
        !(file = get_file_obj( current->process, req->handle, GENERIC_READ ))) return;

    if ((dll = process_load_dll( current->process, file, req->base )))
    {
        dll->file_name_len = get_req_data_size();
        if ( dll->file_name_len )
            dll->file_name = mem_dup( get_req_data(), dll->file_name_len );
        dll->base_size  = req->base_size;
        dll->dbg_offset = req->dbg_offset;
        dll->dbg_size   = req->dbg_size;
        dll->name       = req->name;
        /* only generate event if initialization is done */
        if (!current->process->init_event)
            generate_debug_event( current, LOAD_DLL_DEBUG_EVENT, dll );
    }
    if (file) release_object( file );
}

/* notify the server that a dll is being unloaded */
DECL_HANDLER(unload_dll)
{
    process_unload_dll( current->process, req->base );
}

/* wait for a process to start waiting on input */
/* FIXME: only returns event for now, wait is done in the client */
DECL_HANDLER(wait_input_idle)
{
    struct process *process;

    reply->event = 0;
    if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION )))
    {
        if (process->idle_event && process != current->process && process->queue != current->queue)
            reply->event = alloc_handle( current->process, process->idle_event,
                                         EVENT_ALL_ACCESS, 0 );
        release_object( process );
    }
}


/* Function that retrieves the ID for a particular process. If
   this is a new process (ie. doesn't have a ID already) it will 
   allocate a new entry in the linked list of process ids
*/

/* FIXME MOOF NOTE: Currently the threads and processes won't
   share the same IDs. ie. they come from the same pool. */
process_id_t create_process_id ( struct process *process )
{
   process_id_t pid;
   pid = get_resource_id ( &process->obj, &SERVER_GLOBAL_VARIABLE (threadids), &SERVER_GLOBAL_VARIABLE(first_free_threadid), 
                              &SERVER_GLOBAL_VARIABLE(last_free_threadid) );

   return pid;
}

/* get a process pointer from a process id (and increment the refcount) */
struct process *get_process_from_id( const process_id_t id )
{
   struct process *proc_ptr;
   proc_ptr = (struct process*)get_resource_from_id ( id, &SERVER_GLOBAL_VARIABLE( threadids ) );
   return proc_ptr;
}


/* Removes the process id structure from the linked list of other processes that exist in the system. */
void remove_process_id ( const process_id_t process_id )
{
    remove_resource_id ( process_id, &SERVER_GLOBAL_VARIABLE( threadids ), &SERVER_GLOBAL_VARIABLE( first_free_threadid ),
                         &SERVER_GLOBAL_VARIABLE( last_free_threadid ) );
                         
}

