Create the server directory and socket file in /tmp.

Use fcntl file locking to ensure exclusion on the server socket and to
better recover from crashes.
Flush the registry before closing the socket to avoid timeouts on the
client side.
Moved get_config_dir functionality to libwine.
This commit is contained in:
Alexandre Julliard 2002-06-20 23:21:27 +00:00
parent 88e4261903
commit 4144b5b8fc
13 changed files with 481 additions and 212 deletions

View File

@ -56,16 +56,17 @@
#include "ntddk.h"
#include "wine/winbase16.h" /* for GetCurrentTask */
#include "winerror.h"
#include "winioctl.h"
#include "ntddstor.h"
#include "ntddcdrm.h"
#include "drive.h"
#include "file.h"
#include "heap.h"
#include "msdos.h"
#include "task.h"
#include "wine/debug.h"
#include "wine/library.h"
#include "wine/server.h"
#include "winioctl.h"
#include "ntddstor.h"
#include "ntddcdrm.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
WINE_DECLARE_DEBUG_CHANNEL(file);
@ -197,7 +198,7 @@ int DRIVE_Init(void)
else
{
/* relative paths are relative to config dir */
const char *config = get_config_dir();
const char *config = wine_get_config_dir();
drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
sprintf( drive->root, "%s/%s", config, path );
}

View File

@ -38,8 +38,9 @@
#include "winreg.h"
#include "file.h"
#include "heap.h"
#include "wine/debug.h"
#include "wine/server.h"
#include "wine/library.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(profile);
@ -544,7 +545,7 @@ static BOOL PROFILE_FlushFile(void)
{
/* Try to create it in $HOME/.wine */
/* FIXME: this will need a more general solution */
strcpy( buffer, get_config_dir() );
strcpy( buffer, wine_get_config_dir() );
p = buffer + strlen(buffer);
*p++ = '/';
strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
@ -681,7 +682,7 @@ static BOOL PROFILE_Open( LPCSTR filename )
/* Try to open the profile file, first in $HOME/.wine */
/* FIXME: this will need a more general solution */
strcpy( buffer, get_config_dir() );
strcpy( buffer, wine_get_config_dir() );
p = buffer + strlen(buffer);
*p++ = '/';
strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
@ -1046,7 +1047,7 @@ int PROFILE_LoadWineIni(void)
}
RtlFreeUnicodeString( &nameW );
if (!CLIENT_IsBootThread()) return 1; /* already loaded */
if (disp == REG_OPENED_EXISTING_KEY) return 1; /* loaded by the server */
if ((p = getenv( "HOME" )) != NULL)
{
@ -1055,36 +1056,23 @@ int PROFILE_LoadWineIni(void)
if ((f = fopen( buffer, "r" )) != NULL)
{
lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
goto found;
/* convert to the new format */
sprintf( buffer, "%s/config", wine_get_config_dir() );
convert_config( f, buffer );
fclose( f );
MESSAGE( "The '%s' configuration file has been converted\n"
"to the new format and saved as '%s'.\n", PROFILE_WineIniUsed, buffer );
MESSAGE( "You should verify that the contents of the new file are correct,\n"
"and then remove the old one and restart Wine.\n" );
ExitProcess(0);
}
}
else WARN("could not get $HOME value for config file.\n" );
if (disp == REG_OPENED_EXISTING_KEY) return 1; /* loaded by the server */
MESSAGE( "Can't open configuration file %s/config\n",get_config_dir() );
MESSAGE( "Can't open configuration file %s/config\n", wine_get_config_dir() );
return 0;
found:
if (disp == REG_OPENED_EXISTING_KEY)
{
MESSAGE( "Warning: configuration loaded by the server from '%s/config',\n"
" file '%s' was ignored.\n", get_config_dir(), PROFILE_WineIniUsed );
fclose( f );
return 1;
}
/* convert to the new format */
sprintf( buffer, "%s/config", get_config_dir() );
convert_config( f, buffer );
fclose( f );
MESSAGE( "The '%s' configuration file has been converted\n"
"to the new format and saved as '%s'.\n", PROFILE_WineIniUsed, buffer );
MESSAGE( "You should verify that the contents of the new file are correct,\n"
"and then remove the old one and restart Wine.\n" );
ExitProcess(0);
}
@ -1099,7 +1087,7 @@ void PROFILE_UsageWineIni(void)
{
MESSAGE("Perhaps you have not properly edited or created "
"your Wine configuration file.\n");
MESSAGE("This is (supposed to be) '%s/config'\n", get_config_dir());
MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
/* RTFM, so to say */
}

