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.
This commit is contained in:
parent
647876e598
commit
2fe57779fb
|
@ -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)
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1486,6 +1486,8 @@ void SHELL_LoadRegistry( void )
|
|||
|
||||
TRACE("(void)\n");
|
||||
|
||||
if (!CLIENT_IsBootThread()) return; /* already loaded */
|
||||
|
||||
REGISTRY_Init();
|
||||
SetLoadLevel(0);
|
||||
|
||||
|
|
|
@ -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 @@ 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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "thread.h"
|
||||
#include "unicode.h"
|
||||
|
||||
int debug_level = 0;
|
||||
|
||||
struct object_name
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
197
server/process.c
197
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 @@ 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 */
|
||||
|
|
|
@ -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 );
|
||||
|
|
197
server/request.c
197
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 @@ 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 */
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue