Made the server listen for new clients on a Unix socket in
$HOME/.wine. Newly started wine processes now attach to an existing
server if one is running.

diff --git a/scheduler/client.c b/scheduler/client.c
index 5ccf04f..e8dda19 100644
--- a/scheduler/client.c
+++ b/scheduler/client.c
@@ -7,6 +7,7 @@
 #include "config.h"
 
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -15,9 +16,11 @@
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
+#include <sys/un.h>
 #ifdef HAVE_SYS_MMAN_H
 #include <sys/mman.h>
 #endif
+#include <sys/stat.h>
 #include <sys/uio.h>
 #include <unistd.h>
 #include <stdarg.h>
@@ -26,12 +29,17 @@
 #include "thread.h"
 #include "server.h"
 #include "winerror.h"
+#include "options.h"
+#include "xmalloc.h"
 
 /* Some versions of glibc don't define this */
 #ifndef SCM_RIGHTS
 #define SCM_RIGHTS 1
 #endif
 
+#define SERVERDIR  "/wineserver-"  /* server socket directory (hostname appended) */
+#define SOCKETNAME "socket"        /* name of the socket file */
+
 /* data structure used to pass an fd with sendmsg/recvmsg */
 struct cmsg_fd
 {
@@ -41,6 +49,34 @@
     int fd;    /* fd to pass */
 };
 
+static void *boot_thread_id;
+
+
+/* die on a fatal error; use only during initialization */
+static void fatal_error( const char *err, ... )
+{
+    va_list args;
+
+    va_start( args, err );
+    fprintf( stderr, "wine: " );
+    vfprintf( stderr, err, args );
+    va_end( args );
+    exit(1);
+}
+
+/* die on a fatal error; use only during initialization */
+static void fatal_perror( const char *err, ... )
+{
+    va_list args;
+
+    va_start( args, err );
+    fprintf( stderr, "wine: " );
+    vfprintf( stderr, err, args );
+    perror( " " );
+    va_end( args );
+    exit(1);
+}
+
 /***********************************************************************
  *           CLIENT_Die
  *
@@ -258,40 +294,138 @@
 
 
 /***********************************************************************
+ *           start_server
+ *
+ * Start a new wine server.
+ */
+static void start_server( const char *oldcwd )
+{
+    static int started;  /* we only try once */
+    if (!started)
+    {
+        int pid = fork();
+        if (pid == -1) fatal_perror( "fork" );
+        if (!pid)
+        {
+            execl( BINDIR "/wineserver", "wineserver", NULL );
+            if (oldcwd) chdir( oldcwd );
+            execlp( "wineserver", "wineserver", NULL );
+            execl( "./server/wineserver", "wineserver", NULL );
+            fatal_error( "could not exec wineserver\n" );
+        }
+        started = 1;
+    }
+}
+
+/***********************************************************************
+ *           server_connect
+ *
+ * Attempt to connect to an existing server socket.
+ * We need to be in the server directory already.
+ */
+static int server_connect( const char *oldcwd, const char *serverdir )
+{
+    struct sockaddr_un addr;
+    struct stat st;
+    int s;
+
+    if (chdir( serverdir ) == -1)
+    {
+        if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir );
+        start_server( NULL );
+        return -1;
+    }
+    if (stat( ".", &st ) == -1) fatal_perror( "stat %s", serverdir );
+    if (st.st_uid != getuid()) fatal_error( "'%s' is not owned by you\n", serverdir );
+    if (st.st_mode & 077) fatal_error( "'%s' must not be accessible by other users\n", serverdir );
+
+    if (lstat( SOCKETNAME, &st ) == -1)
+    {
+        if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
+        start_server( oldcwd );
+        return -1;
+    }
+    if (!S_ISSOCK(st.st_mode))
+        fatal_error( "'%s/%s' is not a socket\n", serverdir, SOCKETNAME );
+    if (st.st_uid != getuid())
+        fatal_error( "'%s/%s' is not owned by you\n", serverdir, SOCKETNAME );
+
+    if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
+    addr.sun_family = AF_UNIX;
+    strcpy( addr.sun_path, SOCKETNAME );
+    if (connect( s, &addr, sizeof(addr.sun_family) + strlen(addr.sun_path) ) == -1)
+    {
+        close( s );
+        return -2;
+    }
+    fcntl( s, F_SETFD, 1 ); /* set close on exec flag */
+    return s;
+}
+
+
+/***********************************************************************
  *           CLIENT_InitServer
  *
  * Start the server and create the initial socket pair.
  */
 int CLIENT_InitServer(void)
 {
-    int fd[2];
-    char buffer[16];
-    extern void create_initial_thread( int fd );
+    int delay, fd, size;
+    const char *env_fd;
+    char hostname[64];
+    char *oldcwd, *serverdir;
+    const char *configdir;
 
-    if (socketpair( AF_UNIX, SOCK_STREAM, 0, fd ) == -1)
+    /* first check if we inherited the socket fd */
+    if ((env_fd = getenv( "__WINE_FD" )) && isdigit(env_fd[0]))
     {
-        perror("socketpair");
-        exit(1);
+        fd = atoi( env_fd );
+        if (fcntl( fd, F_GETFL, 0 ) != -1) return fd;
     }
-    switch(fork())
+
+    /* retrieve the current directory */
+    for (size = 512; ; size *= 2)
     {
-    case -1:  /* error */
-        perror("fork");
-        exit(1);
-    case 0:  /* child */
-        close( fd[1] );
-        fcntl( fd[0], F_SETFD, 1 ); /* set close on exec flag */
+        oldcwd = xmalloc( size );
+        if (getcwd( oldcwd, size )) break;
+        free( oldcwd );
+        if (errno == ERANGE) continue;
+        oldcwd = NULL;
         break;
-    default:  /* parent */
-        close( fd[0] );
-        sprintf( buffer, "%d", fd[1] );
-        execl( BINDIR "/wineserver", "wineserver", buffer, NULL );
-        execlp( "wineserver", "wineserver", buffer, NULL );
-        execl( "./server/wineserver", "wineserver", buffer, NULL );
-        create_initial_thread( fd[1] );
-        exit(0);
     }
-    return fd[0];
+
+    /* get the server directory name */
+    if (gethostname( hostname, sizeof(hostname) ) == -1) fatal_perror( "gethostname" );
+    configdir = PROFILE_GetConfigDir();
+    serverdir = xmalloc( strlen(configdir) + strlen(SERVERDIR) + strlen(hostname) + 1 );
+    strcpy( serverdir, configdir );
+    strcat( serverdir, SERVERDIR );
+    strcat( serverdir, hostname );
+
+    /* try to connect, leaving some time for the server to start up */
+    for (delay = 10000; delay < 500000; delay += delay / 2)
+    {
+        if ((fd = server_connect( oldcwd, serverdir )) >= 0) goto done;
+        usleep( delay );
+    }
+
+    if (fd == -2)
+    {
+        fatal_error( "'%s/%s' exists,\n"
+                     "   but I cannot connect to it; maybe the server has crashed?\n"
+                     "   If this is the case, you should remove the socket file and try again.\n",
+                     serverdir, SOCKETNAME );
+    }
+    fatal_error( "could not start wineserver, giving up\n" );
+
+ done:
+    /* switch back to the starting directory */
+    if (oldcwd)
+    {
+        chdir( oldcwd );
+        free( oldcwd );
+    }
+    return fd;
 }
 
 
@@ -319,20 +453,32 @@
     if (server_call( REQ_INIT_THREAD )) return -1;
     teb->process->server_pid = req->pid;
     teb->tid = req->tid;
+    if (req->boot) boot_thread_id = req->tid;
+    else if (boot_thread_id == req->tid) boot_thread_id = 0;
     return 0;
 }
 