View File

@ -42,11 +42,11 @@
#include "winnls.h"
#include "winreg.h"
#include "font.h"
#include "wine/debug.h"
#include "user.h" /* for TWEAK_WineLook (FIXME) */
#include "x11font.h"
#include "wine/server.h"
#include "wine/library.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(font);
@ -1848,7 +1848,7 @@ static void XFONT_LoadIgnores(void)
*/
static char* XFONT_UserMetricsCache( char* buffer, int* buf_size )
{
const char *confdir = get_config_dir();
const char *confdir = wine_get_config_dir();
const char *display_name = XDisplayName(NULL);
int len = strlen(confdir) + strlen(INIFontMetrics) + strlen(display_name) + 8;
int display = 0;

View File

@ -24,6 +24,11 @@
#include <sys/types.h>
#include "winbase.h"
/* configuration */
extern const char *wine_get_config_dir(void);
extern const char *wine_get_server_dir(void);
/* dll loading */
typedef void (*load_dll_callback_t)( void *, const char * );

View File

@ -113,7 +113,6 @@ inline static void wine_server_set_reply( void *req_ptr, void *ptr, unsigned int
/* non-exported functions */
extern void server_protocol_error( const char *err, ... ) WINE_NORETURN;
extern void server_protocol_perror( const char *err ) WINE_NORETURN;
extern const char *get_config_dir(void);
extern void CLIENT_InitServer(void);
extern void CLIENT_InitThread(void);
extern void CLIENT_BootDone( int debug_level );

View File

@ -10,6 +10,7 @@ SONAME = libwine.so
EXTRALIBS = @DLLIBS@
C_SRCS = \
config.c \
debug.c \
errno.c \
ldt.c \

170
library/config.c Normal file
View File

@ -0,0 +1,170 @@
/*
* Configuration parameters shared between Wine server and clients
*
* Copyright 2002 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "wine/port.h"
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static const char * const server_config_dir = "/.wine"; /* config dir relative to $HOME */
static const char * const server_root_prefix = "/tmp/.wine-"; /* prefix for server root dir */
static const char * const server_dir_prefix = "/server-"; /* prefix for server dir */
static char *config_dir;
static char *server_dir;
#ifdef __GNUC__
static void fatal_error( const char *err, ... ) __attribute__((noreturn,format(printf,1,2)));
static void fatal_perror( const char *err, ... ) __attribute__((noreturn,format(printf,1,2)));
#endif
/* die on a fatal error */
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 */
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);
}
/* malloc wrapper */
static void *xmalloc( size_t size )
{
void *res;
if (!size) size = 1;
if (!(res = malloc( size ))) fatal_error( "virtual memory exhausted\n");
return res;
}
/* remove all trailing slashes from a path name */
inline static void remove_trailing_slashes( char *path )
{
int len = strlen( path );
while (len > 1 && path[len-1] == '/') path[--len] = 0;
}
/* initialize all the paths values */
static void init_paths(void)
{
struct stat st;
char uid_str[32], *p;
const char *home = getenv( "HOME" );
const char *user = NULL;
const char *prefix = getenv( "WINEPREFIX" );
struct passwd *pwd = getpwuid( getuid() );
if (pwd)
{
user = pwd->pw_name;
if (!home) home = pwd->pw_dir;
}
if (!user)
{
sprintf( uid_str, "%d", getuid() );
user = uid_str;
}
/* build config_dir */
if (prefix)
{
if (!(config_dir = strdup( prefix ))) fatal_error( "virtual memory exhausted\n");
remove_trailing_slashes( config_dir );
if (config_dir[0] != '/')
fatal_error( "invalid directory %s in WINEPREFIX: not an absolute path\n", prefix );
if (stat( config_dir, &st ) == -1)
fatal_perror( "cannot open %s as specified in WINEPREFIX", config_dir );
}
else
{
if (!home) fatal_error( "could not determine your home directory\n" );
if (home[0] != '/') fatal_error( "your home directory %s is not an absolute path\n", home );
config_dir = xmalloc( strlen(home) + strlen(server_config_dir) + 1 );
strcpy( config_dir, home );
remove_trailing_slashes( config_dir );
strcat( config_dir, server_config_dir );
if (stat( config_dir, &st ) == -1)
fatal_perror( "cannot open %s", config_dir );
}
if (!S_ISDIR(st.st_mode)) fatal_error( "%s is not a directory\n", config_dir );
/* build server_dir */
server_dir = xmalloc( strlen(server_root_prefix) + strlen(user) + strlen( server_dir_prefix ) +
2*sizeof(st.st_dev) + 2*sizeof(st.st_ino) + 2 );
strcpy( server_dir, server_root_prefix );
p = server_dir + strlen(server_dir);
strcpy( p, user );
while (*p)
{
if (*p == '/') *p = '!';
p++;
}
strcpy( p, server_dir_prefix );
if (sizeof(st.st_dev) > sizeof(unsigned long))
sprintf( server_dir + strlen(server_dir), "%llx-", (unsigned long long)st.st_dev );
else
sprintf( server_dir + strlen(server_dir), "%lx-", (unsigned long)st.st_dev );
if (sizeof(st.st_ino) > sizeof(unsigned long))
sprintf( server_dir + strlen(server_dir), "%llx", (unsigned long long)st.st_ino );
else
sprintf( server_dir + strlen(server_dir), "%lx", (unsigned long)st.st_ino );
}
/* return the configuration directory ($WINEPREFIX or $HOME/.wine) */
const char *wine_get_config_dir(void)
{
if (!config_dir) init_paths();
return config_dir;
}
/* return the full name of the server directory (the one containing the socket) */
const char *wine_get_server_dir(void)
{
if (!server_dir) init_paths();
return server_dir;
}

