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;