+/***********************************************************************
+ *           CLIENT_BootDone
+ *
+ * Signal that we have finished booting, and set debug level.
+ */
+int CLIENT_BootDone( int debug_level )
+{
+    struct boot_done_request *req = get_req_buffer();
+    req->debug_level = debug_level;
+    return server_call( REQ_BOOT_DONE );
+}
+
 
 /***********************************************************************
- *           CLIENT_SetDebug
+ *           CLIENT_IsBootThread
  *
- * Send a set debug level request. Return 0 if OK.
+ * Return TRUE if current thread is the boot thread.
  */
-int CLIENT_SetDebug( int level )
+int CLIENT_IsBootThread(void)
 {
-    struct set_debug_request *req = get_req_buffer();
-    req->level = level;
-    return server_call( REQ_SET_DEBUG );
+    return (GetCurrentThreadId() == (DWORD)boot_thread_id);
 }
 
 /***********************************************************************
@@ -346,4 +492,3 @@
     req->op = op;
     return server_call( REQ_DEBUGGER );
 }
-
diff --git a/scheduler/process.c b/scheduler/process.c
index d9ff8fd..89671aa 100644
--- a/scheduler/process.c
+++ b/scheduler/process.c
@@ -5,6 +5,7 @@
  */
 
 #include <assert.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -241,13 +242,13 @@
 
     /* Allocate the env DB */
 
-    if (!(env_db = HeapAlloc( pdb->heap, HEAP_ZERO_MEMORY, sizeof(ENVDB) )))
+    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( pdb->heap, HEAP_ZERO_MEMORY, sizeof(STARTUPINFOA) )))
+    if (!(startup = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(STARTUPINFOA) )))
         return FALSE;
     env_db->startup_info = startup;
 
@@ -263,11 +264,11 @@
 
     /* Copy the parent environment */
 
-    if (!ENV_InheritEnvironment( pdb, req->env_ptr )) return FALSE;
+    if (!ENV_InheritEnvironment( req->env_ptr )) return FALSE;
 
     /* Copy the command line */
 
-    if (!(pdb->env_db->cmd_line = HEAP_strdupA( pdb->heap, 0, cmd_line )))
+    if (!(pdb->env_db->cmd_line = HEAP_strdupA( GetProcessHeap(), 0, cmd_line )))
         return FALSE;
 
     return TRUE;
@@ -286,7 +287,6 @@
     ENV_FreeEnvironment( pdb );
     while (*pptr && (*pptr != pdb)) pptr = &(*pptr)->next;
     if (*pptr) *pptr = pdb->next;
-    if (pdb->heap && (pdb->heap != pdb->system_heap)) HeapDestroy( pdb->heap );
     HeapFree( SystemHeap, 0, pdb );
 }
 
@@ -306,11 +306,9 @@
     pdb->threads         = 1;
     pdb->running_threads = 1;
     pdb->ring0_threads   = 1;
-    pdb->system_heap     = SystemHeap;
     pdb->parent          = parent;
     pdb->group           = pdb;
     pdb->priority        = 8;  /* Normal */
-    pdb->heap            = pdb->system_heap;  /* will be changed later on */
     pdb->next            = PROCESS_First;
     pdb->winver          = 0xffff; /* to be determined */
     pdb->main_queue      = INVALID_HANDLE_VALUE16;
@@ -350,9 +348,9 @@
     /* Remember TEB selector of initial process for emergency use */
     SYSLEVEL_EmergencyTeb = teb->teb_sel;
 
-    /* Create the system heap */
-    if (!(SystemHeap = HeapCreate( HEAP_GROWABLE, 0x10000, 0 ))) return FALSE;
-    initial_pdb.system_heap = initial_pdb.heap = SystemHeap;
+    /* Create the system and process heaps */
+    if (!HEAP_CreateSystemHeap()) return FALSE;
+    initial_pdb.heap = HeapCreate( HEAP_GROWABLE, 0, 0 );
 
     /* Create the idle event for the initial process
        FIXME 1: Shouldn't we call UserSignalProc for the initial process too?
@@ -406,11 +404,13 @@
     InitializeCriticalSection( &pdb->crit_section );
 
     /* Create the heap */