View File

@ -53,6 +53,7 @@
#include "winreg.h"
#include "wine/winbase16.h"
#include "wine/library.h"
#include "wine/server.h"
#include "wine/unicode.h"
#include "file.h"
@ -1030,7 +1031,7 @@ static void _set_registry_levels(int level,int saving,int period)
/* _save_at_exit [Internal] */
static void _save_at_exit(HKEY hkey,LPCSTR path)
{
LPCSTR confdir = get_config_dir();
LPCSTR confdir = wine_get_config_dir();
SERVER_START_REQ( save_registry_atexit )
{
@ -1547,7 +1548,7 @@ static void _load_global_registry(void)
/* load home registry files (stored in ~/.wine) [Internal] */
static void _load_home_registry( HKEY hkey_users_default )
{
LPCSTR confdir = get_config_dir();
LPCSTR confdir = wine_get_config_dir();
LPSTR tmp = _xmalloc(strlen(confdir)+20);
strcpy(tmp,confdir);

View File

@ -46,6 +46,7 @@
#include <stdarg.h>
#include "thread.h"
#include "wine/library.h"
#include "wine/server.h"
#include "winerror.h"
#include "options.h"
@ -55,9 +56,8 @@
#define SCM_RIGHTS 1
#endif
#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 */
#define LOCKNAME "lock" /* name of the lock file */
#ifndef HAVE_MSGHDR_ACCRIGHTS
/* data structure used to pass an fd with sendmsg/recvmsg */
@ -74,8 +74,13 @@ static void *boot_thread_id;
static sigset_t block_set; /* signals to block during server calls */
static int fd_socket; /* socket to exchange file descriptors with the server */
#ifdef __GNUC__
static void fatal_error( const char *err, ... ) __attribute__((noreturn, format(printf,1,2)));
static void fatal_perror( const char *err, ... ) __attribute__((noreturn, format(printf,1,2)));
static void server_connect_error( const char *serverdir ) __attribute__((noreturn));
#endif
/* die on a fatal error; use only during initialization */
static void fatal_error( const char *err, ... ) WINE_NORETURN;
static void fatal_error( const char *err, ... )
{
va_list args;
@ -88,7 +93,6 @@ static void fatal_error( const char *err, ... )
}
/* die on a fatal error; use only during initialization */
static void fatal_perror( const char *err, ... ) WINE_NORETURN;
static void fatal_perror( const char *err, ... )
{
va_list args;
@ -433,42 +437,6 @@ int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *uni
}
/***********************************************************************
* get_config_dir
*
* Return the configuration directory ($WINEPREFIX or $HOME/.wine)
*/
const char *get_config_dir(void)
{
static char *confdir;
if (!confdir)
{
const char *prefix = getenv( "WINEPREFIX" );
if (prefix)
{
int len = strlen(prefix);
if (!(confdir = strdup( prefix ))) fatal_error( "out of memory\n" );
if (len > 1 && confdir[len-1] == '/') confdir[len-1] = 0;
}
else
{
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 );
}
}
return confdir;
}
/***********************************************************************
* start_server
*
@ -495,7 +463,7 @@ static void start_server( const char *oldcwd )
sprintf( path, "%s/%s", oldcwd, p );
p = path;
}
execl( p, "wineserver", NULL );
execl( p, p, NULL );
fatal_perror( "could not exec the server '%s'\n"
" specified in the WINESERVER environment variable", p );
}
@ -511,7 +479,7 @@ static void start_server( const char *oldcwd )
if ((p = strrchr( strcpy( path, full_argv0 ), '/' )))
{
strcpy( p, "/wineserver" );
execl( path, "wineserver", NULL );
execl( path, path, NULL );
}
free(path);
}
@ -520,13 +488,48 @@ static void start_server( const char *oldcwd )
execlp( "wineserver", "wineserver", NULL );
fatal_error( "could not exec wineserver\n" );
}
started = 1;
waitpid( pid, &status, 0 );
status = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
if (status == 2) return; /* server lock held by someone else, will retry later */
if (status) exit(status); /* server failed */
started = 1;
}
}
/***********************************************************************
* server_connect_error
*
* Try to display a meaningful explanation of why we couldn't connect
* to the server.
*/
static void server_connect_error( const char *serverdir )
{
int fd;
struct flock fl;
if ((fd = open( LOCKNAME, O_WRONLY )) == -1)
fatal_error( "for some mysterious reason, the wine server never started.\n" );
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 1;
if (fcntl( fd, F_GETLK, &fl ) != -1)
{
if (fl.l_type == F_WRLCK) /* the file is locked */
fatal_error( "a wine server seems to be running, but I cannot connect to it.\n"
" You probably need to kill that process (it might be pid %d).\n",
(int)fl.l_pid );
fatal_error( "for some mysterious reason, the wine server failed to run.\n" );
}
fatal_error( "the file system of '%s' doesn't support locks,\n"
" and there is a 'socket' file in that directory that prevents wine from starting.\n"
" You should make sure no wine server is running, remove that file and try again.\n",
serverdir );
}
/***********************************************************************
* server_connect
*
@ -552,17 +555,20 @@ static int server_connect( const char *oldcwd, const char *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 );
for (retry = 0; retry < 3; retry++)
for (retry = 0; retry < 6; retry++)
{
/* if not the first try, wait a bit to leave the server time to exit */
if (retry) usleep( 100000 * retry * retry );
/* check for an existing socket */
if (lstat( SOCKETNAME, &st ) == -1)
/* if not the first try, wait a bit to leave the previous server time to exit */
if (retry)
{
usleep( 100000 * retry * retry );
start_server( oldcwd );
if (lstat( SOCKETNAME, &st ) == -1) continue; /* still no socket, wait a bit more */
}
else if (lstat( SOCKETNAME, &st ) == -1) /* check for an already existing socket */
{
if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
start_server( oldcwd );
if (lstat( SOCKETNAME, &st ) == -1) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
if (lstat( SOCKETNAME, &st ) == -1) continue; /* still no socket, wait a bit more */
}
/* make sure the socket is sane (ISFIFO needed for Solaris) */
@ -586,10 +592,7 @@ static int server_connect( const char *oldcwd, const char *serverdir )
}
close( s );
}
fatal_error( "file '%s/%s' exists,\n"
" but I cannot connect to it; maybe the wineserver has crashed?\n"
" If this is the case, you should remove this socket file and try again.\n",
serverdir, SOCKETNAME );
server_connect_error( serverdir );
}
@ -601,9 +604,7 @@ static int server_connect( const char *oldcwd, const char *serverdir )
void CLIENT_InitServer(void)
{
int size;
char hostname[64];
char *oldcwd, *serverdir;
const char *configdir;
char *oldcwd;
obj_handle_t dummy_handle;
/* retrieve the current directory */
@ -631,17 +632,8 @@ void CLIENT_InitServer(void)
}
}
/* get the server directory name */
if (gethostname( hostname, sizeof(hostname) ) == -1) fatal_perror( "gethostname" );
configdir = get_config_dir();
serverdir = malloc( strlen(configdir) + strlen(SERVERDIR) + strlen(hostname) + 1 );
if (!serverdir) fatal_error( "out of memory\n" );
strcpy( serverdir, configdir );
strcat( serverdir, SERVERDIR );
strcat( serverdir, hostname );
/* connect to the server */
fd_socket = server_connect( oldcwd, serverdir );
fd_socket = server_connect( oldcwd, wine_get_server_dir() );
/* switch back to the starting directory */
if (oldcwd)

