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

diff --git a/scheduler/process.c b/scheduler/process.c
index b7cd744..8bdf146 100644
--- a/scheduler/process.c
+++ b/scheduler/process.c
@@ -5,6 +5,8 @@
  */
 
 #include <assert.h>
+#include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -44,31 +46,6 @@
 
 
 /***********************************************************************
- *           PROCESS_WalkProcess
- */
-void PROCESS_WalkProcess(void)
-{
-    PDB  *pdb;
-    char *name;
-
-    pdb = PROCESS_First;
-    MESSAGE( " pid        PDB         #th  modref     module \n" );
-    while(pdb)
-    {
-        if (pdb == &initial_pdb)
-            name = "initial PDB";
-        else
-            name = (pdb->exe_modref) ? pdb->exe_modref->filename : "";
-
-        MESSAGE( " %8p %8p %5d  %8p %s\n", pdb->server_pid, pdb,
-               pdb->threads, pdb->exe_modref, name);
-        pdb = pdb->next;
-    }
-    return;
-}
-
-
-/***********************************************************************
  *           PROCESS_IdToPDB
  *
  * Convert a process id to a PDB, making sure it is valid.
@@ -210,27 +187,15 @@
 static BOOL PROCESS_CreateEnvDB(void)
 {
     struct init_process_request *req = get_req_buffer();
-    STARTUPINFOA *startup;
-    ENVDB *env_db;
-    char cmd_line[4096];
     PDB *pdb = PROCESS_Current();
-
-    /* Allocate the env DB */
-
-    if (!(env_db = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ENVDB) )))
-        return FALSE;
-    pdb->env_db = env_db;
-    InitializeCriticalSection( &env_db->section );
-
-    /* Allocate and fill the startup info */
-    if (!(startup = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(STARTUPINFOA) )))
-        return FALSE;
-    env_db->startup_info = startup;
+    ENVDB *env_db = pdb->env_db;
+    STARTUPINFOA *startup = env_db->startup_info;
 
     /* Retrieve startup info from the server */
 
     req->ldt_copy  = ldt_copy;
     req->ldt_flags = ldt_flags_copy;
+    req->ppid      = getppid();
     if (server_call( REQ_INIT_PROCESS )) return FALSE;
     pdb->exe_file        = req->exe_file;
     startup->dwFlags     = req->start_flags;
@@ -238,16 +203,6 @@
     env_db->hStdin  = startup->hStdInput  = req->hstdin;
     env_db->hStdout = startup->hStdOutput = req->hstdout;
     env_db->hStderr = startup->hStdError  = req->hstderr;
-    lstrcpynA( cmd_line, req->cmdline, sizeof(cmd_line) );
-
-    /* Copy the parent environment */
-
-    if (!ENV_InheritEnvironment( req->env_ptr )) return FALSE;
-
-    /* Copy the command line */
-
-    if (!(pdb->env_db->cmd_line = HEAP_strdupA( GetProcessHeap(), 0, cmd_line )))
-        return FALSE;
 
     return TRUE;
 }
@@ -277,7 +232,8 @@
  */
 static PDB *PROCESS_CreatePDB( PDB *parent, BOOL inherit )
 {
-    PDB *pdb = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PDB) );
+    PDB *pdb = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                          sizeof(PDB) + sizeof(ENVDB) + sizeof(STARTUPINFOA) );
 
     if (!pdb) return NULL;
     pdb->exit_code       = STILL_ACTIVE;
@@ -291,6 +247,11 @@
     pdb->next            = PROCESS_First;
     pdb->winver          = 0xffff; /* to be determined */
     pdb->main_queue      = INVALID_HANDLE_VALUE16;
+    pdb->env_db          = (ENVDB *)(pdb + 1);
+    pdb->env_db->startup_info = (STARTUPINFOA *)(pdb->env_db + 1);
+
+    InitializeCriticalSection( &pdb->env_db->section );
+
     PROCESS_First = pdb;
     return pdb;
 }