-    if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, 
-                                  header? header->SizeOfHeapReserve : 0x10000,
-                                  header? header->SizeOfHeapCommit  : 0 ))) 
-        goto error;
-    pdb->heap_list = pdb->heap;
+    if (!(pdb->heap = GetProcessHeap()))
+    {
+        if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, 
+                                      header? header->SizeOfHeapReserve : 0x10000,
+                                      header? header->SizeOfHeapCommit  : 0 ))) 
+            goto error;
+    }
 
     /* Create the environment db */
     if (!PROCESS_CreateEnvDB()) goto error;
@@ -492,7 +492,8 @@
     }
 
     /* If requested, add entry point breakpoint */
-    if ( Options.debug && TASK_AddTaskEntryBreakpoint )
+    if ( (Options.debug && TASK_AddTaskEntryBreakpoint) ||
+         (pdb->flags & PDB32_DEBUGGED))
         TASK_AddTaskEntryBreakpoint( pdb->task );
 
     /* Call UserSignalProc ( USIG_PROCESS_RUNNING ... ) only for non-GUI win32 apps */
@@ -515,6 +516,7 @@
 
     case PROC_WIN32:
         TRACE_(relay)( "Starting Win32 process (entryproc=%p)\n", entry );
+        if (pdb->flags & PDB32_DEBUGGED) DebugBreak();
         ExitProcess( entry(NULL) );
     }
 
@@ -536,7 +538,7 @@
     HANDLE handles[2], load_done_evt = 0;
     DWORD exitcode, size;
     BOOL alloc_stack16;
-    int server_thandle;
+    int server_thandle, fd = -1;
     struct new_process_request *req = get_req_buffer();
     TEB *teb = NULL;
     PDB *parent = PROCESS_Current();
@@ -568,12 +570,15 @@
     req->cmd_show = startup->wShowWindow;
     req->env_ptr = (void*)env;  /* FIXME: hack */
     lstrcpynA( req->cmdline, cmd_line, server_remaining(req->cmdline) );
-    if (server_call( REQ_NEW_PROCESS )) goto error;
+    if (server_call_fd( REQ_NEW_PROCESS, -1, &fd )) goto error;
+    fcntl( fd, F_SETFD, 1 ); /* set close on exec flag */
     pdb->server_pid   = req->pid;
-    info->hProcess    = req->handle;
-    info->dwProcessId = (DWORD)pdb->server_pid;
+    info->hProcess    = req->phandle;
+    info->dwProcessId = (DWORD)req->pid;
+    info->hThread     = req->thandle;
+    info->dwThreadId  = (DWORD)req->tid;
 