View File

@ -34,16 +34,18 @@
/* command-line options */
int debug_level = 0;
int master_socket_timeout = 3; /* master socket timeout in seconds, default is 3 s */
const char *server_argv0;
/* parse-line args */
/* FIXME: should probably use getopt, and add a (more complete?) help option */
static void usage(const char *exeName)
static void usage(void)
{
fprintf(stderr, "\nusage: %s [options]\n\n", exeName);
fprintf(stderr, "\nusage: %s [options]\n\n", server_argv0);
fprintf(stderr, "options:\n");
fprintf(stderr, " -d<n> set debug level to <n>\n");
fprintf(stderr, " -p[n] make server persistent, optionally for n seconds\n");
fprintf(stderr, " -w wait until the current wineserver terminates\n");
fprintf(stderr, " -h display this help message\n");
fprintf(stderr, "\n");
}
@ -51,6 +53,8 @@ static void usage(const char *exeName)
static void parse_args( int argc, char *argv[] )
{
int i;
server_argv0 = argv[0];
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
@ -62,23 +66,26 @@ static void parse_args( int argc, char *argv[] )
else debug_level++;
break;
case 'h':
usage( argv[0] );
usage();
exit(0);
break;
case 'p':
if (isdigit(argv[i][2])) master_socket_timeout = atoi( argv[i] + 2 );
else master_socket_timeout = -1;
break;
case 'w':
wait_for_lock();
exit(0);
default:
fprintf( stderr, "Unknown option '%s'\n", argv[i] );
usage( argv[0] );
fprintf( stderr, "wineserver: unknown option '%s'\n", argv[i] );
usage();
exit(1);
}
}
else
{
fprintf( stderr, "Unknown argument '%s'. Your version of wine may be too old.\n", argv[i] );
usage(argv[0]);
fprintf( stderr, "wineserver: unknown argument '%s'.\n", argv[i] );
usage();
exit(1);
}
}
@ -107,16 +114,12 @@ int main( int argc, char *argv[] )
open_master_socket();
setvbuf( stderr, NULL, _IOLBF, 0 );
if (debug_level) fprintf( stderr, "Server: starting (pid=%ld)\n", (long) getpid() );
if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() );
init_registry();
select_loop();
close_registry();
if (debug_level) fprintf( stderr, "Server: exiting (pid=%ld)\n", (long) getpid() );
#ifdef DEBUG_OBJECTS
close_atom_table();
dump_objects(); /* dump any remaining objects */
#endif
return 0;
}