@@ -303,10 +264,6 @@
 {
     struct init_process_request *req;
     TEB *teb;
-    int server_fd;
-
-    /* Start the server */
-    server_fd = CLIENT_InitServer();
 
     /* Fill the initial process structure */
     initial_pdb.exit_code       = STILL_ACTIVE;
@@ -320,12 +277,31 @@
     initial_pdb.winver          = 0xffff; /* to be determined */
     initial_pdb.main_queue      = INVALID_HANDLE_VALUE16;
     initial_envdb.startup_info  = &initial_startup;
+    teb = THREAD_Init( &initial_pdb );
+
+    /* Setup the server connection */
+    teb->socket = CLIENT_InitServer();
+    if (CLIENT_InitThread()) return FALSE;
 
     /* Initialize virtual memory management */
     if (!VIRTUAL_Init()) return FALSE;
 
-    /* Create the initial thread structure and socket pair */
-    if (!(teb = THREAD_CreateInitialThread( &initial_pdb, server_fd ))) return FALSE;
+    /* Retrieve startup info from the server */
+    req = get_req_buffer();
+    req->ldt_copy  = ldt_copy;
+    req->ldt_flags = ldt_flags_copy;
+    req->ppid      = getppid();
+    if (server_call( REQ_INIT_PROCESS )) return FALSE;
+    initial_pdb.exe_file        = req->exe_file;
+    initial_startup.dwFlags     = req->start_flags;
+    initial_startup.wShowWindow = req->cmd_show;
+    initial_envdb.hStdin   = initial_startup.hStdInput  = req->hstdin;
+    initial_envdb.hStdout  = initial_startup.hStdOutput = req->hstdout;
+    initial_envdb.hStderr  = initial_startup.hStdError  = req->hstderr;
+    initial_envdb.cmd_line = "";
+
+    /* Initialize signal handling */
+    if (!SIGNAL_Init()) return FALSE;
 
     /* Remember TEB selector of initial process for emergency use */
     SYSLEVEL_EmergencyTeb = teb->teb_sel;
@@ -342,24 +318,8 @@
     initial_pdb.idle_event = CreateEventA ( NULL, TRUE, FALSE, NULL );
     initial_pdb.idle_event = ConvertToGlobalHandle ( initial_pdb.idle_event );
 
-    /* Initialize signal handling */
-    if (!SIGNAL_Init()) return FALSE;
-
-    /* Retrieve startup info from the server */
-    req = get_req_buffer();
-    req->ldt_copy  = ldt_copy;
-    req->ldt_flags = ldt_flags_copy;
-    if (server_call( REQ_INIT_PROCESS )) return FALSE;
-    initial_pdb.exe_file        = req->exe_file;
-    initial_startup.dwFlags     = req->start_flags;
-    initial_startup.wShowWindow = req->cmd_show;
-    initial_envdb.hStdin   = initial_startup.hStdInput  = req->hstdin;
-    initial_envdb.hStdout  = initial_startup.hStdOutput = req->hstdout;
-    initial_envdb.hStderr  = initial_startup.hStdError  = req->hstderr;
-    initial_envdb.cmd_line = "";
-
     /* Copy the parent environment */
-    if (!ENV_InheritEnvironment( NULL )) return FALSE;
+    if (!ENV_BuildEnvironment()) return FALSE;
 
     /* Create the SEGPTR heap */
     if (!(SegptrHeap = HeapCreate( HEAP_WINE_SEGPTR, 0, 0 ))) return FALSE;
@@ -373,6 +333,33 @@
 
 
 /***********************************************************************
+ *           load_system_dlls
+ *
+ * Load system DLLs into the initial process (and initialize them)
+ */
+static int load_system_dlls(void)
+{
+    char driver[MAX_PATH];
+
+    if (!LoadLibraryA( "KERNEL32" )) return 0;
+
+    PROFILE_GetWineIniString( "Wine", "GraphicsDriver", "x11drv", driver, sizeof(driver) );
+    if (!LoadLibraryA( driver ))
+    {
+        MESSAGE( "Could not load graphics driver '%s'\n", driver );
+        return 0;
+    }
+
+    if (!LoadLibraryA("GDI32.DLL")) return 0;
+    if (!LoadLibrary16("GDI.EXE")) return 0;
+    if (!LoadLibrary16("USER.EXE")) return 0;
+    if (!LoadLibraryA("USER32.DLL")) return 0;
+
+    return 1;
+}
+
+
+/***********************************************************************
  *           start_process
  *
  * Startup routine of a new Win32 process. Runs on the new process stack.
@@ -409,13 +396,8 @@
 
     if (pdb->flags & PDB32_CONSOLE_PROC) AllocConsole();
 
-    /* Load system DLLs into the initial process (and initialize them) */
-    if (!LoadLibraryA( "KERNEL32" )) goto error;
-    if (!LoadLibraryA( "x11drv" )) goto error;
-
-    if (   !LoadLibrary16("GDI.EXE" ) || !LoadLibraryA("GDI32.DLL" )
-        || !LoadLibrary16("USER.EXE") || !LoadLibraryA("USER32.DLL"))
-        goto error;
+    /* Load the system dlls */
+    if (!load_system_dlls()) goto error;
 
     /* Get pointers to USER routines called by KERNEL */
     THUNK_InitCallout();
@@ -548,6 +530,250 @@
 
 
 /***********************************************************************
+ *           build_argv
+ *
+ * Build an argv array from a command-line.
+ * The command-line is modified to insert nulls.
+ */
+static char **build_argv( char *cmdline, char *argv0 )
+{
+    char **argv;
+    int count = 1;
+    char *p = cmdline;
+
+    while (*p)
+    {
+        while (*p && isspace(*p)) p++;
+        if (!*p) break;
+        count++;
+        while (*p && !isspace(*p)) p++;
+    }
+    if (argv0) count++;
+    if ((argv = malloc( count * sizeof(*argv) )))
+    {
+        char **argvptr = argv;
+        if (argv0) *argvptr++ = argv0;
+        p = cmdline;
+        while (*p)
+        {
+            while (*p && isspace(*p)) *p++ = 0;
+            if (!*p) break;
+            *argvptr++ = p;
+            while (*p && !isspace(*p)) p++;
+        }
+        *argvptr = 0;
+    }
+    return argv;
+}
+
+
+/***********************************************************************
+ *           build_envp
+ *
+ * Build the environment of a new child process.
+ */
+static char **build_envp( const char *env )
+{
+    const char *p;
+    char **envp;
+    int count;
+
+    for (p = env, count = 0; *p; count++) p += strlen(p) + 1;
+    count += 3;
+    if ((envp = malloc( count * sizeof(*envp) )))
+    {
+        extern char **environ;
+        char **envptr = envp;
+        char **unixptr = environ;
+        /* first put PATH, HOME and WINEPREFIX from the unix env */
+        for (unixptr = environ; unixptr && *unixptr; unixptr++)
+            if (!memcmp( *unixptr, "PATH=", 5 ) ||
+                !memcmp( *unixptr, "HOME=", 5 ) ||
+                !memcmp( *unixptr, "WINEPREFIX=", 11 )) *envptr++ = *unixptr;
+        /* now put the Windows environment strings */
+        for (p = env; *p; p += strlen(p) + 1)
+        {
+            if (memcmp( p, "PATH=", 5 ) &&
+                memcmp( p, "HOME=", 5 ) &&
+                memcmp( p, "WINEPREFIX=", 11 )) *envptr++ = (char *)p;
+        }
+        *envptr = 0;
+    }
+    return envp;
+}
+
+
+/***********************************************************************
+ *           find_wine_binary
+ *
+ * Locate the Wine binary to exec for a new Win32 process.
+ */
+static void exec_wine_binary( char **argv, char **envp )
+{
+    const char *path, *pos, *ptr;
+
+    /* first try bin directory */
+    argv[0] = BINDIR "/wine";
+    execve( argv[0], argv, envp );
+
+    /* now try the path of argv0 of the current binary */
+    if (!(argv[0] = malloc( strlen(argv0) + 6 ))) return;
+    if ((ptr = strrchr( argv0, '/' )))
+    {
+        memcpy( argv[0], argv0, ptr - argv0 );
+        strcpy( argv[0] + (ptr - argv0), "/wine" );
+        execve( argv[0], argv, envp );
+    }
+    free( argv[0] );
+
+    /* now search in the Unix path */
+    if ((path = getenv( "PATH" )))
+    {
+        if (!(argv[0] = malloc( strlen(path) + 6 ))) return;
+        pos = path;
+        for (;;)
+        {
+            while (*pos == ':') pos++;
+            if (!*pos) break;
+            if (!(ptr = strchr( pos, ':' ))) ptr = pos + strlen(pos);
+            memcpy( argv[0], pos, ptr - pos );
+            strcpy( argv[0] + (ptr - pos), "/wine" );
+            execve( argv[0], argv, envp );
+            pos = ptr;
+        }
+    }
+    free( argv[0] );
+
+    /* finally try the current directory */
+    argv[0] = "./wine";
+    execve( argv[0], argv, envp );
+}
+
+
+/***********************************************************************
+ *           fork_and_exec
+ *
+ * Fork and exec a new Unix process, checking for errors.
+ */
+static int fork_and_exec( const char *filename, const char *cmdline, const char *env )
+{
+    int fd[2];
+    int pid, err;
+
+    if (pipe(fd) == -1)
+    {
+        FILE_SetDosError();
+        return -1;
+    }
+    fcntl( fd[1], F_SETFD, 1 );  /* set close on exec */
+    if (!(pid = fork()))  /* child */
+    {
+        char **argv = build_argv( (char *)cmdline, NULL );
+        char **envp = build_envp( env );
+        close( fd[0] );
+        if (argv && envp) execve( filename, argv, envp );
+        err = errno;
+        write( fd[1], &err, sizeof(err) );
+        _exit(1);
+    }
+    close( fd[1] );
+    if ((pid != -1) && (read( fd[0], &err, sizeof(err) ) > 0))  /* exec failed */
+    {
+        errno = err;
+        pid = -1;
+    }
+    if (pid == -1) FILE_SetDosError();
+    close( fd[0] );
+    return pid;
+}
+
+
+/***********************************************************************
+ *           PROCESS_CreateUnixProcess
+ */
+BOOL PROCESS_CreateUnixProcess( LPCSTR filename, LPCSTR cmd_line, LPCSTR env, 
+                                LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
+                                BOOL inherit, DWORD flags, LPSTARTUPINFOA startup,
+                                LPPROCESS_INFORMATION info )
+{
+    int pid;
+    const char *unixfilename = filename;
+    DOS_FULL_NAME full_name;
+    HANDLE load_done_evt = -1;
+    struct new_process_request *req = get_req_buffer();
+    struct wait_process_request *wait_req = get_req_buffer();
+
+    info->hThread = info->hProcess = INVALID_HANDLE_VALUE;
+    
+    if (DOSFS_GetFullName( filename, TRUE, &full_name )) unixfilename = full_name.long_name;
+
+    /* create the process on the server side */
+
+    req->inherit_all  = inherit;
+    req->create_flags = flags;
+    req->start_flags  = startup->dwFlags;
+    req->exe_file     = -1;
+    if (startup->dwFlags & STARTF_USESTDHANDLES)
+    {
+        req->hstdin  = startup->hStdInput;
+        req->hstdout = startup->hStdOutput;
+        req->hstderr = startup->hStdError;
+    }
+    else
+    {
+        req->hstdin  = GetStdHandle( STD_INPUT_HANDLE );
+        req->hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
+        req->hstderr = GetStdHandle( STD_ERROR_HANDLE );
+    }
+    req->cmd_show = startup->wShowWindow;
+    req->alloc_fd = 0;
+    if (server_call( REQ_NEW_PROCESS )) return FALSE;
+
+    /* fork and execute */
+
+    pid = fork_and_exec( unixfilename, cmd_line, env ? env : GetEnvironmentStringsA() );
+
+    wait_req->cancel   = (pid == -1);
+    wait_req->pinherit = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
+    wait_req->tinherit = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
+    wait_req->timeout  = 2000;
+    if (server_call( REQ_WAIT_PROCESS ) || (pid == -1)) goto error;
+    info->dwProcessId = (DWORD)wait_req->pid;
+    info->dwThreadId  = (DWORD)wait_req->tid;
+    info->hProcess    = wait_req->phandle;
+    info->hThread     = wait_req->thandle;
+    load_done_evt     = wait_req->event;
+
+    /* Wait until process is initialized (or initialization failed) */
+    if (load_done_evt != -1)
+    {
+        DWORD res;
+        HANDLE handles[2];
+
+        handles[0] = info->hProcess;
+        handles[1] = load_done_evt;
+        res = WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
+        CloseHandle( load_done_evt );
+        if (res == STATUS_WAIT_0)  /* the process died */
+        {
+            DWORD exitcode;
+            if (GetExitCodeProcess( info->hProcess, &exitcode )) SetLastError( exitcode );
+            CloseHandle( info->hThread );
+            CloseHandle( info->hProcess );
+            return FALSE;
+        }
+    }
+    return TRUE;
+
+error:
+    if (load_done_evt != -1) CloseHandle( load_done_evt );
+    if (info->hThread != INVALID_HANDLE_VALUE) CloseHandle( info->hThread );
+    if (info->hProcess != INVALID_HANDLE_VALUE) CloseHandle( info->hProcess );
+    return FALSE;
+}
+
+
+/***********************************************************************
  *           PROCESS_Start
  *
  * Startup routine of a new process. Called in the context of the new process.
@@ -691,6 +917,7 @@
     BOOL alloc_stack16;
     int fd = -1;
     struct new_process_request *req = get_req_buffer();
+    struct wait_process_request *wait_req = get_req_buffer();
     TEB *teb = NULL;
     PDB *parent = PROCESS_Current();
     PDB *pdb = PROCESS_CreatePDB( parent, inherit );
@@ -698,10 +925,11 @@
     if (!pdb) return NULL;
     info->hThread = info->hProcess = INVALID_HANDLE_VALUE;
     
+    if (!(pdb->env_db->cmd_line = HEAP_strdupA( GetProcessHeap(), 0, cmd_line ))) goto error;
+    if (!ENV_InheritEnvironment( pdb, env ? env : GetEnvironmentStringsA() )) goto error;
+
     /* Create the process on the server side */
 
-    req->pinherit     = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
-    req->tinherit     = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
     req->inherit_all  = 2 /*inherit*/;  /* HACK! */
     req->create_flags = flags;
     req->start_flags  = startup->dwFlags;
@@ -719,15 +947,8 @@
         req->hstderr = GetStdHandle( STD_ERROR_HANDLE );
     }
     req->cmd_show = startup->wShowWindow;
