diff options
Diffstat (limited to 'tdm/backend/server.c')
-rw-r--r-- | tdm/backend/server.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/tdm/backend/server.c b/tdm/backend/server.c new file mode 100644 index 000000000..e78d8a66c --- /dev/null +++ b/tdm/backend/server.c @@ -0,0 +1,376 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001,2003,2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * server.c - manage the X server + */ + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <X11/Xlib.h> + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> + + +struct display *startingServer; +time_t serverTimeout = TO_INF; + +char ** +PrepServerArgv( struct display *d, const char *args ) +{ + char **argv; +#ifdef HAVE_VTS + char vtstr[8]; +#endif + + if (!(argv = parseArgs( 0, d->serverCmd )) || + !(argv = parseArgs( argv, args )) || + !(argv = addStrArr( argv, d->name, -1 ))) + exit( 47 ); +#ifdef HAVE_VTS + if (d->serverVT && + !(argv = addStrArr( argv, vtstr, + sprintf( vtstr, "vt%d", d->serverVT ) ))) + exit( 47 ); +#endif + return argv; +} + +static void +StartServerOnce( void ) +{ + struct display *d = startingServer; + char **argv; + int pid; + + Debug( "StartServerOnce for %s, try %d\n", d->name, ++d->startTries ); + d->serverStatus = starting; + switch (pid = Fork()) { + case 0: + argv = PrepServerArgv( d, d->serverArgsLocal ); + if (d->authFile) { + if (!(argv = addStrArr( argv, "-auth", 5 )) || + !(argv = addStrArr( argv, d->authFile, -1 ))) + exit( 47 ); + } + Debug( "exec %\"[s\n", argv ); + /* + * give the server SIGUSR1 ignored, + * it will notice that and send SIGUSR1 + * when ready + */ + (void)Signal( SIGUSR1, SIG_IGN ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + + /* Let's try again with some standard paths */ + argv[0] = (char *)realloc(argv[0], strlen("/usr/X11R6/bin/X") + 1); + if (argv[0] != NULL) { + argv[0] = "/usr/X11R6/bin/X"; + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + + argv[0] = "/usr/bin/X"; /* Shorter than the previous file name */ + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + } + + exit( 47 ); + case -1: + LogError( "X server fork failed\n" ); + StartServerFailed(); + break; + default: + Debug( "X server forked, pid %d\n", pid ); + d->serverPid = pid; + serverTimeout = d->serverTimeout + now; + break; + } +} + +void +StartServer( struct display *d ) +{ + startingServer = d; + d->startTries = 0; + StartServerOnce(); +} + +void +AbortStartServer( struct display *d ) +{ + if (startingServer == d) + { + if (d->serverStatus != ignore) + { + d->serverStatus = ignore; + serverTimeout = TO_INF; + Debug( "aborting X server start\n" ); + } + startingServer = 0; + } +} + +void +StartServerSuccess() +{ + struct display *d = startingServer; + d->serverStatus = ignore; + serverTimeout = TO_INF; + Debug( "X server ready, starting session\n" ); + StartDisplayP2( d ); +} + +void +StartServerFailed() +{ + struct display *d = startingServer; + if (!d->serverAttempts || d->startTries < d->serverAttempts) { + d->serverStatus = pausing; + serverTimeout = d->openDelay + now; + } else { + d->serverStatus = ignore; + serverTimeout = TO_INF; + startingServer = 0; + LogError( "X server for display %s can't be started," + " session disabled\n", d->name ); + StopDisplay( d ); + } +} + +void +StartServerTimeout() +{ + struct display *d = startingServer; + switch (d->serverStatus) { + case ignore: + case awaiting: + break; /* cannot happen */ + case starting: + LogError( "X server startup timeout, terminating\n" ); + kill( d->serverPid, d->termSignal ); + d->serverStatus = d->termSignal == SIGKILL ? killed : terminated; + serverTimeout = d->serverTimeout + now; + break; + case terminated: + LogInfo( "X server termination timeout, killing\n" ); + kill( d->serverPid, SIGKILL ); + d->serverStatus = killed; + serverTimeout = 10 + now; + break; + case killed: + LogInfo( "X server is stuck in D state; leaving it alone\n" ); + StartServerFailed(); + break; + case pausing: + StartServerOnce(); + break; + } +} + + +Display *dpy; + +/* + * this code is complicated by some TCP failings. On + * many systems, the connect will occasionally hang forever, + * this trouble is avoided by setting up a timeout to Longjmp + * out of the connect (possibly leaving piles of garbage around + * inside Xlib) and give up, terminating the server. + */ + +static Jmp_buf openAbort; + +/* ARGSUSED */ +static void +abortOpen( int n ATTR_UNUSED ) +{ + Longjmp( openAbort, 1 ); +} + +#ifdef XDMCP + +#ifdef STREAMSCONN +# include <tiuser.h> +#endif + +static void +GetRemoteAddress( struct display *d, int fd ) +{ + char buf[512]; + int len = sizeof(buf); +#ifdef STREAMSCONN + struct netbuf netb; +#endif + + XdmcpDisposeARRAY8( &d->peer ); +#ifdef STREAMSCONN + netb.maxlen = sizeof(buf); + netb.buf = buf; + t_getname( fd, &netb, REMOTENAME ); + len = 8; + /* lucky for us, t_getname returns something that looks like a sockaddr */ +#else + getpeername( fd, (struct sockaddr *)buf, (void *)&len ); +#endif + if (len && XdmcpAllocARRAY8( &d->peer, len )) + memmove( (char *)d->peer.data, buf, len ); + Debug( "got remote address %s %d\n", d->name, d->peer.length ); +} + +#endif /* XDMCP */ + +static int +openErrorHandler( Display *dspl ATTR_UNUSED ) +{ + LogError( "IO Error in XOpenDisplay\n" ); + exit( EX_OPENFAILED_DPY ); + /*NOTREACHED*/ + return (0); +} + +void +WaitForServer( struct display *d ) +{ + volatile int i; + /* static int i; */ + + i = 0; + do { + (void)Signal( SIGALRM, abortOpen ); + (void)alarm( (unsigned)d->openTimeout ); + if (!Setjmp( openAbort )) { + Debug( "before XOpenDisplay(%s)\n", d->name ); + errno = 0; + (void)XSetIOErrorHandler( openErrorHandler ); + dpy = XOpenDisplay( d->name ); +#ifdef STREAMSCONN + { + /* For some reason, the next XOpenDisplay we do is + going to fail, so we might as well get that out + of the way. There is something broken here. */ + Display *bogusDpy = XOpenDisplay( d->name ); + Debug( "bogus XOpenDisplay %s\n", + bogusDpy ? "succeeded" : "failed" ); + if (bogusDpy) XCloseDisplay( bogusDpy ); /* just in case */ + } +#endif + (void)alarm( (unsigned)0 ); + (void)Signal( SIGALRM, SIG_DFL ); + (void)XSetIOErrorHandler( (int (*)( Display * )) 0 ); + Debug( "after XOpenDisplay(%s)\n", d->name ); + if (dpy) { +#ifdef XDMCP + if ((d->displayType & d_location) == dForeign) + GetRemoteAddress( d, ConnectionNumber( dpy ) ); +#endif + RegisterCloseOnFork( ConnectionNumber( dpy ) ); + return; + } + Debug( "OpenDisplay(%s) attempt %d failed: %m\n", d->name, i + 1 ); + sleep( (unsigned)d->openDelay ); + } else { + LogError( "Hung in XOpenDisplay(%s), aborting\n", d->name ); + (void)Signal( SIGALRM, SIG_DFL ); + break; + } + } while (++i < d->openRepeat); + LogError( "Cannot connect to %s, giving up\n", d->name ); + exit( EX_OPENFAILED_DPY ); +} + + +void +ResetServer( struct display *d ) +{ + if (dpy && (d->displayType & d_origin) != dFromXDMCP) + pseudoReset(); +} + + +static Jmp_buf pingTime; + +static void +PingLost( void ) +{ + Longjmp( pingTime, 1 ); +} + +/* ARGSUSED */ +static int +PingLostIOErr( Display *dspl ATTR_UNUSED ) +{ + PingLost(); + return 0; +} + +/* ARGSUSED */ +static void +PingLostSig( int n ATTR_UNUSED ) +{ + PingLost(); +} + +int +PingServer( struct display *d ) +{ + int (*oldError)( Display * ); + void (*oldSig)( int ); + int oldAlarm; + + oldError = XSetIOErrorHandler( PingLostIOErr ); + oldAlarm = alarm( 0 ); + oldSig = Signal( SIGALRM, PingLostSig ); + (void)alarm( d->pingTimeout * 60 ); + if (!Setjmp( pingTime )) { + Debug( "ping X server\n" ); + XSync( dpy, 0 ); + } else { + Debug( "X server dead\n" ); + (void)alarm( 0 ); + (void)Signal( SIGALRM, SIG_DFL ); + XSetIOErrorHandler( oldError ); + return 0; + } + (void)alarm( 0 ); + (void)Signal( SIGALRM, oldSig ); + (void)alarm( oldAlarm ); + Debug( "X server alive\n" ); + XSetIOErrorHandler( oldError ); + return 1; +} |