View File

@ -191,7 +191,7 @@ struct thread *create_process( int fd )
struct thread *thread = NULL;
int request_pipe[2];
if (!(process = alloc_object( &process_ops, fd ))) return NULL;
if (!(process = alloc_object( &process_ops, fd ))) goto error;
process->next = NULL;
process->prev = NULL;
process->parent = NULL;
@ -230,7 +230,12 @@ struct thread *create_process( int fd )
file_set_error();
goto error;
}
send_client_fd( process, request_pipe[1], 0 );
if (send_client_fd( process, request_pipe[1], 0 ) == -1)
{
close( request_pipe[0] );
close( request_pipe[1] );
goto error;
}
close( request_pipe[1] );
if (!(thread = create_thread( request_pipe[0], process ))) goto error;
@ -239,8 +244,9 @@ struct thread *create_process( int fd )
return thread;
error:
if (thread) release_object( thread );
release_object( process );
if (process) release_object( process );
/* if we failed to start our first process, close everything down */
if (!running_processes) close_master_socket();
return NULL;
}
@ -502,13 +508,7 @@ static void process_killed( struct process *process )
if (process->exe.file) release_object( process->exe.file );
process->exe.file = NULL;
wake_up( &process->obj, 0 );
if (!--running_processes)
{
/* last process died, close global handles */
close_global_handles();
/* this will cause the select loop to terminate */
close_master_socket();
}
if (!--running_processes) close_master_socket();
}
/* add a thread to a process running threads list */