-    req->env_ptr = (void*)env;  /* FIXME: hack */
-    lstrcpynA( req->cmdline, cmd_line, server_remaining(req->cmdline) );
+    req->alloc_fd = 1;
     if (server_call_fd( REQ_NEW_PROCESS, -1, &fd )) goto error;
-    pdb->server_pid   = req->pid;
-    info->hProcess    = req->phandle;
-    info->dwProcessId = (DWORD)req->pid;
-    info->hThread     = req->thandle;
-    info->dwThreadId  = (DWORD)req->tid;
-    load_done_evt     = req->event;
 
     if (pModule->module32)   /* Win32 process */
     {
@@ -752,13 +973,26 @@
 
     /* Create the main thread */
 
-    if (!(teb = THREAD_Create( pdb, req->pid, req->tid, fd, size, alloc_stack16 ))) goto error;
-    teb->startup = PROCESS_Start;
-    fd = -1;  /* don't close it */
+    if ((teb = THREAD_Create( pdb, fd, size, alloc_stack16 )))
+    {
+        teb->startup = PROCESS_Start;
+        fd = -1;  /* don't close it */
 
-    /* Pass module to new process (FIXME: hack) */
-    pdb->module = pModule->self;
-    SYSDEPS_SpawnThread( teb );
+        /* Pass module to new process (FIXME: hack) */
+        pdb->module = pModule->self;
+        SYSDEPS_SpawnThread( teb );
+    }
+
+    wait_req->cancel   = !teb;
+    wait_req->pinherit = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
+    wait_req->tinherit = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
+    wait_req->timeout  = 2000;
+    if (server_call( REQ_WAIT_PROCESS ) || !teb) goto error;
+    info->dwProcessId = (DWORD)wait_req->pid;
+    info->dwThreadId  = (DWORD)wait_req->tid;
+    info->hProcess    = wait_req->phandle;
+    info->hThread     = wait_req->thandle;
+    load_done_evt     = wait_req->event;
 
     /* Wait until process is initialized (or initialization failed) */
     handles[0] = info->hProcess;