Rewrote Unix process launching to allow passing startup information to
Winelib apps. Improved handling of execve() failures.

diff --git a/server/process.c b/server/process.c
index c84672d..57825e8 100644
--- a/server/process.c
+++ b/server/process.c
@@ -55,47 +55,54 @@
     process_destroy              /* destroy */
 };
 
-/* set the process creation info */
-static int set_creation_info( struct process *process, struct new_process_request *req,
-                              const char *cmd_line, size_t len )
+/* process startup info */
+
+struct startup_info
 {
-    if (!(process->info = mem_alloc( sizeof(*process->info) + len ))) return 0;
-    if (req)
-    {
-        /* copy the request structure */
-        memcpy( process->info, req, sizeof(*req) );
-    }
-    else  /* no request, use defaults */
-    {
-        req = process->info;
-        req->pinherit     = 0;
-        req->tinherit     = 0;
-        req->inherit_all  = 0;
-        req->create_flags = CREATE_NEW_CONSOLE;
-        req->start_flags  = STARTF_USESTDHANDLES;
-        req->exe_file     = -1;
-        req->hstdin       = -1;
-        req->hstdout      = -1;
-        req->hstderr      = -1;
-        req->cmd_show     = 0;
-        req->env_ptr      = NULL;
-    }
-    memcpy( process->info->cmdline, cmd_line, len );
-    process->info->cmdline[len] = 0;
-    process->create_flags = process->info->create_flags;
-    return 1;
-}
+    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 */
+    int                 exe_file;     /* file handle for main exe */
+    int                 hstdin;       /* handle for stdin */
+    int                 hstdout;      /* handle for stdout */
+    int                 hstderr;      /* handle for stderr */
+    int                 cmd_show;     /* main window show mode */
+    struct process     *process;      /* created process */
+    struct thread      *thread;       /* created thread */
+};
+
+static void startup_info_dump( struct object *obj, int verbose );
+static int startup_info_signaled( struct object *obj, struct thread *thread );
+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_read_fd,                    /* get_read_fd */
+    no_write_fd,                   /* get_write_fd */
+    no_flush,                      /* flush */
+    no_get_file_info,              /* get_file_info */
+    startup_info_destroy           /* destroy */
+};
+
 
 /* set the console and stdio handles for a newly created process */
