From c10c9ef4f11c5eca331f4e391b2121f0539b26f4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 11 Aug 2000 21:16:53 +0000 Subject: [PATCH] Made server startup more robust against races caused by a previous server terminating at the same time. --- scheduler/client.c | 93 +++++++++++++++++++++++++++++++----------------------- server/request.c | 9 ++++-- 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/scheduler/client.c b/scheduler/client.c index 5888975d591..ef39a499190 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -338,6 +338,7 @@ const char *get_config_dir(void) static void start_server( const char *oldcwd ) { static int started; /* we only try once */ + char *path, *p; if (!started) { int status; @@ -345,24 +346,36 @@ static void start_server( const char *oldcwd ) if (pid == -1) fatal_perror( "fork" ); if (!pid) { - char *path, *p; /* first try the installation dir */ execl( BINDIR "/wineserver", "wineserver", NULL ); - if (oldcwd) chdir( oldcwd ); + /* 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" ); - if ((p = strrchr( strcpy( path, argv0 ), '/' ))) + if ((p = strrchr( strcpy( path, oldcwd ), '/' ))) { 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 */ - execl( "./server/wineserver", "wineserver", NULL ); fatal_error( "could not exec wineserver\n" ); } started = 1; @@ -382,13 +395,13 @@ static int server_connect( const char *oldcwd, const char *serverdir ) { struct sockaddr_un addr; struct stat st; - int s, slen; + int s, slen, retry; /* chdir to the server directory */ if (chdir( serverdir ) == -1) { if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir ); - start_server( NULL ); + start_server( "." ); 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_mode & 077) fatal_error( "'%s' must not be accessible by other users\n", serverdir ); - /* check for an existing socket */ - if (lstat( SOCKETNAME, &st ) == -1) + for (retry = 0; retry < 3; retry++) { - 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 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 (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME ); + start_server( oldcwd ); + if (lstat( SOCKETNAME, &st ) == -1) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME ); + } - /* make sure the socket is sane */ - 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 ); + /* make sure the socket is sane */ + 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 ); - /* 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; + /* 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 - addr.sun_len = slen; + addr.sun_len = slen; #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 (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" - " If this is the case, you should remove this socket file and try again.\n", - serverdir, SOCKETNAME ); + if (connect( s, (struct sockaddr *)&addr, slen ) != -1) + { + fcntl( s, F_SETFD, 1 ); /* set close on exec flag */ + return s; + } + close( s ); } - fcntl( s, F_SETFD, 1 ); /* set close on exec flag */ - return s; + fatal_error( "file '%s/%s' exists,\n" + " 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 ); } diff --git a/server/request.c b/server/request.c index cea639b57c6..7f0cf646679 100644 --- a/server/request.c +++ b/server/request.c @@ -296,7 +296,8 @@ static void master_socket_poll_event( struct object *obj, int event ) /* remove the socket upon exit */ 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 ) @@ -382,7 +383,7 @@ void open_master_socket(void) if (bind( fd, (struct sockaddr *)&addr, slen ) == -1) { if ((errno == EEXIST) || (errno == EADDRINUSE)) - fatal_error( "another server is already running\n" ); + exit(0); /* pretend we succeeded to start */ else fatal_perror( "bind" ); } @@ -411,7 +412,9 @@ void open_master_socket(void) /* close the master socket and stop waiting for new clients */ 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 */ -- 2.11.4.GIT