-    if ((flags & DEBUG_PROCESS) ||
+    if ((flags & (DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS)) ||
         ((parent->flags & PDB32_DEBUGGED) && !(flags & DEBUG_ONLY_THIS_PROCESS)))
         pdb->flags |= PDB32_DEBUGGED;
 
@@ -600,11 +605,11 @@
 
     /* Create the main thread */
 
-    if (!(teb = THREAD_Create( pdb, flags & CREATE_SUSPENDED, size,
+    if (!(teb = THREAD_Create( pdb, fd, flags & CREATE_SUSPENDED, size,
                                alloc_stack16, tsa, &server_thandle ))) goto error;
-    info->hThread     = server_thandle;
-    info->dwThreadId  = (DWORD)teb->tid;
+    teb->tid = (void *)info->dwThreadId;
     teb->startup = PROCESS_Start;
+    fd = -1;  /* don't close it */
 
     /* Pass module to new process (FIXME: hack) */
     pdb->module = pModule->self;
@@ -650,6 +655,7 @@
     if (info->hThread != INVALID_HANDLE_VALUE) CloseHandle( info->hThread );
     if (info->hProcess != INVALID_HANDLE_VALUE) CloseHandle( info->hProcess );
     PROCESS_FreePDB( pdb );
+    if (fd != -1) close( fd );
     return NULL;
 }
 
@@ -853,16 +859,6 @@
 
 
 /***********************************************************************
- *           GetProcessHeap    (KERNEL32.259)
- */
-HANDLE WINAPI GetProcessHeap(void)
-{
-    PDB *pdb = PROCESS_Current();
-    return pdb->heap ? pdb->heap : SystemHeap;
-}
-
-
-/***********************************************************************
  *           GetThreadLocale    (KERNEL32.295)
  */
 LCID WINAPI GetThreadLocale(void)
@@ -1237,22 +1233,6 @@
 
 
 /***********************************************************************
- * GetProcessHeaps [KERNEL32.376]
- */
-DWORD WINAPI GetProcessHeaps(DWORD nrofheaps,HANDLE *heaps) {
-	FIXME_(win32)("(%ld,%p), incomplete implementation.\n",nrofheaps,heaps);
-
-	if (nrofheaps) {
-		heaps[0] = GetProcessHeap();
-		/* ... probably SystemHeap too ? */
-		return 1;
-	}
-	/* number of available heaps */
-	return 1;
-}
-
-
-/***********************************************************************
  *           SetErrorMode   (KERNEL32.486)
  */
 UINT WINAPI SetErrorMode( UINT mode )
diff --git a/scheduler/thread.c b/scheduler/thread.c
index 4038a8a..247d4b5 100644
--- a/scheduler/thread.c
+++ b/scheduler/thread.c
@@ -212,8 +212,8 @@
  *	allocate in this area and don't support a granularity of 4kb
  *	yet we leave it to VirtualAlloc to choose an address.
  */
-TEB *THREAD_Create( PDB *pdb, DWORD flags, DWORD stack_size, BOOL alloc_stack16,
-                     LPSECURITY_ATTRIBUTES sa, int *server_handle )
+TEB *THREAD_Create( PDB *pdb, int fd, DWORD flags, DWORD stack_size, BOOL alloc_stack16,
+                    LPSECURITY_ATTRIBUTES sa, int *server_handle )
 {
     struct new_thread_request *req = get_req_buffer();
     HANDLE cleanup_object;
@@ -227,7 +227,7 @@
     teb->tls_ptr     = teb->tls_array;
     teb->process     = pdb;
     teb->exit_code   = 0x103; /* STILL_ACTIVE */
-    teb->socket      = -1;
+    teb->socket      = fd;
 
     /* Allocate the TEB selector (%fs register) */
 
@@ -237,13 +237,15 @@
 
     /* Create the thread on the server side */
 
-    req->pid     = teb->process->server_pid;
-    req->suspend = ((flags & CREATE_SUSPENDED) != 0);
-    req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
-    if (server_call_fd( REQ_NEW_THREAD, -1, &teb->socket )) goto error;
-    teb->tid = req->tid;
-    *server_handle = req->handle;
-    fcntl( teb->socket, F_SETFD, 1 ); /* set close on exec flag */
+    if (teb->socket == -1)
+    {
+        req->suspend = ((flags & CREATE_SUSPENDED) != 0);
+        req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
+        if (server_call_fd( REQ_NEW_THREAD, -1, &teb->socket )) goto error;
+        teb->tid = req->tid;
+        *server_handle = req->handle;
+        fcntl( teb->socket, F_SETFD, 1 ); /* set close on exec flag */
+    }
 
     /* Do the rest of the initialization */
 
@@ -295,7 +297,7 @@
                             DWORD flags, LPDWORD id )
 {
     int handle = -1;
-    TEB *teb = THREAD_Create( PROCESS_Current(), flags, stack, TRUE, sa, &handle );
+    TEB *teb = THREAD_Create( PROCESS_Current(), -1, flags, stack, TRUE, sa, &handle );
     if (!teb) return 0;
     teb->flags      |= TEBF_WIN32;
     teb->entry_point = start;