summaryrefslogtreecommitdiffstats
path: root/kdm/backend/session.c
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kdm/backend/session.c
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdm/backend/session.c')
-rw-r--r--kdm/backend/session.c792
1 files changed, 792 insertions, 0 deletions
diff --git a/kdm/backend/session.c b/kdm/backend/session.c
new file mode 100644
index 000000000..72da0a820
--- /dev/null
+++ b/kdm/backend/session.c
@@ -0,0 +1,792 @@
+/*
+
+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>
+
+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;
+
+ 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 );
+
+ if (!(clientPid = StartClient())) {
+ 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 );
+ }
+ }
+ /*
+ * 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;
+}