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/Makefile.in b/Makefile.in
index bd2af5e..385f8d6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -100,7 +100,6 @@
relay32 \
resources \
scheduler \
- server \
win32 \
windows \
windows/ttydrv
@@ -112,7 +111,8 @@
EMUSUBDIRS = \
debugger \
- miscemu
+ miscemu \
+ server
PROGSUBDIRS = libtest programs
@@ -213,7 +213,6 @@
relay32/relay32.o \
resources/resources.o \
scheduler/scheduler.o \
- server/server.o \
win32/win32.o \
windows/windows.o \
windows/ttydrv/ttydrv.o
@@ -231,7 +230,7 @@
ALT_LINK = @ALT_LINK@
-all: Makefile Make.rules $(MAIN_TARGET)
+all: Makefile Make.rules server $(MAIN_TARGET)
@echo "Wine build complete."
LIBLINTS = $(LIBOBJS:.o=.ln)
diff --git a/include/server.h b/include/server.h
index 96685b0..30d4e97 100644
--- a/include/server.h
+++ b/include/server.h
@@ -121,7 +121,9 @@
IN int cmd_show; /* main window show mode */
IN void* env_ptr; /* pointer to environment (FIXME: hack) */
OUT void* pid; /* process id */
- OUT int handle; /* process handle (in the current process) */
+ OUT int phandle; /* process handle (in the current process) */
+ OUT void* tid; /* thread id */
+ OUT int thandle; /* thread handle (in the current process) */
IN char cmdline[1]; /* command line */
};
@@ -129,7 +131,6 @@
/* Create a new thread from the context of the parent */
struct new_thread_request
{
- IN void* pid; /* process id for the new thread */
IN int suspend; /* new thread should be suspended on creation */
IN int inherit; /* inherit flag */
OUT void* tid; /* thread id */
@@ -137,10 +138,10 @@
};
-/* Set the server debug level */
-struct set_debug_request
+/* Signal that we are finished booting on the client side */
+struct boot_done_request
{
- IN int level; /* New debug level */
+ IN int debug_level; /* new debug level */
};
@@ -171,6 +172,7 @@
IN void* teb; /* TEB of new thread (in thread address space) */
OUT void* pid; /* process id of the new thread's process */
OUT void* tid; /* thread id of the new thread */
+ OUT int boot; /* is this the boot thread? */
};
@@ -1036,7 +1038,7 @@
{
REQ_NEW_PROCESS,
REQ_NEW_THREAD,
- REQ_SET_DEBUG,
+ REQ_BOOT_DONE,
REQ_INIT_PROCESS,
REQ_INIT_PROCESS_DONE,
REQ_INIT_THREAD,
@@ -1189,7 +1191,8 @@
}
extern int CLIENT_InitServer(void);
-extern int CLIENT_SetDebug( int level );
+extern int CLIENT_BootDone( int debug_level );
+extern int CLIENT_IsBootThread(void);
extern int CLIENT_DebuggerRequest( int op );
extern int CLIENT_InitThread(void);
#endif /* __WINE_SERVER__ */
diff --git a/include/thread.h b/include/thread.h
index a9255f5..a08dc18 100644
--- a/include/thread.h
+++ b/include/thread.h
@@ -121,7 +121,7 @@
/* scheduler/thread.c */
extern TEB *THREAD_CreateInitialThread( struct _PDB *pdb, int server_fd );
-extern TEB *THREAD_Create( struct _PDB *pdb, DWORD flags,
+extern TEB *THREAD_Create( struct _PDB *pdb, int fd, DWORD flags,
DWORD stack_size, BOOL alloc_stack16,
LPSECURITY_ATTRIBUTES sa, int *server_handle );
extern BOOL THREAD_IsWin16( TEB *thdb );
diff --git a/loader/main.c b/loader/main.c
index 9be7476..9d6b9ad 100644
--- a/loader/main.c
+++ b/loader/main.c
@@ -67,24 +67,27 @@
/* Parse command line arguments */
MAIN_WineInit( argc, argv );
- /* Set server debug level */
- CLIENT_SetDebug( TRACE_ON(server) );
-
/* Load the configuration file */
if (!PROFILE_LoadWineIni()) return FALSE;
- /* Initialize module loadorder */
- if (!MODULE_InitLoadOrder()) return FALSE;
-
- /* Initialize DOS memory */
- if (!DOSMEM_Init(0)) return FALSE;
-
/* Initialise DOS drives */
if (!DRIVE_Init()) return FALSE;
/* Initialise DOS directories */
if (!DIR_Init()) return FALSE;
+ /* Registry initialisation */
+ SHELL_LoadRegistry();
+
+ /* Global boot finished, the rest is process-local */
+ CLIENT_BootDone( TRACE_ON(server) );
+
+ /* Initialize module loadorder */
+ if (!MODULE_InitLoadOrder()) return FALSE;
+
+ /* Initialize DOS memory */
+ if (!DOSMEM_Init(0)) return FALSE;
+
/* Initialize event handling */
if (!EVENT_Init()) return FALSE;
@@ -94,9 +97,6 @@
/* Initialize IO-port permissions */
IO_port_init();
- /* registry initialisation */
- SHELL_LoadRegistry();
-
/* Read DOS config.sys */
if (!DOSCONF_ReadConfig()) return FALSE;
diff --git a/misc/registry.c b/misc/registry.c
index e6c9b17..fbd806c 100644
--- a/misc/registry.c
+++ b/misc/registry.c
@@ -1486,6 +1486,8 @@
TRACE("(void)\n");
+ if (!CLIENT_IsBootThread()) return; /* already loaded */
+
REGISTRY_Init();
SetLoadLevel(0);
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;
diff --git a/server/Makefile.in b/server/Makefile.in
index e2bad9f..e849135 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -1,9 +1,9 @@
-DEFS = @DLLFLAGS@ -D__WINE__ -D__WINE_SERVER__
+DEFS = -D__WINE__ -D__WINE_SERVER__
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ..
SRCDIR = @srcdir@
VPATH = @srcdir@
-MODULE = server
+MODULE = none
C_SRCS = \
change.c \
@@ -14,6 +14,7 @@
event.c \
file.c \
handle.c \
+ main.c \
mapping.c \
mutex.c \
object.c \
@@ -31,16 +32,13 @@
trace.c \
unicode.c
-EXTRA_SRCS = main.c
-MAIN_OBJS = main.o
-
PROGRAMS = wineserver
-all: $(MODULE).o $(PROGRAMS)
+all: $(PROGRAMS)
@MAKE_RULES@
-wineserver: $(OBJS) $(MAIN_OBJS)
- $(CC) -o $(PROGRAMS) $(OBJS) $(MAIN_OBJS) $(LIBS)
+wineserver: $(OBJS)
+ $(CC) -o $(PROGRAMS) $(OBJS) $(LIBS)
### Dependencies:
diff --git a/server/main.c b/server/main.c
index 51490ce..7ec67d7 100644
--- a/server/main.c
+++ b/server/main.c
@@ -4,39 +4,98 @@
* Copyright (C) 1998 Alexandre Julliard
*/
+#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/time.h>
#include <unistd.h>
#include "object.h"
#include "thread.h"
+#include "request.h"
+
+/* command-line options */
+int debug_level = 0;
+int persistent_server = 0;
+
+/* parse-line args */
+/* FIXME: should probably use getopt, and add a help option */
+static void parse_args( int argc, char *argv[] )
+{
+ int i;
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch(argv[i][1])
+ {
+ case 'd':
+ if (isdigit(argv[i][2])) debug_level = atoi( argv[i] + 2 );
+ else debug_level++;
+ break;
+ case 'p':
+ persistent_server = 1;
+ break;
+ default:
+ fprintf( stderr, "Unknown option '%s'\n", argv[i] );
+ exit(1);
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Unknown argument '%s'\n", argv[i] );
+ exit(1);
+ }
+ }
+}
+
+static void sigterm_handler()
+{
+ exit(1); /* make sure atexit functions get called */
+}
+
+/* initialize signal handling */
+static void signal_init(void)
+{
+#if 0
+ if (!debug_level)
+ {
+ switch(fork())
+ {
+ case -1:
+ break;
+ case 0:
+ setsid();
+ break;
+ default:
+ exit(0);
+ }
+ }
+#endif
+ signal( SIGPIPE, SIG_IGN );
+ signal( SIGHUP, sigterm_handler );
+ signal( SIGINT, sigterm_handler );
+ signal( SIGQUIT, sigterm_handler );
+ signal( SIGTERM, sigterm_handler );
+ signal( SIGABRT, sigterm_handler );
+}
int main( int argc, char *argv[] )
{
- int fd;
-
- if (argc != 2) goto error;
- if (!isdigit( *argv[1] )) goto error;
- fd = atoi( argv[1] );
- /* make sure the fd is valid */
- if (fcntl( fd, F_GETFL, 0 ) == -1) goto error;
-
-/* debug_level = 1; */
+ parse_args( argc, argv );
+ signal_init();
+ open_master_socket();
if (debug_level) fprintf( stderr, "Server: starting (pid=%ld)\n", (long) getpid() );
- create_initial_thread( fd );
+ select_loop();
if (debug_level) fprintf( stderr, "Server: exiting (pid=%ld)\n", (long) getpid() );
- close_registry();
#ifdef DEBUG_OBJECTS
+ close_registry();
dump_objects(); /* dump any remaining objects */
#endif
-
exit(0);
-
- error:
- fprintf( stderr, "%s: must be run from Wine.\n", argv[0] );
- exit(1);
}
diff --git a/server/object.c b/server/object.c
index 71e9451..4ade006 100644
--- a/server/object.c
+++ b/server/object.c
@@ -15,7 +15,6 @@
#include "thread.h"
#include "unicode.h"
-int debug_level = 0;
struct object_name
{
diff --git a/server/object.h b/server/object.h
index fb874f7..5ab85a1 100644
--- a/server/object.h
+++ b/server/object.h
@@ -165,6 +165,9 @@
extern void close_registry(void);
+/* global variables (command-line options) */
+
extern int debug_level;
+extern int persistent_server;
#endif /* __WINE_SERVER_OBJECT_H */
diff --git a/server/process.c b/server/process.c
index b2a53e4..b2743e1 100644
--- a/server/process.c
+++ b/server/process.c
@@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
+#include <sys/socket.h>
#include <unistd.h>
#include "winbase.h"
@@ -50,14 +51,89 @@
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 )
+{
+ 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->inherit = 0;
+ req->inherit_all = 0;
+ req->create_flags = CREATE_NEW_CONSOLE;
+ req->start_flags = STARTF_USESTDHANDLES;
+ req->hstdin = -1;
+ req->hstdout = -1;
+ req->hstderr = -1;
+ req->event = -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;
+}
-/* create a new process */
-static struct process *create_process( struct process *parent, struct new_process_request *req,
- const char *cmd_line, size_t len )
+/* set the console and stdio handles for a newly created process */
+static int set_process_console( struct process *process, struct process *parent )
+{
+ 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))
+ {
+ if (parent->console_in) process->console_in = grab_object( parent->console_in );
+ if (parent->console_out) process->console_out = grab_object( parent->console_out );
+ }
+ if (parent)
+ {
+ 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 );
+ }
+ }
+ 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 );
+ }
+ 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 process *process;
+ struct thread *thread = NULL;
- if (!(process = alloc_object( &process_ops, -1 ))) return NULL;
+ if (!(process = alloc_object( &process_ops, -1 )))
+ {
+ close( fd );
+ return NULL;
+ }
process->next = NULL;
process->prev = NULL;
process->thread_list = NULL;
@@ -74,16 +150,13 @@
process->init_event = NULL;
process->info = NULL;
gettimeofday( &process->start_time, NULL );
+ if ((process->next = first_process) != NULL) process->next->prev = process;
+ first_process = process;
/* copy the request structure */
- if (!(process->info = mem_alloc( sizeof(*process->info) + len ))) goto error;
- memcpy( process->info, req, sizeof(*req) );
- memcpy( process->info->cmdline, cmd_line, len );
- process->info->cmdline[len] = 0;
- req = process->info; /* use the copy now */
- process->create_flags = req->create_flags;
+ if (!set_creation_info( process, req, cmd_line, len )) goto error;
- if (req->inherit_all)
+ if (process->info->inherit_all)
process->handles = copy_handle_table( process, parent );
else
process->handles = alloc_handle_table( process, 0 );
@@ -93,32 +166,18 @@
alloc_handle( process, process, PROCESS_ALL_ACCESS, 0 );
/* get the init done event */
- if (req->event != -1)
+ if (process->info->event != -1)
{
- if (!(process->init_event = get_event_obj( parent, req->event, EVENT_MODIFY_STATE )))
- goto error;
+ if (!(process->init_event = get_event_obj( parent, process->info->event,
+ EVENT_MODIFY_STATE ))) goto error;
}
/* set the process console */
- if (process->create_flags & CREATE_NEW_CONSOLE)
- {
- if (!alloc_console( process )) goto error;
- }
- else if (!(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 );
- }
+ if (!set_process_console( process, parent )) goto error;
- if (!req->inherit_all && !(req->start_flags & STARTF_USESTDHANDLES))
- {
- process->info->hstdin = duplicate_handle( parent, req->hstdin, process,
- 0, TRUE, DUPLICATE_SAME_ACCESS );
- process->info->hstdout = duplicate_handle( parent, req->hstdout, process,
- 0, TRUE, DUPLICATE_SAME_ACCESS );
- process->info->hstderr = duplicate_handle( parent, req->hstderr, process,
- 0, TRUE, DUPLICATE_SAME_ACCESS );
- }
+ /* create the main thread */
+ if (!(thread = create_thread( fd, process, (process->create_flags & CREATE_SUSPENDED) != 0)))
+ goto error;
/* attach to the debugger if requested */
if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
@@ -126,45 +185,17 @@
else if (parent && parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
debugger_attach( process, parent->debugger );
- if ((process->next = first_process) != NULL) process->next->prev = process;
- first_process = process;
- return process;
+ release_object( process );
+ return thread;
error:
+ close( fd );
free_console( process );
if (process->handles) release_object( process->handles );
release_object( process );
return NULL;
}
-/* create the initial process */
-struct process *create_initial_process(void)
-{
- struct process *process;
- struct new_process_request req;
-
- req.inherit = 0;
- req.inherit_all = 0;
- req.create_flags = CREATE_NEW_CONSOLE;
- req.start_flags = STARTF_USESTDHANDLES;
- req.hstdin = -1;
- req.hstdout = -1;
- req.hstderr = -1;
- req.event = -1;
- req.cmd_show = 0;
- req.env_ptr = NULL;
- if ((process = create_process( NULL, &req, "", 1 )))
- {
- process->info->hstdin = alloc_handle( process, process->console_in,
- GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
- process->info->hstdout = alloc_handle( process, process->console_out,
- GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
- process->info->hstderr = alloc_handle( process, process->console_out,
- GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
- }
- return process;
-}
-
/* destroy a process when its refcount is 0 */
static void process_destroy( struct object *obj )
{
@@ -229,6 +260,8 @@
{
/* last process died, close global handles */
close_global_handles();
+ /* this will cause the select loop to terminate */
+ if (!persistent_server) close_master_socket();
}
}
@@ -455,16 +488,40 @@
DECL_HANDLER(new_process)
{
size_t len = get_req_strlen( req->cmdline );
- struct process *process;
+ struct thread *thread;
+ int sock[2];
- req->handle = -1;
- req->pid = NULL;
- if ((process = create_process( current->process, req, req->cmdline, len )))
+ req->phandle = -1;
+ req->thandle = -1;
+ req->pid = NULL;
+ req->tid = NULL;
+
+ if (socketpair( AF_UNIX, SOCK_STREAM, 0, sock ) == -1)
{
- req->handle = alloc_handle( current->process, process, PROCESS_ALL_ACCESS, req->inherit );
- req->pid = process;
- release_object( process );
+ file_set_error();
+ return;
}
+
+ if ((thread = create_process( sock[0], current->process, req, req->cmdline, len )))
+ {
+ int phandle = alloc_handle( current->process, thread->process,
+ PROCESS_ALL_ACCESS, req->inherit );
+ if ((req->phandle = phandle) != -1)
+ {
+ if ((req->thandle = alloc_handle( current->process, thread,
+ THREAD_ALL_ACCESS, req->inherit )) != -1)
+ {
+ /* thread object will be released when the thread gets killed */
+ set_reply_fd( current, sock[1] );
+ req->pid = thread->process;
+ req->tid = thread;
+ return;
+ }
+ close_handle( current->process, phandle );
+ }
+ release_object( thread );
+ }
+ close( sock[1] );
}
/* initialize a new process */
diff --git a/server/process.h b/server/process.h
index 305d1c5..737de56 100644
--- a/server/process.h
+++ b/server/process.h
@@ -47,7 +47,9 @@
/* process functions */
-extern struct process *create_initial_process(void);
+extern struct thread *create_process( int fd, struct process *parent,
+ struct new_process_request *req,
+ const char *cmd_line, size_t len );
extern struct process *get_process_from_id( void *id );
extern struct process *get_process_from_handle( int handle, unsigned int access );
extern int process_set_debugger( struct process *process, struct thread *thread );
diff --git a/server/request.c b/server/request.c
index 945126b..b7a5161 100644
--- a/server/request.c
+++ b/server/request.c
@@ -9,22 +9,27 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#include <sys/uio.h>
+#include <sys/un.h>
#include <unistd.h>
#include "winnt.h"
#include "winbase.h"
#include "wincon.h"
#include "thread.h"
+#include "process.h"
#include "server.h"
#define WANT_REQUEST_HANDLERS
#include "request.h"
@@ -34,9 +39,41 @@
#define SCM_RIGHTS 1
#endif
-
+ /* path names for server master Unix socket */
+#define CONFDIR "/.wine" /* directory for Wine config relative to $HOME */
+#define SERVERDIR "/wineserver-" /* server socket directory (hostname appended) */
+#define SOCKETNAME "socket" /* name of the socket file */
+
+struct master_socket
+{
+ struct object obj; /* object header */
+};
+
+static void master_socket_dump( struct object *obj, int verbose );
+static void master_socket_poll_event( struct object *obj, int event );
+static void master_socket_destroy( struct object *obj );
+
+static const struct object_ops master_socket_ops =
+{
+ sizeof(struct master_socket), /* size */
+ master_socket_dump, /* dump */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ NULL, /* get_poll_events */
+ master_socket_poll_event, /* poll_event */
+ no_read_fd, /* get_read_fd */
+ no_write_fd, /* get_write_fd */
+ no_flush, /* flush */
+ no_get_file_info, /* get_file_info */
+ master_socket_destroy /* destroy */
+};
+
+
struct thread *current = NULL; /* thread handling the current request */
+static struct master_socket *master_socket; /* the master socket object */
/* socket communication static structures */
static struct iovec myiovec;
@@ -64,6 +101,31 @@
kill_thread( thread, PROTOCOL_ERROR );
}
+/* die on a fatal error */
+static void fatal_error( const char *err, ... )
+{
+ va_list args;
+
+ va_start( args, err );
+ fprintf( stderr, "wineserver: " );
+ vfprintf( stderr, err, args );
+ va_end( args );
+ exit(1);
+}
+
+/* die on a fatal error */
+static void fatal_perror( const char *err, ... )
+{
+ va_list args;
+
+ va_start( args, err );
+ fprintf( stderr, "wineserver: " );
+ vfprintf( stderr, err, args );
+ perror( " " );
+ va_end( args );
+ exit(1);
+}
+
/* call a request handler */
static void call_req_handler( struct thread *thread, enum request req, int fd )
{
@@ -187,12 +249,135 @@
return 1;
}
-/* set the debug level */
-DECL_HANDLER(set_debug)
+static void master_socket_dump( struct object *obj, int verbose )
{
- debug_level = req->level;
- /* Make sure last_req is initialized */
- current->last_req = REQ_SET_DEBUG;
+ struct master_socket *sock = (struct master_socket *)obj;
+ assert( obj->ops == &master_socket_ops );
+ fprintf( stderr, "Master socket fd=%d\n", sock->obj.fd );
+}
+
+/* handle a socket event */
+static void master_socket_poll_event( struct object *obj, int event )
+{
+ struct master_socket *sock = (struct master_socket *)obj;
+ assert( obj->ops == &master_socket_ops );
+
+ assert( sock == master_socket ); /* there is only one master socket */
+
+ if (event & (POLLERR | POLLHUP))
+ {
+ /* this is not supposed to happen */
+ fprintf( stderr, "wineserver: Error on master socket\n" );
+ release_object( obj );
+ }
+ else if (event & POLLIN)
+ {
+ struct sockaddr_un dummy;
+ int len = sizeof(dummy);
+ int client = accept( master_socket->obj.fd, &dummy, &len );
+ if (client != -1) create_process( client, NULL, NULL, "", 1 );
+ }
+}
+
+/* remove the socket upon exit */
+static void socket_cleanup(void)
+{
+ unlink( SOCKETNAME );
+}
+
+static void master_socket_destroy( struct object *obj )
+{
+ socket_cleanup();
+}
+
+/* return the configuration directory ($HOME/.wine) */
+static const char *get_config_dir(void)
+{
+ static char *confdir;
+ if (!confdir)
+ {
+ const char *home = getenv( "HOME" );
+ if (!home)
+ {
+ struct passwd *pwd = getpwuid( getuid() );
+ if (!pwd) fatal_error( "could not find your home directory\n" );
+ home = pwd->pw_dir;
+ }
+ if (!(confdir = malloc( strlen(home) + strlen(CONFDIR) + 1 )))
+ fatal_error( "out of memory\n" );
+ strcpy( confdir, home );
+ strcat( confdir, CONFDIR );
+ mkdir( confdir, 0755 ); /* just in case */
+ }
+ return confdir;
+}
+
+/* create the server directory and chdir to it */
+static void create_server_dir(void)
+{
+ char hostname[64];
+ char *serverdir;
+ const char *confdir = get_config_dir();
+ struct stat st;
+
+ if (gethostname( hostname, sizeof(hostname) ) == -1) fatal_perror( "gethostname" );
+
+ if (!(serverdir = malloc( strlen(confdir) + strlen(SERVERDIR) + strlen(hostname) + 1 )))
+ fatal_error( "out of memory\n" );
+
+ strcpy( serverdir, confdir );
+ strcat( serverdir, SERVERDIR );
+ strcat( serverdir, hostname );
+
+ if (chdir( serverdir ) == -1)
+ {
+ if (errno != ENOENT) fatal_perror( "chdir %s", serverdir );
+ if (mkdir( serverdir, 0700 ) == -1) fatal_perror( "mkdir %s", serverdir );
+ if (chdir( serverdir ) == -1) fatal_perror( "chdir %s", serverdir );
+ }
+ if (stat( ".", &st ) == -1) fatal_perror( "stat %s", serverdir );
+ if (!S_ISDIR(st.st_mode)) fatal_error( "%s is not a directory\n", 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 );
+}
+
+/* open the master server socket and start waiting for new clients */
+void open_master_socket(void)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ create_server_dir();
+ if ((fd = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
+ addr.sun_family = AF_UNIX;
+ strcpy( addr.sun_path, "socket" );
+ if (bind( fd, &addr, sizeof(addr.sun_family) + strlen(addr.sun_path) ) == -1)
+ {
+ if ((errno == EEXIST) || (errno == EADDRINUSE))
+ fatal_error( "another server is already running\n" );
+ else
+ fatal_perror( "bind" );
+ }
+ atexit( socket_cleanup );
+
+ chmod( "socket", 0600 ); /* make sure no other user can connect */
+ if (listen( fd, 5 ) == -1) fatal_perror( "listen" );
+
+ if (!(master_socket = alloc_object( &master_socket_ops, fd )))
+ fatal_error( "out of memory\n" );
+ set_select_events( &master_socket->obj, POLLIN );
+}
+
+/* close the master socket and stop waiting for new clients */
+void close_master_socket(void)
+{
+ release_object( master_socket );
+}
+
+/* lock/unlock the master socket to stop accepting new clients */
+void lock_master_socket( int locked )
+{
+ set_select_events( &master_socket->obj, locked ? 0 : POLLIN );
}
/* debugger support operations */
diff --git a/server/request.h b/server/request.h
index 80ad9aa..c9fcd10 100644
--- a/server/request.h
+++ b/server/request.h
@@ -31,6 +31,9 @@
extern void fatal_protocol_error( struct thread *thread, const char *err, ... );
extern void set_reply_fd( struct thread *thread, int pass_fd );
extern void send_reply( struct thread *thread );
+extern void open_master_socket(void);
+extern void close_master_socket(void);
+extern void lock_master_socket( int locked );
extern void trace_request( enum request req, int fd );
extern void trace_kill( struct thread *thread );
@@ -69,7 +72,7 @@
DECL_HANDLER(new_process);
DECL_HANDLER(new_thread);
-DECL_HANDLER(set_debug);
+DECL_HANDLER(boot_done);
DECL_HANDLER(init_process);
DECL_HANDLER(init_process_done);
DECL_HANDLER(init_thread);
@@ -168,7 +171,7 @@
} req_handlers[REQ_NB_REQUESTS] = {
{ (void(*)())req_new_process, sizeof(struct new_process_request) },
{ (void(*)())req_new_thread, sizeof(struct new_thread_request) },
- { (void(*)())req_set_debug, sizeof(struct set_debug_request) },
+ { (void(*)())req_boot_done, sizeof(struct boot_done_request) },
{ (void(*)())req_init_process, sizeof(struct init_process_request) },
{ (void(*)())req_init_process_done, sizeof(struct init_process_done_request) },
{ (void(*)())req_init_thread, sizeof(struct init_thread_request) },
diff --git a/server/select.c b/server/select.c
index 98b4817..c46ccd6 100644
--- a/server/select.c
+++ b/server/select.c
@@ -210,9 +210,6 @@
sigset_t sigset;
struct sigaction action;
- setsid();
- signal( SIGPIPE, SIG_IGN );
-
/* block the signals we use */
sigemptyset( &sigset );
sigaddset( &sigset, SIGCHLD );
diff --git a/server/thread.c b/server/thread.c
index f162918..d2c5a56 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -87,6 +87,7 @@
};
static struct thread *first_thread;
+static struct thread *booting_thread;
/* allocate the buffer for the communication with the client */
static int alloc_client_buffer( struct thread *thread )
@@ -106,7 +107,7 @@
}
/* create a new thread */
-static struct thread *create_thread( int fd, struct process *process, int suspend )
+struct thread *create_thread( int fd, struct process *process, int suspend )
{
struct thread *thread;
int buf_fd;
@@ -135,14 +136,15 @@
thread->suspend = (suspend != 0);
thread->buffer = (void *)-1;
thread->last_req = REQ_GET_THREAD_BUFFER;
+ thread->process = (struct process *)grab_object( process );
- if (!first_thread) /* creating the first thread */
+ if (!current) current = thread;
+
+ if (!booting_thread) /* first thread ever */
{
- current = thread;
- thread->process = process = create_initial_process();
- assert( process );
+ booting_thread = thread;
+ lock_master_socket(1);
}
- else thread->process = (struct process *)grab_object( process );
if ((thread->next = first_thread) != NULL) thread->next->prev = thread;
first_thread = thread;
@@ -161,13 +163,6 @@
return NULL;
}
-/* create the initial thread and start the main server loop */
-void create_initial_thread( int fd )
-{
- create_thread( fd, NULL, 0 );
- select_loop();
-}
-
/* handle a client event */
void thread_poll_event( struct object *obj, int event )
{
@@ -543,25 +538,34 @@
release_object( thread );
}
+/* signal that we are finished booting on the client side */
+DECL_HANDLER(boot_done)
+{
+ debug_level = req->debug_level;
+ /* Make sure last_req is initialized */
+ current->last_req = REQ_BOOT_DONE;
+ if (current == booting_thread)
+ {
+ booting_thread = (struct thread *)~0UL; /* make sure it doesn't match other threads */
+ lock_master_socket(0); /* allow other clients now */
+ }
+}
+
/* create a new thread */
DECL_HANDLER(new_thread)
{
struct thread *thread;
- struct process *process;
int sock[2];
- if (!(process = get_process_from_id( req->pid ))) return;
-
if (socketpair( AF_UNIX, SOCK_STREAM, 0, sock ) != -1)
{
- if ((thread = create_thread( sock[0], process, req->suspend )))
+ if ((thread = create_thread( sock[0], current->process, req->suspend )))
{
req->tid = thread;
if ((req->handle = alloc_handle( current->process, thread,
THREAD_ALL_ACCESS, req->inherit )) != -1)
{
set_reply_fd( current, sock[1] );
- release_object( process );
/* thread object will be released when the thread gets killed */
return;
}
@@ -570,7 +574,6 @@
close( sock[1] );
}
else file_set_error();
- release_object( process );
}
/* retrieve the thread buffer file descriptor */
@@ -590,8 +593,9 @@
current->unix_pid = req->unix_pid;
current->teb = req->teb;
if (current->suspend + current->process->suspend > 0) stop_thread( current );
- req->pid = current->process;
- req->tid = current;
+ req->pid = current->process;
+ req->tid = current;
+ req->boot = (current == booting_thread);
}
/* terminate a thread */
diff --git a/server/thread.h b/server/thread.h
index c19b9d4..5e46493 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -62,7 +62,7 @@
/* thread functions */
-extern void create_initial_thread( int fd );
+extern struct thread *create_thread( int fd, struct process *process, int suspend );
extern struct thread *get_thread_from_id( void *id );
extern struct thread *get_thread_from_handle( int handle, unsigned int access );
extern struct thread *get_thread_from_pid( int pid );
diff --git a/server/trace.c b/server/trace.c
index f140afb..89547b5 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -215,12 +215,13 @@
static void dump_new_process_reply( const struct new_process_request *req )
{
fprintf( stderr, " pid=%p,", req->pid );
- fprintf( stderr, " handle=%d", req->handle );
+ fprintf( stderr, " phandle=%d,", req->phandle );
+ fprintf( stderr, " tid=%p,", req->tid );
+ fprintf( stderr, " thandle=%d", req->thandle );
}
static void dump_new_thread_request( const struct new_thread_request *req )
{
- fprintf( stderr, " pid=%p,", req->pid );
fprintf( stderr, " suspend=%d,", req->suspend );
fprintf( stderr, " inherit=%d", req->inherit );
}
@@ -231,9 +232,9 @@
fprintf( stderr, " handle=%d", req->handle );
}
-static void dump_set_debug_request( const struct set_debug_request *req )
+static void dump_boot_done_request( const struct boot_done_request *req )
{
- fprintf( stderr, " level=%d", req->level );
+ fprintf( stderr, " debug_level=%d", req->debug_level );
}
static void dump_init_process_request( const struct init_process_request *req )
@@ -265,7 +266,8 @@
static void dump_init_thread_reply( const struct init_thread_request *req )
{
fprintf( stderr, " pid=%p,", req->pid );
- fprintf( stderr, " tid=%p", req->tid );
+ fprintf( stderr, " tid=%p,", req->tid );
+ fprintf( stderr, " boot=%d", req->boot );
}
static void dump_get_thread_buffer_request( const struct get_thread_buffer_request *req )
@@ -1168,7 +1170,7 @@
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_new_process_request,
(dump_func)dump_new_thread_request,
- (dump_func)dump_set_debug_request,
+ (dump_func)dump_boot_done_request,
(dump_func)dump_init_process_request,
(dump_func)dump_init_process_done_request,
(dump_func)dump_init_thread_request,
@@ -1358,7 +1360,7 @@
static const char * const req_names[REQ_NB_REQUESTS] = {
"new_process",
"new_thread",
- "set_debug",
+ "boot_done",
"init_process",
"init_process_done",
"init_thread",