-static int set_process_console( struct process *process, struct process *parent )
+static int set_process_console( struct process *process, struct process *parent,
+                                struct startup_info *info, struct init_process_request *req )
 {
-    struct new_process_request *info = process->info;
-
     if (process->create_flags & CREATE_NEW_CONSOLE)
     {
         if (!alloc_console( process )) return 0;
     }
-    else if (!(process->create_flags & DETACHED_PROCESS))
+    else if (parent && !(process->create_flags & DETACHED_PROCESS))
     {
         if (parent->console_in) process->console_in = grab_object( parent->console_in );
         if (parent->console_out) process->console_out = grab_object( parent->console_out );
@@ -105,31 +112,35 @@
         if (!info->inherit_all && !(info->start_flags & STARTF_USESTDHANDLES))
         {
             /* duplicate the handle from the parent into this process */
-            info->hstdin  = duplicate_handle( parent, info->hstdin, process,
-                                              0, TRUE, DUPLICATE_SAME_ACCESS );
-            info->hstdout = duplicate_handle( parent, info->hstdout, process,
-                                              0, TRUE, DUPLICATE_SAME_ACCESS );
-            info->hstderr = duplicate_handle( parent, info->hstderr, process,
-                                              0, TRUE, DUPLICATE_SAME_ACCESS );
+            req->hstdin  = duplicate_handle( parent, info->hstdin, process,
+                                             0, TRUE, DUPLICATE_SAME_ACCESS );
+            req->hstdout = duplicate_handle( parent, info->hstdout, process,
+                                             0, TRUE, DUPLICATE_SAME_ACCESS );
+            req->hstderr = duplicate_handle( parent, info->hstderr, process,
+                                             0, TRUE, DUPLICATE_SAME_ACCESS );
+        }
+        else
+        {
+            req->hstdin  = info->hstdin;
+            req->hstdout = info->hstdout;
+            req->hstderr = info->hstderr;
         }
     }
     else
     {
         /* no parent, use handles to the console for stdio */
-        info->hstdin  = alloc_handle( process, process->console_in,
-                                      GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
-        info->hstdout = alloc_handle( process, process->console_out,
-                                      GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
-        info->hstderr = alloc_handle( process, process->console_out,
-                                      GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
+        req->hstdin  = alloc_handle( process, process->console_in,
+                                     GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
+        req->hstdout = alloc_handle( process, process->console_out,
+                                     GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
+        req->hstderr = alloc_handle( process, process->console_out,
+                                     GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
     }
     return 1;
 }
 
 /* create a new process and its main thread */
-struct thread *create_process( int fd, struct process *parent,
-                               struct new_process_request *req,
-                               const char *cmd_line, size_t len )
+struct thread *create_process( int fd )
 {
     struct process *process;
     struct thread *thread = NULL;
@@ -153,7 +164,6 @@
     process->console_in      = NULL;
     process->console_out     = NULL;
     process->init_event      = NULL;
-    process->info            = NULL;
     process->ldt_copy        = NULL;
     process->ldt_flags       = NULL;
     process->exe.next        = NULL;
@@ -165,54 +175,99 @@
     if ((process->next = first_process) != NULL) process->next->prev = process;
     first_process = process;
 
-    /* copy the request structure */
-    if (!set_creation_info( process, req, cmd_line, len )) goto error;
-
-    if (process->info->inherit_all == 2)  /* HACK! */
-        process->handles = grab_object( parent->handles );
-    else if (process->info->inherit_all)
-        process->handles = copy_handle_table( process, parent );
-    else
-        process->handles = alloc_handle_table( process, 0 );
-    if (!process->handles) goto error;
-
-    /* retrieve the main exe file */
-    if (process->info->exe_file != -1)
-    {
-        if (!(process->exe.file = get_file_obj( parent, process->info->exe_file,
-                                                GENERIC_READ ))) goto error;
-        process->info->exe_file = -1;
-    }
-
     /* create the main thread */
-    if (!(thread = create_thread( fd, process, (process->create_flags & CREATE_SUSPENDED) != 0)))
-        goto error;
+    if (!(thread = create_thread( fd, process ))) goto error;
 
     /* create the init done event */
     if (!(process->init_event = create_event( NULL, 0, 1, 0 ))) goto error;
 
-    /* set the process console */
-    if (!set_process_console( process, parent )) goto error;
-
-    /* attach to the debugger if requested */
-    if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
-        set_process_debugger( process, current );
-    else if (parent && parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
-        set_process_debugger( process, parent->debugger );
-
     add_process_thread( process, thread );
     release_object( process );
     return thread;
 
  error:
-    close( fd );
-    free_console( process );
-    if (process->handles) release_object( process->handles );
     if (thread) release_object( thread );
     release_object( process );
     return NULL;
 }
 
+/* initialize the current process and fill in the request */
+static void init_process( int ppid, struct init_process_request *req )
+{
+    struct process *process = current->process;
+    struct thread *parent_thread = get_thread_from_pid( ppid );
+    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;
+        }
+    }
+
+    /* set the process flags */
+    process->create_flags = info ? info->create_flags : CREATE_NEW_CONSOLE;
+
+    /* create the handle table */
+    if (parent && info->inherit_all == 2)  /* HACK! */
+        process->handles = grab_object( parent->handles );
+    else if (parent && info->inherit_all)
+        process->handles = copy_handle_table( process, parent );
+    else
+        process->handles = alloc_handle_table( process, 0 );
+    if (!process->handles) goto error;
+
+    /* retrieve the main exe file */
+    req->exe_file = -1;
+    if (parent && info->exe_file != -1)
+    {
+        if (!(process->exe.file = get_file_obj( parent, info->exe_file, GENERIC_READ )))
+            goto error;
+        if ((req->exe_file = alloc_handle( process, process->exe.file, GENERIC_READ, 0 )) == -1)
+            goto error;
+    }
+
+    /* set the process console */
+    if (!set_process_console( process, parent, info, req )) goto error;
+
+    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 );
+    }
+
+    /* thread will be actually suspended in init_done */
+    if (process->create_flags & CREATE_SUSPENDED) current->suspend++;
+
+    if (info)
+    {
+        req->start_flags = info->start_flags;
+        req->cmd_show    = info->cmd_show;
+        info->process = (struct process *)grab_object( process );
+        info->thread  = (struct thread *)grab_object( current );
+        wake_up( &info->obj, 0 );
+    }
+    else
+    {
+        req->start_flags  = STARTF_USESTDHANDLES;
+        req->cmd_show     = 0;
+    }
+ error:
+}
+
 /* destroy a process when its refcount is 0 */
 static void process_destroy( struct object *obj )
 {
@@ -224,7 +279,6 @@
     if (process->next) process->next->prev = process->prev;
     if (process->prev) process->prev->next = process->next;
     else first_process = process->next;
-    if (process->info) free( process->info );
     if (process->init_event) release_object( process->init_event );
     if (process->exe.file) release_object( process->exe.file );
 }
@@ -247,6 +301,57 @@
 }
 
 
+static void startup_info_destroy( struct object *obj )
+{
+    struct startup_info *info = (struct startup_info *)obj;
+    assert( obj->ops == &startup_info_ops );
+    if (info->process) release_object( info->process );
+    if (info->thread) release_object( info->thread );
+}
+
+static void startup_info_dump( struct object *obj, int verbose )
+{
+    struct startup_info *info = (struct startup_info *)obj;
+    assert( obj->ops == &startup_info_ops );
+
+    fprintf( stderr, "Startup info flags=%x in=%d out=%d err=%d\n",
+             info->start_flags, info->hstdin, info->hstdout, info->hstderr );
+}
+
+static int startup_info_signaled( struct object *obj, struct thread *thread )
+{
+    struct startup_info *info = (struct startup_info *)obj;
+    return (info->thread != NULL);
+}
+
+
+/* build a reply for the wait_process request */
+static void build_wait_process_reply( struct thread *thread, struct object *obj, int signaled )
+{
+    struct wait_process_request *req = get_req_ptr( thread );
+    if (obj)
+    {
+        struct startup_info *info = (struct startup_info *)obj;
+        assert( obj->ops == &startup_info_ops );
+
+        req->pid = get_process_id( info->process );
+        req->tid = get_thread_id( info->thread );
+        req->phandle = alloc_handle( thread->process, info->process,
+                                     PROCESS_ALL_ACCESS, req->pinherit );
+        req->thandle = alloc_handle( thread->process, info->thread,
+                                     THREAD_ALL_ACCESS, req->tinherit );
+        if (info->process->init_event)
+            req->event = alloc_handle( thread->process, info->process->init_event,
+                                       EVENT_ALL_ACCESS, 0 );
+        else
+            req->event = -1;
+
+        /* FIXME: set_error */
+    }
+    release_object( thread->info );
+    thread->info = NULL;
+}
+
 /* get a process from an id (and increment the refcount) */
 struct process *get_process_from_id( void *id )
 {
@@ -314,7 +419,7 @@
 {
     assert( !process->thread_list );
     gettimeofday( &process->end_time, NULL );
-    release_object( process->handles );
+    if (process->handles) release_object( process->handles );
     process->handles = NULL;
     free_console( process );
     while (process->exe.next)
@@ -594,83 +699,83 @@
 /* create a new process */
 DECL_HANDLER(new_process)
 {
-    size_t len = get_req_strlen( req, req->cmdline );
-    struct thread *thread;
+    struct startup_info *info;
     int sock[2];
-    int event = -1, phandle = -1;
 
-    req->phandle = -1;
-    req->thandle = -1;
-    req->event   = -1;
-    req->pid     = NULL;
-    req->tid     = NULL;
-
-    if (socketpair( AF_UNIX, SOCK_STREAM, 0, sock ) == -1)
+    if (current->info)
     {
-        file_set_error();
+        fatal_protocol_error( current, "new_process: another process is being created\n" );
         return;
     }
 
-    if (!(thread = create_process( sock[0], current->process, req, req->cmdline, len )))
-        goto error;
+    /* build the startup info for a new process */
+    if (!(info = alloc_object( &startup_info_ops, -1 ))) return;
+    info->inherit_all  = req->inherit_all;
+    info->create_flags = req->create_flags;
+    info->start_flags  = req->start_flags;
+    info->exe_file     = req->exe_file;
+    info->hstdin       = req->hstdin;
+    info->hstdout      = req->hstdout;
+    info->hstderr      = req->hstderr;
+    info->cmd_show     = req->cmd_show;
+    info->process      = NULL;
+    info->thread       = NULL;
 
-    if ((event = alloc_handle( current->process, thread->process->init_event,
-                               EVENT_ALL_ACCESS, 0 )) == -1)
-        goto error;
+    if (req->alloc_fd)
+    {
+        if (socketpair( AF_UNIX, SOCK_STREAM, 0, sock ) == -1)
+        {
+            file_set_error();
+            release_object( info );
+            return;
+        }
+        if (!create_process( sock[0] ))
+        {
+            release_object( info );
+            close( sock[1] );
+            return;
+        }
+        /* thread object will be released when the thread gets killed */
+        set_reply_fd( current, sock[1] );
+    }
+    current->info = info;
+}
 
-    if ((phandle = alloc_handle( current->process, thread->process,
-                                 PROCESS_ALL_ACCESS, req->pinherit )) == -1)
-        goto error;
-
-    if ((req->thandle = alloc_handle( current->process, thread,
-                                      THREAD_ALL_ACCESS, req->tinherit )) == -1)
-        goto error;
-
-    /* thread object will be released when the thread gets killed */
-    set_reply_fd( current, sock[1] );
-    req->pid = get_process_id( thread->process );
-    req->tid = get_thread_id( thread );
-    req->phandle = phandle;
-    req->event = event;
-    return;
-
- error:
-    if (phandle != -1) close_handle( current->process, phandle );
-    if (event != -1) close_handle( current->process, event );
-    if (thread) release_object( thread );
-    close( sock[1] );
+/* Wait for the new process to start */
+DECL_HANDLER(wait_process)
+{
+    if (!current->info)
+    {
+        fatal_protocol_error( current, "wait_process: no process is being created\n" );
+        return;
+    }
+    req->pid     = 0;
+    req->tid     = 0;
+    req->phandle = -1;
+    req->thandle = -1;
+    if (req->cancel)
+    {
+        release_object( current->info );
+        current->info = NULL;
+    }
+    else
+    {
+        struct object *obj = &current->info->obj;
+        sleep_on( 1, &obj, SELECT_TIMEOUT, req->timeout, build_wait_process_reply );
+    }
 }
 
 /* initialize a new process */
 DECL_HANDLER(init_process)
 {
-    struct new_process_request *info;
-
     if (!current->unix_pid)
     {
         fatal_protocol_error( current, "init_process: init_thread not called yet\n" );
         return;
     }
-    if (!(info = current->process->info))
-    {
-        fatal_protocol_error( current, "init_process: called twice\n" );
-        return;
-    }
     current->process->ldt_copy  = req->ldt_copy;
     current->process->ldt_flags = req->ldt_flags;
-    current->process->info = NULL;
-    req->exe_file    = -1;
-    req->start_flags = info->start_flags;
-    req->hstdin      = info->hstdin;
-    req->hstdout     = info->hstdout;
-    req->hstderr     = info->hstderr;
-    req->cmd_show    = info->cmd_show;
-    req->env_ptr     = info->env_ptr;
-    strcpy( req->cmdline, info->cmdline );
-    if (current->process->exe.file)
-        req->exe_file = alloc_handle( current->process, current->process->exe.file,
-                                      GENERIC_READ, 0 );
-    free( info );
+    init_process( req->ppid, req );
 }
 
 /* signal the end of the process initialization */