Made server startup more robust against races caused by a previous
server terminating at the same time.
This commit is contained in:
parent
e0df32ff4f
commit
c10c9ef4f1
|
@ -338,6 +338,7 @@ const char *get_config_dir(void)
|
||||||
static void start_server( const char *oldcwd )
|
static void start_server( const char *oldcwd )
|
||||||
{
|
{
|
||||||
static int started; /* we only try once */
|
static int started; /* we only try once */
|
||||||
|
char *path, *p;
|
||||||
if (!started)
|
if (!started)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
@ -345,24 +346,36 @@ static void start_server( const char *oldcwd )
|
||||||
if (pid == -1) fatal_perror( "fork" );
|
if (pid == -1) fatal_perror( "fork" );
|
||||||
if (!pid)
|
if (!pid)
|
||||||
{
|
{
|
||||||
char *path, *p;
|
|
||||||
/* first try the installation dir */
|
/* first try the installation dir */
|
||||||
execl( BINDIR "/wineserver", "wineserver", NULL );
|
execl( BINDIR "/wineserver", "wineserver", NULL );
|
||||||
if (oldcwd) chdir( oldcwd );
|
|
||||||
/* now try the dir we were launched from */
|
/* now try the dir we were launched from */
|
||||||
if (!(path = malloc( strlen(argv0) + 20 )))
|
if (full_argv0)
|
||||||
|
{
|
||||||
|
if (!(path = malloc( strlen(full_argv0) + 20 )))
|
||||||
|
fatal_error( "out of memory\n" );
|
||||||
|
if ((p = strrchr( strcpy( path, full_argv0 ), '/' )))
|
||||||
|
{
|
||||||
|
strcpy( p, "/wineserver" );
|
||||||
|
execl( path, "wineserver", NULL );
|
||||||
|
strcpy( p, "/server/wineserver" );
|
||||||
|
execl( path, "wineserver", NULL );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now try the path */
|
||||||
|
execlp( "wineserver", "wineserver", NULL );
|
||||||
|
|
||||||
|
/* and finally the current dir */
|
||||||
|
if (!(path = malloc( strlen(oldcwd) + 20 )))
|
||||||
fatal_error( "out of memory\n" );
|
fatal_error( "out of memory\n" );
|
||||||
if ((p = strrchr( strcpy( path, argv0 ), '/' )))
|
if ((p = strrchr( strcpy( path, oldcwd ), '/' )))
|
||||||
{
|
{
|
||||||
strcpy( p, "/wineserver" );
|
strcpy( p, "/wineserver" );
|
||||||
execl( path, "wineserver", NULL );
|
execl( path, "wineserver", NULL );
|
||||||
strcpy( p, "/server/wineserver" );
|
strcpy( p, "/server/wineserver" );
|
||||||
execl( path, "wineserver", NULL );
|
execl( path, "wineserver", NULL );
|
||||||
}
|
}
|
||||||
/* now try the path */
|
|
||||||
execlp( "wineserver", "wineserver", NULL );
|
|
||||||
/* and finally the current dir */
|
|
||||||
execl( "./server/wineserver", "wineserver", NULL );
|
|
||||||
fatal_error( "could not exec wineserver\n" );
|
fatal_error( "could not exec wineserver\n" );
|
||||||
}
|
}
|
||||||
started = 1;
|
started = 1;
|
||||||
|
@ -382,13 +395,13 @@ static int server_connect( const char *oldcwd, const char *serverdir )
|
||||||
{
|
{
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int s, slen;
|
int s, slen, retry;
|
||||||
|
|
||||||
/* chdir to the server directory */
|
/* chdir to the server directory */
|
||||||
if (chdir( serverdir ) == -1)
|
if (chdir( serverdir ) == -1)
|
||||||
{
|
{
|
||||||
if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir );
|
if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir );
|
||||||
start_server( NULL );
|
start_server( "." );
|
||||||
if (chdir( serverdir ) == -1) fatal_perror( "chdir to %s", serverdir );
|
if (chdir( serverdir ) == -1) fatal_perror( "chdir to %s", serverdir );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,42 +410,44 @@ 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_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 (st.st_mode & 077) fatal_error( "'%s' must not be accessible by other users\n", serverdir );
|
||||||
|
|
||||||
/* check for an existing socket */
|
for (retry = 0; retry < 3; retry++)
|
||||||
if (lstat( SOCKETNAME, &st ) == -1)
|
|
||||||
{
|
{
|
||||||
if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
|
/* if not the first try, wait a bit to leave the server time to exit */
|
||||||
start_server( oldcwd );
|
if (retry) usleep( 100000 * retry * retry );
|
||||||
if (lstat( SOCKETNAME, &st ) == -1) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure the socket is sane */
|
/* check for an existing socket */
|
||||||
if (!S_ISSOCK(st.st_mode))
|
if (lstat( SOCKETNAME, &st ) == -1)
|
||||||
fatal_error( "'%s/%s' is not a socket\n", serverdir, SOCKETNAME );
|
{
|
||||||
if (st.st_uid != getuid())
|
if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
|
||||||
fatal_error( "'%s/%s' is not owned by you\n", serverdir, SOCKETNAME );
|
start_server( oldcwd );
|
||||||
|
if (lstat( SOCKETNAME, &st ) == -1) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
|
||||||
|
}
|
||||||
|
|
||||||
/* try to connect to it */
|
/* make sure the socket is sane */
|
||||||
addr.sun_family = AF_UNIX;
|
if (!S_ISSOCK(st.st_mode))
|
||||||
strcpy( addr.sun_path, SOCKETNAME );
|
fatal_error( "'%s/%s' is not a socket\n", serverdir, SOCKETNAME );
|
||||||
slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1;
|
if (st.st_uid != getuid())
|
||||||
|
fatal_error( "'%s/%s' is not owned by you\n", serverdir, SOCKETNAME );
|
||||||
|
|
||||||
|
/* try to connect to it */
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strcpy( addr.sun_path, SOCKETNAME );
|
||||||
|
slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1;
|
||||||
#ifdef HAVE_SOCKADDR_SUN_LEN
|
#ifdef HAVE_SOCKADDR_SUN_LEN
|
||||||
addr.sun_len = slen;
|
addr.sun_len = slen;
|
||||||
#endif
|
#endif
|
||||||
if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
|
|
||||||
if (connect( s, (struct sockaddr *)&addr, slen ) == -1)
|
|
||||||
{
|
|
||||||
close( s );
|
|
||||||
/* wait a bit and retry with a new socket */
|
|
||||||
usleep( 50000 );
|
|
||||||
if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
|
if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
|
||||||
if (connect( s, (struct sockaddr *)&addr, slen ) == -1)
|
if (connect( s, (struct sockaddr *)&addr, slen ) != -1)
|
||||||
fatal_error( "file '%s/%s' exists,\n"
|
{
|
||||||
" but I cannot connect to it; maybe the server has crashed?\n"
|
fcntl( s, F_SETFD, 1 ); /* set close on exec flag */
|
||||||
" If this is the case, you should remove this socket file and try again.\n",
|
return s;
|
||||||
serverdir, SOCKETNAME );
|
}
|
||||||
|
close( s );
|
||||||
}
|
}
|
||||||
fcntl( s, F_SETFD, 1 ); /* set close on exec flag */
|
fatal_error( "file '%s/%s' exists,\n"
|
||||||
return s;
|
" but I cannot connect to it; maybe the server has crashed?\n"
|
||||||
|
" If this is the case, you should remove this socket file and try again.\n",
|
||||||
|
serverdir, SOCKETNAME );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,8 @@ static void master_socket_poll_event( struct object *obj, int event )
|
||||||
/* remove the socket upon exit */
|
/* remove the socket upon exit */
|
||||||
static void socket_cleanup(void)
|
static void socket_cleanup(void)
|
||||||
{
|
{
|
||||||
unlink( SOCKETNAME );
|
static int do_it_once;
|
||||||
|
if (!do_it_once++) unlink( SOCKETNAME );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void master_socket_destroy( struct object *obj )
|
static void master_socket_destroy( struct object *obj )
|
||||||
|
@ -382,7 +383,7 @@ void open_master_socket(void)
|
||||||
if (bind( fd, (struct sockaddr *)&addr, slen ) == -1)
|
if (bind( fd, (struct sockaddr *)&addr, slen ) == -1)
|
||||||
{
|
{
|
||||||
if ((errno == EEXIST) || (errno == EADDRINUSE))
|
if ((errno == EEXIST) || (errno == EADDRINUSE))
|
||||||
fatal_error( "another server is already running\n" );
|
exit(0); /* pretend we succeeded to start */
|
||||||
else
|
else
|
||||||
fatal_perror( "bind" );
|
fatal_perror( "bind" );
|
||||||
}
|
}
|
||||||
|
@ -411,7 +412,9 @@ void open_master_socket(void)
|
||||||
/* close the master socket and stop waiting for new clients */
|
/* close the master socket and stop waiting for new clients */
|
||||||
void close_master_socket(void)
|
void close_master_socket(void)
|
||||||
{
|
{
|
||||||
release_object( master_socket );
|
/* if a new client is waiting, we keep on running */
|
||||||
|
if (!check_select_events( master_socket->obj.fd, POLLIN ))
|
||||||
|
release_object( master_socket );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* lock/unlock the master socket to stop accepting new clients */
|
/* lock/unlock the master socket to stop accepting new clients */
|
||||||
|
|
Loading…
Reference in New Issue