From 2fe57779fb6adb7508be26f52ff0a9c226e43178 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 25 Jan 2000 01:40:27 +0000 Subject: [PATCH] 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. --- Makefile.in | 7 +- include/server.h | 17 ++-- include/thread.h | 2 +- loader/main.c | 24 +++--- misc/registry.c | 2 + scheduler/client.c | 203 +++++++++++++++++++++++++++++++++++++------- scheduler/process.c | 80 +++++++---------- scheduler/thread.c | 24 +++--- server/Makefile.in | 14 ++- server/main.c | 91 ++++++++++++++++---- server/object.c | 1 - server/object.h | 3 + server/process.c | 197 +++++++++++++++++++++++++++--------------- server/process.h | 4 +- server/request.c | 197 ++++++++++++++++++++++++++++++++++++++++-- server/request.h | 7 +- server/select.c | 3 - server/thread.c | 46 +++++----- server/thread.h | 2 +- server/trace.c | 16 ++-- 20 files changed, 690 insertions(+), 250 deletions(-) diff --git a/Makefile.in b/Makefile.in index bd2af5e527e..385f8d61582 100644 --- a/Makefile.in +++ b/Makefile.in @@ -100,7 +100,6 @@ LIBSUBDIRS = \ relay32 \ resources \ scheduler \ - server \ win32 \ windows \ windows/ttydrv @@ -112,7 +111,8 @@ X11SUBDIRS = \ EMUSUBDIRS = \ debugger \ - miscemu + miscemu \ + server PROGSUBDIRS = libtest programs @@ -213,7 +213,6 @@ LIBOBJS = \ 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 @@ LIB_TARGET = @LIB_TARGET@ 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 96685b0cfd4..30d4e9748d3 100644 --- a/include/server.h +++ b/include/server.h @@ -121,7 +121,9 @@ struct new_process_request 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 @@ struct new_process_request /* 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 @@ struct new_thread_request }; -/* 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 @@ struct init_thread_request 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 @@ enum request { 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 @@ static inline void server_strcpyAtoW( WCHAR *dst, const char *src ) } 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 a9255f5e097..a08dc18f8c9 100644 --- a/include/thread.h +++ b/include/thread.h @@ -121,7 +121,7 @@ typedef struct _TEB /* 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 9be7476970b..9d6b9adb327 100644 --- a/loader/main.c +++ b/loader/main.c @@ -67,24 +67,27 @@ BOOL MAIN_MainInit( int *argc, char *argv[] ) /* 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 @@ BOOL MAIN_MainInit( int *argc, char *argv[] ) /* 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 e6c9b176515..fbd806cfdb3 100644 --- a/misc/registry.c +++ b/misc/registry.c @@ -1486,6 +1486,8 @@ void SHELL_LoadRegistry( void ) TRACE("(void)\n"); + if (!CLIENT_IsBootThread()) return; /* already loaded */ + REGISTRY_Init(); SetLoadLevel(0); diff --git a/scheduler/client.c b/scheduler/client.c index 5ccf04f7a79..e8dda1996bb 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -7,6 +7,7 @@ #include "config.h" #include +#include #include #include #include @@ -15,9 +16,11 @@ #ifdef HAVE_SYS_SOCKET_H # include #endif +#include #ifdef HAVE_SYS_MMAN_H #include #endif +#include #include #include #include @@ -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 @@ struct cmsg_fd 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 * @@ -257,6 +293,76 @@ unsigned int server_call_fd( enum request req, int fd_out, int *fd_in ) } +/*********************************************************************** + * 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 * @@ -264,34 +370,62 @@ unsigned int server_call_fd( enum request req, int fd_out, int *fd_in ) */ 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 @@ int CLIENT_InitThread(void) 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 @@ int CLIENT_DebuggerRequest( int op ) req->op = op; return server_call( REQ_DEBUGGER ); } - diff --git a/scheduler/process.c b/scheduler/process.c index d9ff8fd1291..89671aa1fd3 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -241,13 +242,13 @@ static BOOL PROCESS_CreateEnvDB(void) /* 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 @@ static BOOL PROCESS_CreateEnvDB(void) /* 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 @@ void PROCESS_FreePDB( PDB *pdb ) 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 @@ static PDB *PROCESS_CreatePDB( PDB *parent, BOOL inherit ) 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 @@ BOOL PROCESS_Init(void) /* 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 @@ void PROCESS_Start(void) 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 @@ void PROCESS_Start(void) } /* 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 @@ void PROCESS_Start(void) 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 @@ PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env, 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 @@ PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env, 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 @@ PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env, /* 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 @@ error: 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; } @@ -852,16 +858,6 @@ DWORD WINAPI GetCurrentProcessId(void) } -/*********************************************************************** - * GetProcessHeap (KERNEL32.259) - */ -HANDLE WINAPI GetProcessHeap(void) -{ - PDB *pdb = PROCESS_Current(); - return pdb->heap ? pdb->heap : SystemHeap; -} - - /*********************************************************************** * GetThreadLocale (KERNEL32.295) */ @@ -1236,22 +1232,6 @@ BOOL WINAPI GetExitCodeProcess( } -/*********************************************************************** - * 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) */ diff --git a/scheduler/thread.c b/scheduler/thread.c index 4038a8a92aa..247d4b580e8 100644 --- a/scheduler/thread.c +++ b/scheduler/thread.c @@ -212,8 +212,8 @@ TEB *THREAD_CreateInitialThread( PDB *pdb, int server_fd ) * 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 *THREAD_Create( PDB *pdb, DWORD flags, DWORD stack_size, BOOL alloc_stack16, 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 @@ TEB *THREAD_Create( PDB *pdb, DWORD flags, DWORD stack_size, BOOL alloc_stack16, /* 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 @@ HANDLE WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, DWORD stack, 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 e2bad9f3a62..e849135d373 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 @@ C_SRCS = \ event.c \ file.c \ handle.c \ + main.c \ mapping.c \ mutex.c \ object.c \ @@ -31,16 +32,13 @@ C_SRCS = \ 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 51490ce63e7..7ec67d712d4 100644 --- a/server/main.c +++ b/server/main.c @@ -4,39 +4,98 @@ * Copyright (C) 1998 Alexandre Julliard */ +#include #include #include +#include #include #include +#include #include #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 71e9451f51d..4ade0063023 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 fb874f7db42..5ab85a1d3e0 100644 --- a/server/object.h +++ b/server/object.h @@ -165,6 +165,9 @@ extern int get_page_size(void); 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 b2a53e45162..b2743e12033 100644 --- a/server/process.c +++ b/server/process.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "winbase.h" @@ -50,14 +51,89 @@ static const struct object_ops process_ops = 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 @@ static struct process *create_process( struct process *parent, struct new_proces 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 @@ static struct process *create_process( struct process *parent, struct new_proces 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 @@ static struct process *create_process( struct process *parent, struct new_proces 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 @@ static void process_killed( struct process *process, int exit_code ) { /* 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 @@ struct process_snapshot *process_snap( int *count ) 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 305d1c5afe3..737de561bc7 100644 --- a/server/process.h +++ b/server/process.h @@ -47,7 +47,9 @@ struct process_snapshot /* 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 945126b17f2..b7a5161d44a 100644 --- a/server/request.c +++ b/server/request.c @@ -9,22 +9,27 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif #include +#include #include #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 @@ void fatal_protocol_error( struct thread *thread, const char *err, ... ) 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 @@ int write_request( struct thread *thread ) 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 80ad9aa4fa9..c9fcd10ccfb 100644 --- a/server/request.h +++ b/server/request.h @@ -31,6 +31,9 @@ extern int write_request( struct thread *thread ); 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 @@ static inline size_t get_req_strlenW( const WCHAR *str ) 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 @@ static const struct handler { } 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 98b4817e982..c46ccd629fc 100644 --- a/server/select.c +++ b/server/select.c @@ -210,9 +210,6 @@ void select_loop(void) 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 f1629189c96..d2c5a56e5a3 100644 --- a/server/thread.c +++ b/server/thread.c @@ -87,6 +87,7 @@ static const struct object_ops thread_ops = }; 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 @@ static int alloc_client_buffer( struct thread *thread ) } /* 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 @@ static struct thread *create_thread( int fd, struct process *process, int suspen 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 @@ static struct thread *create_thread( int fd, struct process *process, int suspen 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 @@ void kill_thread( struct thread *thread, int exit_code ) 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 @@ DECL_HANDLER(new_thread) close( sock[1] ); } else file_set_error(); - release_object( process ); } /* retrieve the thread buffer file descriptor */ @@ -590,8 +593,9 @@ DECL_HANDLER(init_thread) 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 c19b9d40ff0..5e464938615 100644 --- a/server/thread.h +++ b/server/thread.h @@ -62,7 +62,7 @@ extern struct thread *current; /* 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 f140afb7ad4..89547b50f67 100644 --- a/server/trace.c +++ b/server/trace.c @@ -215,12 +215,13 @@ static void dump_new_process_request( const struct new_process_request *req ) 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 @@ static void dump_new_thread_reply( const struct new_thread_request *req ) 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_request( const struct init_thread_request *req ) 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 void dump_set_thread_context_request( const struct set_thread_context_req 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 dump_func reply_dumpers[REQ_NB_REQUESTS] = { static const char * const req_names[REQ_NB_REQUESTS] = { "new_process", "new_thread", - "set_debug", + "boot_done", "init_process", "init_process_done", "init_thread",