diff options
Diffstat (limited to 'tdm/backend/session.c')
-rw-r--r-- | tdm/backend/session.c | 813 |
1 files changed, 813 insertions, 0 deletions
diff --git a/tdm/backend/session.c b/tdm/backend/session.c new file mode 100644 index 000000000..9a12ce312 --- /dev/null +++ b/tdm/backend/session.c @@ -0,0 +1,813 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org> + +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 + * + * subdaemon event loop, etc. + */ + +#include "dm.h" +#include "dm_error.h" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/cursorfont.h> + +#include <stdio.h> +#include <ctype.h> +#include <signal.h> + +#ifdef WITH_CONSOLE_KIT +#include "consolekit.h" +#endif + +struct display *td; +const char *td_setup = "auto"; + +static void DeleteXloginResources( void ); +static void LoadXloginResources( void ); +static void SetupDisplay( const char *arg ); + + +static Jmp_buf pingTime; + +/* ARGSUSED */ +static void +catchAlrm( int n ATTR_UNUSED ) +{ + Longjmp( pingTime, 1 ); +} + +static Jmp_buf tenaciousClient; + +/* ARGSUSED */ +static void +waitAbort( int n ATTR_UNUSED ) +{ + Longjmp( tenaciousClient, 1 ); +} + +static void +AbortClient( int pid ) +{ + int sig = SIGTERM; + volatile int i; + int retId; + + for (i = 0; i < 4; i++) { + if (kill( -pid, sig ) == -1) { + switch (errno) { + case EPERM: + LogError( "Can't kill client\n" ); + case EINVAL: + case ESRCH: + return; + } + } + if (!Setjmp( tenaciousClient )) { + (void)Signal( SIGALRM, waitAbort ); + (void)alarm( (unsigned)10 ); + retId = wait( (waitType *)0 ); + (void)alarm( (unsigned)0 ); + (void)Signal( SIGALRM, SIG_DFL ); + if (retId == pid) + break; + } else + (void)Signal( SIGALRM, SIG_DFL ); + sig = SIGKILL; + } +} + + +static char * +conv_auto( int what, const char *prompt ATTR_UNUSED ) +{ + switch (what) { + case GCONV_USER: + return curuser; + case GCONV_PASS: + case GCONV_PASS_ND: + return curpass; + default: + LogError( "Unknown authentication data type requested for autologin.\n" ); + return 0; + } +} + +static void +DoAutoLogon( void ) +{ + ReStr( &curuser, td->autoUser ); + ReStr( &curpass, td->autoPass ); + ReStr( &curtype, "classic" ); + cursource = PWSRC_AUTOLOGIN; +} + +static int +AutoLogon( Time_t tdiff ) +{ + Debug( "autoLogon, tdiff = %d, rLogin = %d, goodexit = %d, nuser = %s\n", + tdiff, td->hstent->rLogin, td->hstent->goodExit, td->hstent->nuser ); + if (td->hstent->rLogin == 2 || + (td->hstent->rLogin == 1 && + tdiff <= 0 && !td->hstent->goodExit && !td->hstent->lock)) + { + curuser = td->hstent->nuser; + td->hstent->nuser = 0; + curpass = td->hstent->npass; + td->hstent->npass = 0; + newdmrc = td->hstent->nargs; + td->hstent->nargs = 0; + ReStr( &curtype, "classic" ); + cursource = (td->hstent->rLogin == 1) ? PWSRC_RELOGIN : PWSRC_MANUAL; + return 1; + } else if (*td->autoUser && !td->autoDelay && + ((tdiff > 0 && ((td->displayType & d_lifetime) == dTransient || + !td->hstent->lastExit)) || + td->autoAgain)) + { + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer( dpy, DefaultRootWindow( dpy ), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &lmask ); + if (lmask & ShiftMask) + return 0; + DoAutoLogon(); + return 1; + } + return 0; +} + + +static const struct { + int vcode, echo, ndelay; +} grqs[] = { + { V_GET_TEXT, TRUE, FALSE }, + { V_GET_TEXT, FALSE, FALSE }, + { V_GET_TEXT, TRUE, FALSE }, + { V_GET_TEXT, FALSE, FALSE }, + { V_GET_TEXT, FALSE, TRUE }, + { V_GET_BINARY, 0, 0 } +}; + +char * +conv_interact( int what, const char *prompt ) +{ + char *ret; + int tag; + + GSendInt( grqs[what].vcode ); + if (what == GCONV_BINARY) { + unsigned const char *up = (unsigned const char *)prompt; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + GSendArr( len, prompt ); + GSendInt( FALSE ); /* ndelay */ + return GRecvArr( &len ); + } else { + GSendStr( prompt ); + GSendInt( grqs[what].echo ); + GSendInt( grqs[what].ndelay ); + ret = GRecvStr(); + if (ret) { + tag = GRecvInt(); + switch (what) { + case GCONV_USER: + /* assert(tag & V_IS_USER); */ + if (curuser) + free( curuser ); + curuser = ret; + break; + case GCONV_PASS: + case GCONV_PASS_ND: + /* assert(tag & V_IS_PASSWORD); */ + if (curpass) + free( curpass ); + curpass = ret; + break; + default: + if (tag & V_IS_USER) + ReStr( &curuser, ret ); + else if (tag & V_IS_PASSWORD) + ReStr( &curpass, ret ); + else if (tag & V_IS_NEWPASSWORD) + ReStr( &newpass, ret ); + else if (tag & V_IS_OLDPASSWORD) + ReStr( &ret, curpass ); + } + } + return ret; + } +} + +static int greeter; +GProc grtproc; +GTalk grttalk; + +GTalk mstrtalk; /* make static; see dm.c */ + +int +CtrlGreeterWait( int wreply ) +{ + int i, cmd, type, rootok; + char *name, *pass, **avptr; +#ifdef XDMCP + ARRAY8Ptr aptr; +#endif + + if (Setjmp( mstrtalk.errjmp )) { + CloseGreeter( TRUE ); + SessionExit( EX_UNMANAGE_DPY ); + } + + while (GRecvCmd( &cmd )) { + switch (cmd) + { + case G_Ready: + Debug( "G_Ready\n" ); + return 0; + case G_GetCfg: + /*Debug ("G_GetCfg\n");*/ + type = GRecvInt(); + /*Debug (" index %#x\n", type);*/ + if (type == C_isLocal) + i = (td->displayType & d_location) == dLocal; + else if (type == C_hasConsole) +#ifdef HAVE_VTS + i = *consoleTTYs != 0; +#else + i = td->console != 0; +#endif + else if (type == C_isAuthorized) + i = td->authorizations != 0; + else + goto normal; + GSendInt( GE_Ok ); + /*Debug (" -> bool %d\n", i);*/ + GSendInt( i ); + break; + normal: + if (!(avptr = FindCfgEnt( td, type ))) { + /*Debug (" -> not found\n");*/ + GSendInt( GE_NoEnt ); + break; + } + switch (type & C_TYPE_MASK) { + default: + /*Debug (" -> unknown type\n");*/ + GSendInt( GE_BadType ); + break; + case C_TYPE_INT: + case C_TYPE_STR: + case C_TYPE_ARGV: +#ifdef XDMCP + case C_TYPE_ARR: +#endif + GSendInt( GE_Ok ); + switch (type & C_TYPE_MASK) { + case C_TYPE_INT: + /*Debug (" -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr);*/ + GSendInt( *(long *)avptr ); + break; + case C_TYPE_STR: + /*Debug (" -> string %\"s\n", *avptr);*/ + GSendStr( *avptr ); + break; + case C_TYPE_ARGV: + /*Debug (" -> sending argv %\"[{s\n", *(char ***)avptr);*/ + GSendArgv( *(char ***)avptr ); + break; +#ifdef XDMCP + case C_TYPE_ARR: + aptr = *(ARRAY8Ptr *)avptr; + /*Debug (" -> sending array %02[*:hhx\n", + aptr->length, aptr->data);*/ + GSendArr( aptr->length, (char *)aptr->data ); + break; +#endif + } + break; + } + break; + case G_ReadDmrc: + Debug( "G_ReadDmrc\n" ); + name = GRecvStr(); + Debug( " user %\"s\n", name ); + if (StrCmp( dmrcuser, name )) { + if (curdmrc) { free( curdmrc ); curdmrc = 0; } + if (dmrcuser) + free( dmrcuser ); + dmrcuser = name; + i = ReadDmrc(); + Debug( " -> status %d\n", i ); + GSendInt( i ); + Debug( " => %\"s\n", curdmrc ); + } else { + if (name) + free( name ); + Debug( " -> status " stringify( GE_Ok ) "\n" ); + GSendInt( GE_Ok ); + Debug( " => keeping old\n" ); + } + break; + case G_GetDmrc: + Debug( "G_GetDmrc\n" ); + name = GRecvStr(); + Debug( " key %\"s\n", name ); + pass = iniEntry( curdmrc, "Desktop", name, 0 ); + Debug( " -> %\"s\n", pass ); + GSendStr( pass ); + if (pass) + free( pass ); + free( name ); + break; +/* case G_ResetDmrc: + Debug ("G_ResetDmrc\n"); + if (newdmrc) { free (newdmrc); newdmrc = 0; } + break; */ + case G_PutDmrc: + Debug( "G_PutDmrc\n" ); + name = GRecvStr(); + Debug( " key %\"s\n", name ); + pass = GRecvStr(); + Debug( " value %\"s\n", pass ); + newdmrc = iniEntry( newdmrc, "Desktop", name, pass ); + free( pass ); + free( name ); + break; + case G_VerifyRootOK: + Debug( "G_VerifyRootOK\n" ); + rootok = TRUE; + goto doverify; + case G_Verify: + Debug( "G_Verify\n" ); + rootok = FALSE; + doverify: + if (curuser) { free( curuser ); curuser = 0; } + if (curpass) { free( curpass ); curpass = 0; } + if (curtype) free( curtype ); + curtype = GRecvStr(); + Debug( " type %\"s\n", curtype ); + cursource = PWSRC_MANUAL; + if (Verify( conv_interact, rootok )) { + Debug( " -> return success\n" ); + GSendInt( V_OK ); + } else + Debug( " -> failure returned\n" ); + break; + case G_AutoLogin: + Debug( "G_AutoLogin\n" ); + DoAutoLogon(); + if (Verify( conv_auto, FALSE )) { + Debug( " -> return success\n" ); + GSendInt( V_OK ); + } else + Debug( " -> failure returned\n" ); + break; + case G_SetupDpy: + Debug( "G_SetupDpy\n" ); + SetupDisplay( 0 ); + td_setup = 0; + GSendInt( 0 ); + break; + default: + return cmd; + } + if (!wreply) + return -1; + } + Debug( "lost connection to greeter\n" ); + return -2; +} + +void +OpenGreeter() +{ + char *name, **env; + static Time_t lastStart; + int cmd; + Cursor xcursor; + + GSet( &grttalk ); + if (greeter) + return; + if (time( 0 ) < lastStart + 10) /* XXX should use some readiness indicator instead */ + SessionExit( EX_UNMANAGE_DPY ); + greeter = 1; + ASPrintf( &name, "greeter for display %s", td->name ); + Debug( "starting %s\n", name ); + + /* Hourglass cursor */ + if ((xcursor = XCreateFontCursor( dpy, XC_watch ))) { + XDefineCursor( dpy, DefaultRootWindow( dpy ), xcursor ); + XFreeCursor( dpy, xcursor ); + } + XFlush( dpy ); + + /* Load system default Resources (if any) */ + LoadXloginResources(); + + grttalk.pipe = &grtproc.pipe; + env = systemEnv( (char *)0 ); + if (GOpen( &grtproc, (char **)0, "_greet", env, name, &td->gpipe )) + SessionExit( EX_UNMANAGE_DPY ); + freeStrArr( env ); + if ((cmd = CtrlGreeterWait( TRUE ))) { + if (cmd != -2) + LogError( "Received unknown or unexpected command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + SessionExit( EX_UNMANAGE_DPY ); + } + Debug( "%s ready\n", name ); + time( &lastStart ); +} + +int +CloseGreeter( int force ) +{ + int ret; + + if (!greeter) + return EX_NORMAL; + greeter = 0; + ret = GClose (&grtproc, 0, force); + Debug( "greeter for %s stopped\n", td->name ); + if (WaitCode( ret ) > EX_NORMAL && WaitCode( ret ) <= EX_MAX) { + Debug( "greeter-initiated session exit, code %d\n", WaitCode( ret ) ); + SessionExit( WaitCode( ret ) ); + } + return ret; +} + +void +PrepErrorGreet() +{ + if (!greeter) { + OpenGreeter(); + GSendInt( G_ErrorGreet ); + GSendStr( curuser ); + } +} + +static Jmp_buf idleTOJmp; + +/* ARGSUSED */ +static void +IdleTOJmp( int n ATTR_UNUSED ) +{ + Longjmp( idleTOJmp, 1 ); +} + + +static Jmp_buf abortSession; + +/* ARGSUSED */ +static void +catchTerm( int n ATTR_UNUSED ) +{ + Signal( SIGTERM, SIG_IGN ); + Longjmp( abortSession, EX_AL_RESERVER_DPY ); +} + +/* + * We need our own error handlers because we can't be sure what exit code Xlib + * will use, and our Xlib does exit(1) which matches EX_REMANAGE_DPY, which + * can cause a race condition leaving the display wedged. We need to use + * EX_RESERVER_DPY for IO errors, to ensure that the manager waits for the + * server to terminate. For other X errors, we should give up. + */ + +/*ARGSUSED*/ +static int +IOErrorHandler( Display *dspl ATTR_UNUSED ) +{ + LogError( "Fatal X server IO error: %m\n" ); + /* The only X interaction during the session are pings, and those + have an own IOErrorHandler -> not EX_AL_RESERVER_DPY */ + Longjmp( abortSession, EX_RESERVER_DPY ); + /*NOTREACHED*/ + return 0; +} + +/*ARGSUSED*/ +static int +ErrorHandler( Display *dspl ATTR_UNUSED, XErrorEvent *event ) +{ + LogError( "X error\n" ); + if (event->error_code == BadImplementation) + Longjmp( abortSession, EX_UNMANAGE_DPY ); + return 0; +} + +void +ManageSession( struct display *d ) +{ + int ex, cmd; + volatile int clientPid = 0; + volatile Time_t tdiff = 0; +#ifdef WITH_CONSOLE_KIT + char *ck_session_cookie; +#endif + + + td = d; + Debug( "ManageSession %s\n", d->name ); + if ((ex = Setjmp( abortSession ))) { + CloseGreeter( TRUE ); + if (clientPid) + AbortClient( clientPid ); + SessionExit( ex ); + /* NOTREACHED */ + } + (void)XSetIOErrorHandler( IOErrorHandler ); + (void)XSetErrorHandler( ErrorHandler ); + (void)Signal( SIGTERM, catchTerm ); + + (void)Signal( SIGHUP, SIG_IGN ); + + if (Setjmp( grttalk.errjmp )) + Longjmp( abortSession, EX_RESERVER_DPY ); /* EX_RETRY_ONCE */ + +#ifdef XDMCP + if (d->useChooser) + DoChoose(); + /* NOTREACHED */ +#endif + + if (d->hstent->sdRec.how) { + OpenGreeter(); + GSendInt( G_ConfShutdown ); + GSendInt( d->hstent->sdRec.how ); + GSendInt( d->hstent->sdRec.uid ); + GSendStr( d->hstent->sdRec.osname ); + if ((cmd = CtrlGreeterWait( TRUE )) != G_Ready) { + LogError( "Received unknown command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + } + goto regreet; + } + + tdiff = time( 0 ) - td->hstent->lastExit - td->openDelay; + if (AutoLogon( tdiff )) { + if (!Verify( conv_auto, FALSE )) + goto gcont; + if (greeter) + GSendInt( V_OK ); + } else { + regreet: + OpenGreeter(); + if (Setjmp( idleTOJmp )) { + CloseGreeter( TRUE ); + SessionExit( EX_NORMAL ); + } + Signal( SIGALRM, IdleTOJmp ); + alarm( td->idleTimeout ); +#ifdef XDMCP + if (((d->displayType & d_location) == dLocal) && + d->loginMode >= LOGIN_DEFAULT_REMOTE) + goto choose; +#endif + for (;;) { + Debug( "ManageSession, greeting, tdiff = %d\n", tdiff ); + GSendInt( (*td->autoUser && td->autoDelay && + (tdiff > 0 || td->autoAgain)) ? + G_GreetTimed : G_Greet ); + gcont: + cmd = CtrlGreeterWait( TRUE ); +#ifdef XDMCP + recmd: + if (cmd == G_DChoose) { + choose: + cmd = DoChoose(); + goto recmd; + } + if (cmd == G_DGreet) + continue; +#endif + alarm( 0 ); + if (cmd == G_Ready) + break; + if (cmd == -2) + CloseGreeter( FALSE ); + else { + LogError( "Received unknown command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + } + goto regreet; + } + } + + if (CloseGreeter( FALSE ) != EX_NORMAL) + goto regreet; + + DeleteXloginResources(); + + if (td_setup) + SetupDisplay( td_setup ); + +#ifdef WITH_CONSOLE_KIT + ck_session_cookie = open_ck_session (getpwnam(curuser), d); + if (!(clientPid = StartClient(ck_session_cookie))) { +#else + if (!(clientPid = StartClient())) { +#endif + LogError( "Client start failed\n" ); + SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */ + } + Debug( "client Started\n" ); + + /* + * Wait for session to end, + */ + for (;;) { + if (!Setjmp( pingTime )) { + (void)Signal( SIGALRM, catchAlrm ); + (void)alarm( d->pingInterval * 60 ); /* may be 0 */ + (void)Wait4( clientPid ); + (void)alarm( 0 ); + break; + } else { + (void)alarm( 0 ); + if (!PingServer( d )) + catchTerm( SIGTERM ); + } + } + +#ifdef WITH_CONSOLE_KIT + if (ck_session_cookie != NULL) { + close_ck_session (ck_session_cookie); + free (ck_session_cookie); + } +#endif + + /* + * Sometimes the Xsession somehow manages to exit before + * a server crash is noticed - so we sleep a bit and wait + * for being killed. + */ + if (!PingServer( d )) { + Debug( "X server dead upon session exit.\n" ); + if ((d->displayType & d_location) == dLocal) + sleep( 10 ); + SessionExit( EX_AL_RESERVER_DPY ); + } + SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */ +} + +static int xResLoaded; + +void +LoadXloginResources() +{ + char **args; + char **env; + + if (!xResLoaded && td->resources[0] && access( td->resources, 4 ) == 0) { + env = systemEnv( (char *)0 ); + if ((args = parseArgs( (char **)0, td->xrdb )) && + (args = addStrArr( args, td->resources, -1 ))) + { + Debug( "loading resource file: %s\n", td->resources ); + (void)runAndWait( args, env ); + freeStrArr( args ); + } + freeStrArr( env ); + xResLoaded = TRUE; + } +} + +void +SetupDisplay( const char *arg ) +{ + char **env; + + env = systemEnv( (char *)0 ); + (void)source( env, td->setup, arg ); + freeStrArr( env ); +} + +void +DeleteXloginResources() +{ + int i; + Atom prop; + + if (!xResLoaded) + return; + xResLoaded = FALSE; + prop = XInternAtom( dpy, "SCREEN_RESOURCES", True ); + XDeleteProperty( dpy, RootWindow( dpy, 0 ), XA_RESOURCE_MANAGER ); + if (prop) + for (i = ScreenCount(dpy); --i >= 0; ) + XDeleteProperty( dpy, RootWindow( dpy, i ), prop ); + XSync( dpy, 0 ); +} + + +int +source( char **env, const char *file, const char *arg ) +{ + char **args; + int ret; + + if (file && file[0]) { + Debug( "source %s\n", file ); + if (!(args = parseArgs( (char **)0, file ))) + return waitCompose( 0,0,3 ); + if (arg && !(args = addStrArr( args, arg, -1 ))) + return waitCompose( 0,0,3 ); + ret = runAndWait( args, env ); + freeStrArr( args ); + return ret; + } + return 0; +} + +char ** +inheritEnv( char **env, const char **what ) +{ + char *value; + + for (; *what; ++what) + if ((value = getenv( *what ))) + env = setEnv( env, *what, value ); + return env; +} + +char ** +baseEnv( const char *user ) +{ + char **env; + + env = 0; + +#ifdef _AIX + /* we need the tags SYSENVIRON: and USRENVIRON: in the call to setpenv() */ + env = setEnv( env, "SYSENVIRON:", 0 ); +#endif + + if (user) { + env = setEnv( env, "USER", user ); +#ifdef _AIX + env = setEnv( env, "LOGIN", user ); +#endif + env = setEnv( env, "LOGNAME", user ); + } + +#ifdef _AIX + env = setEnv( env, "USRENVIRON:", 0 ); +#endif + + env = inheritEnv( env, (const char **)exportList ); + + env = setEnv( env, "DISPLAY", + memcmp( td->name, "localhost:", 10 ) ? + td->name : td->name + 9 ); + + if (td->ctrl.path) + env = setEnv( env, "DM_CONTROL", fifoDir ); + + return env; +} + +char ** +systemEnv( const char *user ) +{ + char **env; + + env = baseEnv( user ); + if (td->authFile) + env = setEnv( env, "XAUTHORITY", td->authFile ); + env = setEnv( env, "PATH", td->systemPath ); + env = setEnv( env, "SHELL", td->systemShell ); + return env; +} |