View File

@ -36,6 +36,9 @@
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include <sys/uio.h>
#include <sys/un.h>
#include <unistd.h>
@ -43,6 +46,9 @@
#include "winnt.h"
#include "winbase.h"
#include "wincon.h"
#include "wine/library.h"
#include "handle.h"
#include "thread.h"
#include "process.h"
#define WANT_REQUEST_HANDLERS
@ -53,10 +59,9 @@
#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 */
/* path names for server master Unix socket */
static const char * const server_socket_name = "socket"; /* name of the socket file */
static const char * const server_lock_name = "lock"; /* name of the server lock file */
struct master_socket
{
@ -399,10 +404,13 @@ int send_client_fd( struct process *process, int fd, obj_handle_t handle )
if (ret >= 0)
{
if (ret > 0)
fprintf( stderr, "Protocol error: process %p: partial sendmsg %d\n", process, ret );
fprintf( stderr, "Protocol error: process %p: partial sendmsg %d\n", process, ret );
kill_process( process, NULL, 1 );
}
else if (errno == EPIPE)
{
kill_process( process, NULL, 0 );
}
else
{
fprintf( stderr, "Protocol error: process %p: ", process );
@ -461,84 +469,136 @@ static void master_socket_poll_event( struct object *obj, int event )
static void socket_cleanup(void)
{
static int do_it_once;
if (!do_it_once++) unlink( SOCKETNAME );
if (!do_it_once++) unlink( server_socket_name );
}
/* return the configuration directory ($WINEPREFIX or $HOME/.wine) */
const char *get_config_dir(void)
/* create a directory and check its permissions */
static void create_dir( const char *name, struct stat *st )
{
static char *confdir;
if (!confdir)
if (lstat( name, st ) == -1)
{
const char *prefix = getenv( "WINEPREFIX" );
if (prefix)
{
int len = strlen(prefix);
if (!(confdir = strdup( prefix ))) fatal_error( "out of memory\n" );
if (len > 1 && confdir[len-1] == '/') confdir[len-1] = 0;
}
else
{
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 );
}
if (errno != ENOENT) fatal_perror( "lstat %s", name );
if (mkdir( name, 0700 ) == -1) fatal_perror( "mkdir %s", name );
if (lstat( name, st ) == -1) fatal_perror( "lstat %s", name );
}
return confdir;
if (!S_ISDIR(st->st_mode)) fatal_error( "%s is not a directory\n", name );
if (st->st_uid != getuid()) fatal_error( "%s is not owned by you\n", name );
if (st->st_mode & 077) fatal_error( "%s must not be accessible by other users\n", name );
}
/* 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;
char *p, *server_dir;
struct stat st, st2;
if (gethostname( hostname, sizeof(hostname) ) == -1) fatal_perror( "gethostname" );
if (!(server_dir = strdup( wine_get_server_dir() ))) fatal_error( "out of memory\n" );
if (!(serverdir = malloc( strlen(SERVERDIR) + strlen(hostname) + 1 )))
fatal_error( "out of memory\n" );
/* first create the base directory if needed */
if (chdir( confdir ) == -1) fatal_perror( "chdir %s", confdir );
p = strrchr( server_dir, '/' );
*p = 0;
create_dir( server_dir, &st );
strcpy( serverdir, SERVERDIR );
strcat( serverdir, hostname );
/* now create the server directory */
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 );
*p = '/';
create_dir( server_dir, &st );
if (chdir( server_dir ) == -1) fatal_perror( "chdir %s", server_dir );
if (stat( ".", &st2 ) == -1) fatal_perror( "stat %s", server_dir );
if (st.st_dev != st2.st_dev || st.st_ino != st2.st_ino)
fatal_error( "chdir did not end up in %s\n", server_dir );
free( server_dir );
}
/* open the master server socket and start waiting for new clients */
void open_master_socket(void)
/* wait for the server lock */
int wait_for_lock(void)
{
struct sockaddr_un addr;
int fd, slen;
/* make sure no request is larger than the maximum size */
assert( sizeof(union generic_request) == sizeof(struct request_max_size) );
assert( sizeof(union generic_reply) == sizeof(struct request_max_size) );
int fd, r;
struct flock fl;
create_server_dir();
if ((fd = open( server_lock_name, O_TRUNC|O_WRONLY, 0600 )) == -1)
return -1;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 1;
r = fcntl( fd, F_SETLKW, &fl );
close(fd);
return r;
}
/* acquire the main server lock */
static void acquire_lock(void)
{
const char *server_dir_name = wine_get_server_dir();
struct sockaddr_un addr;
struct stat st;
struct flock fl;
int fd, slen, got_lock = 0;
if (lstat( server_lock_name, &st ) == -1)
{
if (errno != ENOENT)
fatal_perror( "lstat %s/%s", server_dir_name, server_lock_name );
}
else
{
if (!S_ISREG(st.st_mode))
fatal_error( "%s/%s is not a regular file\n", server_dir_name, server_lock_name );
}
if ((fd = open( server_lock_name, O_CREAT|O_TRUNC|O_WRONLY, 0600 )) == -1)
fatal_perror( "error creating %s/%s", server_dir_name, server_lock_name );
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 1;
if (fcntl( fd, F_SETLK, &fl ) != -1)
{
/* check for crashed server */
if (stat( server_socket_name, &st ) != -1 && /* there is a leftover socket */
stat( "core", &st ) != -1 && st.st_size) /* and there is a non-empty core file */
{
fprintf( stderr,
"Warning: a previous instance of the wine server seems to have crashed.\n"
"Please run 'gdb %s %s/core',\n"
"type 'backtrace' at the gdb prompt and report the results. Thanks.\n\n",
server_argv0, server_dir_name );
}
unlink( server_socket_name ); /* we got the lock, we can safely remove the socket */
got_lock = 1;
/* in that case we reuse fd without closing it, this ensures
* that we hold the lock until the process exits */
}
else
{
switch(errno)
{
case ENOLCK:
break;
case EACCES:
/* check whether locks work at all on this file system */
if (fcntl( fd, F_GETLK, &fl ) == -1) break;
/* fall through */
case EAGAIN:
exit(2); /* we didn't get the lock, exit with special status */
default:
fatal_perror( "fcntl %s/%s", server_dir_name, server_lock_name );
}
/* it seems we can't use locks on this fs, so we will use the socket existence as lock */
close( fd );
}
if ((fd = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
addr.sun_family = AF_UNIX;
strcpy( addr.sun_path, SOCKETNAME );
strcpy( addr.sun_path, server_socket_name );
slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1;
#ifdef HAVE_SOCKADDR_SUN_LEN
addr.sun_len = slen;
@ -546,19 +606,65 @@ void open_master_socket(void)
if (bind( fd, (struct sockaddr *)&addr, slen ) == -1)
{
if ((errno == EEXIST) || (errno == EADDRINUSE))
exit(0); /* pretend we succeeded to start */
else
fatal_perror( "bind" );
{
if (got_lock)
fatal_error( "couldn't bind to the socket even though we hold the lock\n" );
exit(2); /* we didn't get the lock, exit with special status */
}
fatal_perror( "bind" );
}
atexit( socket_cleanup );
chmod( SOCKETNAME, 0600 ); /* make sure no other user can connect */
chmod( server_socket_name, 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" );
master_socket->timeout = NULL;
set_select_events( &master_socket->obj, POLLIN );
}
/* open the master server socket and start waiting for new clients */
void open_master_socket(void)
{
int pid, status, sync_pipe[2];
char dummy;
/* make sure no request is larger than the maximum size */
assert( sizeof(union generic_request) == sizeof(struct request_max_size) );
assert( sizeof(union generic_reply) == sizeof(struct request_max_size) );
create_server_dir();
if (pipe( sync_pipe ) == -1) fatal_perror( "pipe" );
pid = fork();
switch( pid )
{
case 0: /* child */
setsid();
close( sync_pipe[0] );
acquire_lock();
/* signal parent */
write( sync_pipe[1], &dummy, 1 );
close( sync_pipe[1] );
break;
case -1:
fatal_perror( "fork" );
break;
default: /* parent */
close( sync_pipe[1] );
/* wait for child to signal us and then exit */
if (read( sync_pipe[0], &dummy, 1 ) == 1) _exit(0);
/* child terminated, propagate exit status */
wait4( pid, &status, 0, NULL );
if (WIFEXITED(status)) _exit( WEXITSTATUS(status) );
_exit(1);
}
/* setup msghdr structure constant fields */
msghdr.msg_name = NULL;
@ -568,26 +674,28 @@ void open_master_socket(void)
/* init startup ticks */
server_start_ticks = get_tick_count();
/* go in the background */
switch(fork())
{
case -1:
fatal_perror( "fork" );
case 0:
setsid();
break;
default:
_exit(0); /* do not call atexit functions */
}
}
/* master socket timer expiration handler */
static void close_socket_timeout( void *arg )
{
master_socket->timeout = NULL;
flush_registry();
/* if a new client is waiting, we keep on running */
if (!check_select_events( master_socket->obj.fd, POLLIN ))
release_object( master_socket );
if (check_select_events( master_socket->obj.fd, POLLIN )) return;
if (debug_level) fprintf( stderr, "wineserver: exiting (pid=%ld)\n", (long) getpid() );
#ifdef DEBUG_OBJECTS
/* shut down everything properly */
release_object( master_socket );
close_global_handles();
close_registry();
close_atom_table();
#else
exit(0);
#endif
}
/* close the master socket and stop waiting for new clients */

View File

@ -59,6 +59,7 @@ extern unsigned int get_tick_count(void);
extern void open_master_socket(void);
extern void close_master_socket(void);
extern void lock_master_socket( int locked );
extern int wait_for_lock(void);
extern void trace_request(void);
extern void trace_reply( enum request req, const union generic_reply *reply );