add more spacing
[personal-kdebase.git] / workspace / kdm / backend / session.c
blobd6371c7d278cc803f042fe30db98bbe998d9dea4
1 /*
3 Copyright 1988, 1998 The Open Group
4 Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation.
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
23 Except as contained in this notice, the name of a copyright holder shall
24 not be used in advertising or otherwise to promote the sale, use or
25 other dealings in this Software without prior written authorization
26 from the copyright holder.
31 * xdm - display manager daemon
32 * Author: Keith Packard, MIT X Consortium
34 * subdaemon event loop, etc.
37 #include "dm.h"
38 #include "dm_error.h"
40 #include <X11/Xlib.h>
41 #include <X11/Xatom.h>
42 #include <X11/cursorfont.h>
44 #include <stdio.h>
45 #include <ctype.h>
46 #include <signal.h>
48 struct display *td;
49 const char *td_setup = "auto";
51 static void deleteXloginResources( void );
52 static void loadXloginResources( void );
53 static void resetXProperties( void );
54 static void setupDisplay( const char *arg );
55 static char **dupEnv( void );
58 static Jmp_buf pingTime;
60 /* ARGSUSED */
61 static void
62 catchAlrm( int n ATTR_UNUSED )
64 Longjmp( pingTime, 1 );
67 static Jmp_buf tenaciousClient;
69 /* ARGSUSED */
70 static void
71 waitAbort( int n ATTR_UNUSED )
73 Longjmp( tenaciousClient, 1 );
76 static void
77 abortClient( int pid )
79 int sig = SIGTERM;
80 volatile int i;
81 int retId;
83 for (i = 0; i < 4; i++) {
84 if (kill( -pid, sig ) == -1) {
85 switch (errno) {
86 case EPERM:
87 logError( "Cannot kill client\n" );
88 case EINVAL:
89 case ESRCH:
90 return;
93 if (!Setjmp( tenaciousClient )) {
94 (void)Signal( SIGALRM, waitAbort );
95 (void)alarm( (unsigned)10 );
96 retId = wait( 0 );
97 (void)alarm( (unsigned)0 );
98 (void)Signal( SIGALRM, SIG_DFL );
99 if (retId == pid)
100 break;
101 } else
102 (void)Signal( SIGALRM, SIG_DFL );
103 sig = SIGKILL;
108 static char *
109 conv_auto( int what, const char *prompt ATTR_UNUSED )
111 switch (what) {
112 case GCONV_USER:
113 return curuser;
114 case GCONV_PASS:
115 case GCONV_PASS_ND:
116 return curpass;
117 default:
118 logError( "Unknown authentication data type requested for autologin.\n" );
119 return 0;
123 static void
124 doAutoLogon( void )
126 reStr( &curuser, td->autoUser );
127 reStr( &curpass, td->autoPass );
128 reStr( &curtype, "classic" );
129 cursource = PWSRC_AUTOLOGIN;
132 static int
133 autoLogon( time_t tdiff )
135 debug( "autoLogon, tdiff = %d, rLogin = %d, goodexit = %d, nuser = %s\n",
136 tdiff, td->hstent->rLogin, td->hstent->goodExit, td->hstent->nuser );
137 if (td->hstent->rLogin == 2 ||
138 (td->hstent->rLogin == 1 &&
139 tdiff <= 0 && !td->hstent->goodExit && !td->hstent->lock))
141 curuser = td->hstent->nuser;
142 td->hstent->nuser = 0;
143 curpass = td->hstent->npass;
144 td->hstent->npass = 0;
145 newdmrc = td->hstent->nargs;
146 td->hstent->nargs = 0;
147 reStr( &curtype, "classic" );
148 cursource = (td->hstent->rLogin == 1) ? PWSRC_RELOGIN : PWSRC_MANUAL;
149 return True;
150 } else if (*td->autoUser && !td->autoDelay &&
151 ((tdiff > 0 && ((td->displayType & d_lifetime) == dTransient ||
152 !td->hstent->lastExit)) ||
153 td->autoAgain))
155 unsigned int lmask;
156 Window dummy1, dummy2;
157 int dummy3, dummy4, dummy5, dummy6;
158 XQueryPointer( dpy, DefaultRootWindow( dpy ),
159 &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
160 &lmask );
161 if (lmask & ShiftMask)
162 return False;
163 doAutoLogon();
164 return True;
166 return False;
170 static const struct {
171 int vcode, echo, ndelay;
172 } grqs[] = {
173 { V_GET_TEXT, True, False },
174 { V_GET_TEXT, False, False },
175 { V_GET_TEXT, True, False },
176 { V_GET_TEXT, False, False },
177 { V_GET_TEXT, False, True },
178 { V_GET_BINARY, 0, 0 }
181 char *
182 conv_interact( int what, const char *prompt )
184 char *ret;
185 int tag;
187 gSendInt( grqs[what].vcode );
188 if (what == GCONV_BINARY) {
189 unsigned const char *up = (unsigned const char *)prompt;
190 int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
191 gSendArr( len, prompt );
192 gSendInt( False ); /* ndelay */
193 return gRecvArr( &len );
194 } else {
195 gSendStr( prompt );
196 gSendInt( grqs[what].echo );
197 gSendInt( grqs[what].ndelay );
198 ret = gRecvStr();
199 if (ret) {
200 tag = gRecvInt();
201 switch (what) {
202 case GCONV_USER:
203 /* assert( tag & V_IS_USER );*/
204 if (curuser)
205 free( curuser );
206 curuser = ret;
207 break;
208 case GCONV_PASS:
209 case GCONV_PASS_ND:
210 /* assert( tag & V_IS_PASSWORD );*/
211 wipeStr( curpass );
212 curpass = ret;
213 break;
214 default:
215 if (tag & V_IS_USER)
216 reStr( &curuser, ret );
217 else if (tag & V_IS_PASSWORD)
218 reStr( &curpass, ret );
219 else if (tag & V_IS_NEWPASSWORD)
220 reStr( &newpass, ret );
221 else if (tag & V_IS_OLDPASSWORD)
222 reStr( &ret, curpass );
225 return ret;
229 GProc grtproc;
230 GTalk grttalk;
232 GTalk mstrtalk; /* make static; see dm.c */
235 ctrlGreeterWait( int wreply )
237 int i, cmd, type, rootok;
238 char *name, *pass;
239 void **avptr;
240 #ifdef XDMCP
241 ARRAY8Ptr aptr;
242 #endif
244 if (Setjmp( mstrtalk.errjmp )) {
245 closeGreeter( True );
246 sessionExit( EX_UNMANAGE_DPY );
249 while (gRecvCmd( &cmd )) {
250 switch (cmd)
252 case G_Ready:
253 debug( "G_Ready\n" );
254 return 0;
255 case G_GetCfg:
256 /*debug( "G_GetCfg\n" );*/
257 type = gRecvInt();
258 /*debug( " index %#x\n", type );*/
259 if (type == C_isLocal)
260 i = (td->displayType & d_location) == dLocal;
261 else if (type == C_isReserve)
262 i = (td->displayType & d_lifetime) == dReserve;
263 else if (type == C_hasConsole)
264 #ifdef HAVE_VTS
265 i = *consoleTTYs != 0;
266 #else
267 i = td->console != 0;
268 #endif
269 else if (type == C_isAuthorized)
270 i = td->authorizations != 0;
271 else
272 goto normal;
273 gSendInt( GE_Ok );
274 /*debug( " -> bool %d\n", i );*/
275 gSendInt( i );
276 break;
277 normal:
278 if (!(avptr = findCfgEnt( td, type ))) {
279 /*debug( " -> not found\n" );*/
280 gSendInt( GE_NoEnt );
281 break;
283 switch (type & C_TYPE_MASK) {
284 default:
285 /*debug( " -> unknown type\n" );*/
286 gSendInt( GE_BadType );
287 break;
288 case C_TYPE_INT:
289 case C_TYPE_STR:
290 case C_TYPE_ARGV:
291 #ifdef XDMCP
292 case C_TYPE_ARR:
293 #endif
294 gSendInt( GE_Ok );
295 switch (type & C_TYPE_MASK) {
296 case C_TYPE_INT:
297 /*debug( " -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr );*/
298 gSendInt( *(int *)avptr );
299 break;
300 case C_TYPE_STR:
301 /*debug( " -> string %\"s\n", *avptr );*/
302 gSendStr( *(char **)avptr );
303 break;
304 case C_TYPE_ARGV:
305 /*debug( " -> sending argv %\"[{s\n", *(char ***)avptr );*/
306 gSendArgv( *(char ***)avptr );
307 break;
308 #ifdef XDMCP
309 case C_TYPE_ARR:
310 aptr = *(ARRAY8Ptr *)avptr;
311 /*debug( " -> sending array %02[*:hhx\n",
312 aptr->length, aptr->data );*/
313 gSendArr( aptr->length, (char *)aptr->data );
314 break;
315 #endif
317 break;
319 break;
320 case G_ReadDmrc:
321 debug( "G_ReadDmrc\n" );
322 name = gRecvStr();
323 debug( " user %\"s\n", name );
324 if (strCmp( dmrcuser, name )) {
325 if (curdmrc) { free( curdmrc ); curdmrc = 0; }
326 if (dmrcuser)
327 free( dmrcuser );
328 dmrcuser = name;
329 i = readDmrc();
330 debug( " -> status %d\n", i );
331 gSendInt( i );
332 debug( " => %\"s\n", curdmrc );
333 } else {
334 if (name)
335 free( name );
336 debug( " -> status " stringify( GE_Ok ) "\n" );
337 gSendInt( GE_Ok );
338 debug( " => keeping old\n" );
340 break;
341 case G_GetDmrc:
342 debug( "G_GetDmrc\n" );
343 name = gRecvStr();
344 debug( " key %\"s\n", name );
345 pass = iniEntry( curdmrc, "Desktop", name, 0 );
346 debug( " -> %\"s\n", pass );
347 gSendStr( pass );
348 if (pass)
349 free( pass );
350 free( name );
351 break;
352 /* case G_ResetDmrc:
353 debug( "G_ResetDmrc\n" );
354 if (newdmrc) { free( newdmrc ); newdmrc = 0; }
355 break; */
356 case G_PutDmrc:
357 debug( "G_PutDmrc\n" );
358 name = gRecvStr();
359 debug( " key %\"s\n", name );
360 pass = gRecvStr();
361 debug( " value %\"s\n", pass );
362 newdmrc = iniEntry( newdmrc, "Desktop", name, pass );
363 free( pass );
364 free( name );
365 break;
366 case G_VerifyRootOK:
367 debug( "G_VerifyRootOK\n" );
368 rootok = True;
369 goto doverify;
370 case G_Verify:
371 debug( "G_Verify\n" );
372 rootok = False;
373 doverify:
374 if (curuser) { free( curuser ); curuser = 0; }
375 wipeStr( curpass ); curpass = 0;
376 if (curtype) free( curtype );
377 curtype = gRecvStr();
378 debug( " type %\"s\n", curtype );
379 cursource = PWSRC_MANUAL;
380 if (verify( conv_interact, rootok )) {
381 debug( " -> return success\n" );
382 gSendInt( V_OK );
383 } else
384 debug( " -> failure returned\n" );
385 break;
386 case G_AutoLogin:
387 debug( "G_AutoLogin\n" );
388 doAutoLogon();
389 if (verify( conv_auto, False )) {
390 debug( " -> return success\n" );
391 gSendInt( V_OK );
392 } else
393 debug( " -> failure returned\n" );
394 break;
395 case G_SetupDpy:
396 debug( "G_SetupDpy\n" );
397 setupDisplay( 0 );
398 td_setup = 0;
399 gSendInt( 0 );
400 break;
401 default:
402 return cmd;
404 if (!wreply)
405 return -1;
407 debug( "lost connection to greeter\n" );
408 return -2;
411 void
412 openGreeter()
414 char *name, **env;
415 static time_t lastStart;
416 int cmd;
417 Cursor xcursor;
419 gSet( &grttalk );
420 if (grtproc.pid > 0)
421 return;
422 if (time( 0 ) < lastStart + 10) /* XXX should use some readiness indicator instead */
423 sessionExit( EX_UNMANAGE_DPY );
424 ASPrintf( &name, "greeter for display %s", td->name );
425 debug( "starting %s\n", name );
427 /* Hourglass cursor */
428 if ((xcursor = XCreateFontCursor( dpy, XC_watch ))) {
429 XDefineCursor( dpy, DefaultRootWindow( dpy ), xcursor );
430 XFreeCursor( dpy, xcursor );
432 XFlush( dpy );
434 /* Load system default Resources (if any) */
435 loadXloginResources();
437 grttalk.pipe = &grtproc.pipe;
438 env = systemEnv( dupEnv(), 0 );
439 if (gOpen( &grtproc, (char **)0, "_greet", env, name, &td->gpipe ))
440 sessionExit( EX_UNMANAGE_DPY );
441 freeStrArr( env );
442 if ((cmd = ctrlGreeterWait( True ))) {
443 logError( "Received unknown or unexpected command %d from greeter\n", cmd );
444 closeGreeter( True );
445 sessionExit( EX_UNMANAGE_DPY );
447 debug( "%s ready\n", name );
448 time( &lastStart );
452 closeGreeter( int force )
454 int ret;
456 if (grtproc.pid <= 0)
457 return EX_NORMAL;
459 ret = gClose( &grtproc, 0, force );
460 debug( "greeter for %s stopped\n", td->name );
461 if (wcCode( ret ) > EX_NORMAL && wcCode( ret ) <= EX_MAX) {
462 debug( "greeter-initiated session exit, code %d\n", wcCode( ret ) );
463 sessionExit( wcCode( ret ) );
466 deleteXloginResources();
468 return ret;
471 void
472 prepareErrorGreet()
474 if (grtproc.pid <= 0) {
475 openGreeter();
476 gSendInt( G_ErrorGreet );
477 gSendStr( curuser );
479 gSet( &grttalk );
482 void
483 finishGreet()
485 int ret;
487 if (grtproc.pid > 0) {
488 gSet( &grttalk );
489 gSendInt( V_OK );
490 if ((ret = closeGreeter( False )) != EX_NORMAL) {
491 logError( "Abnormal greeter termination, code %d, sig %d\n",
492 wcCode( ret ), wcSig( ret ) );
493 sessionExit( EX_RESERVER_DPY );
498 static Jmp_buf idleTOJmp;
500 /* ARGSUSED */
501 static void
502 IdleTimeout( int n ATTR_UNUSED )
504 Longjmp( idleTOJmp, 1 );
508 static Jmp_buf abortSession;
510 /* ARGSUSED */
511 static void
512 catchTerm( int n ATTR_UNUSED )
514 Signal( SIGTERM, SIG_IGN );
515 Longjmp( abortSession, EX_AL_RESERVER_DPY );
519 * We need our own error handlers because we can't be sure what exit code Xlib
520 * will use, and our Xlib does exit(1) which matches EX_REMANAGE_DPY, which
521 * can cause a race condition leaving the display wedged. We need to use
522 * EX_RESERVER_DPY for IO errors, to ensure that the manager waits for the
523 * server to terminate. For other X errors, we should give up.
526 /*ARGSUSED*/
527 static int
528 IOErrorHandler( Display *dspl ATTR_UNUSED )
530 logError( "Fatal X server IO error: %m\n" );
531 /* The only X interaction during the session are pings, and those
532 have an own IOErrorHandler -> not EX_AL_RESERVER_DPY */
533 Longjmp( abortSession, EX_RESERVER_DPY );
534 /*NOTREACHED*/
535 return 0;
538 /*ARGSUSED*/
539 static int
540 errorHandler( Display *dspl ATTR_UNUSED, XErrorEvent *event )
542 logError( "X error\n" );
543 if (event->error_code == BadImplementation)
544 Longjmp( abortSession, EX_UNMANAGE_DPY );
545 return 0;
548 void
549 manageSession( void )
551 int ex, cmd;
552 volatile int clientPid = -1;
553 volatile time_t tdiff = 0;
554 sigset_t ss;
556 debug( "manageSession %s\n", td->name );
557 if ((ex = Setjmp( abortSession ))) {
558 closeGreeter( True );
559 if (clientPid > 0)
560 abortClient( clientPid );
561 sessionExit( ex );
562 /* NOTREACHED */
564 (void)XSetIOErrorHandler( IOErrorHandler );
565 (void)XSetErrorHandler( errorHandler );
566 (void)Signal( SIGTERM, catchTerm );
568 (void)Signal( SIGHUP, SIG_IGN );
570 if (Setjmp( grttalk.errjmp ))
571 Longjmp( abortSession, EX_RESERVER_DPY ); /* EX_RETRY_ONCE */
573 #ifdef XDMCP
574 if (td->useChooser)
575 doChoose();
576 /* NOTREACHED */
577 #endif
579 tdiff = time( 0 ) - td->hstent->lastExit - td->openDelay;
580 if (autoLogon( tdiff )) {
581 if (!verify( conv_auto, False ))
582 goto gcont;
583 } else {
584 regreet:
585 openGreeter();
586 if (Setjmp( idleTOJmp )) {
587 closeGreeter( True );
588 sessionExit( EX_NORMAL );
590 Signal( SIGALRM, IdleTimeout );
591 alarm( td->idleTimeout );
592 #ifdef XDMCP
593 if (((td->displayType & d_location) == dLocal) &&
594 td->loginMode >= LOGIN_DEFAULT_REMOTE)
595 goto choose;
596 #endif
597 for (;;) {
598 debug( "manageSession, greeting, tdiff = %d\n", tdiff );
599 gSendInt( (*td->autoUser && td->autoDelay &&
600 (tdiff > 0 || td->autoAgain)) ?
601 G_GreetTimed : G_Greet );
602 gcont:
603 cmd = ctrlGreeterWait( True );
604 #ifdef XDMCP
605 recmd:
606 if (cmd == G_DChoose) {
607 choose:
608 cmd = doChoose();
609 goto recmd;
611 if (cmd == G_DGreet)
612 continue;
613 #endif
614 alarm( 0 );
615 if (cmd == G_Ready)
616 break;
617 if (cmd == -2)
618 closeGreeter( False );
619 else {
620 logError( "Received unknown command %d from greeter\n", cmd );
621 closeGreeter( True );
623 goto regreet;
627 if (td_setup)
628 setupDisplay( td_setup );
630 if (!startClient( &clientPid )) {
631 logError( "Client start failed\n" );
632 sessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */
634 debug( "client Started\n" );
637 * Wait for session to end,
639 for (;;) {
640 if (!Setjmp( pingTime )) {
641 (void)Signal( SIGALRM, catchAlrm );
642 (void)alarm( td->pingInterval * 60 ); /* may be 0 */
643 (void)Wait4( &clientPid );
644 (void)alarm( 0 );
645 break;
646 } else {
647 (void)alarm( 0 );
648 if (!pingServer( td ))
649 catchTerm( SIGTERM );
653 * Sometimes the Xsession somehow manages to exit before
654 * a server crash is noticed - so we sleep a bit and wait
655 * for being killed.
657 if (!pingServer( td )) {
658 debug( "X server dead upon session exit.\n" );
659 if ((td->displayType & d_location) == dLocal)
660 sleep( 10 );
661 sessionExit( EX_AL_RESERVER_DPY );
664 sigemptyset( &ss );
665 sigaddset( &ss, SIGTERM );
666 sigprocmask( SIG_BLOCK, &ss, 0 );
667 clientExited();
668 sigprocmask( SIG_UNBLOCK, &ss, 0 );
670 gSet( &mstrtalk );
671 gSendInt( D_UnUser );
672 if (gRecvInt()) {
673 resetXProperties();
674 openGreeter();
675 gSendInt( G_ConfShutdown );
676 if ((cmd = ctrlGreeterWait( True )) != G_Ready) {
677 logError( "Received unknown command %d from greeter\n", cmd );
678 closeGreeter( True );
682 sessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */
685 static int xResLoaded;
687 void
688 loadXloginResources()
690 char **args;
691 char **env;
693 if (!xResLoaded && td->resources[0] && access( td->resources, 4 ) == 0) {
694 env = systemEnv( 0, 0 );
695 if ((args = parseArgs( (char **)0, td->xrdb )) &&
696 (args = addStrArr( args, td->resources, -1 )))
698 debug( "loading resource file: %s\n", td->resources );
699 (void)runAndWait( args, env );
700 freeStrArr( args );
702 freeStrArr( env );
703 xResLoaded = True;
707 void
708 setupDisplay( const char *arg )
710 char **env;
712 env = systemEnv( 0, 0 );
713 (void)source( env, td->setup, arg );
714 freeStrArr( env );
717 void
718 deleteXloginResources()
720 int i;
721 Atom prop;
723 if (!xResLoaded)
724 return;
725 xResLoaded = False;
726 prop = XInternAtom( dpy, "SCREEN_RESOURCES", True );
727 XDeleteProperty( dpy, RootWindow( dpy, 0 ), XA_RESOURCE_MANAGER );
728 if (prop)
729 for (i = ScreenCount(dpy); --i >= 0; )
730 XDeleteProperty( dpy, RootWindow( dpy, i ), prop );
731 XSync( dpy, False );
734 void
735 resetXProperties()
737 int i, j, nprops;
738 char *name;
739 Atom *props;
741 for (i = ScreenCount(dpy); --i >= 0; )
742 if ((props = XListProperties( dpy, RootWindow( dpy, i ), &nprops ))) {
743 for (j = 0; j < nprops; j++)
744 if ((name = XGetAtomName( dpy, props[j] ))) {
745 if (!memcmp( name, "_NET_", 5 ))
746 XDeleteProperty( dpy, RootWindow( dpy, i ), props[j] );
747 XFree( name );
749 XFree( props );
751 XSync( dpy, False );
756 source( char **env, const char *file, const char *arg )
758 char **args;
759 int ret;
761 if (file && file[0]) {
762 debug( "source %s\n", file );
763 if (!(args = parseArgs( (char **)0, file )))
764 return wcCompose( 0, 0, 127 );
765 if (arg && !(args = addStrArr( args, arg, -1 )))
766 return wcCompose( 0, 0, 127 );
767 ret = runAndWait( args, env );
768 freeStrArr( args );
769 return ret;
771 return 0;
774 static char **
775 dupEnv( void )
777 int i, l = arrLen( environ );
778 char **env;
779 if (!(env = Malloc( (l + 1) * sizeof(char *) )))
780 return 0;
781 for (i = 0; i < l; i++)
782 strDup( env + i, environ[i] );
783 env[i] = 0;
784 return env;
787 char **
788 inheritEnv( char **env, const char **what )
790 char *value;
792 for (; *what; ++what)
793 if ((value = getenv( *what )))
794 env = setEnv( env, *what, value );
795 return env;
798 char **
799 baseEnv( char **env, const char *user )
801 if (!env)
802 env = inheritEnv( 0, (const char **)exportList );
804 if (user) {
805 env = setEnv( env, "USER", user );
806 env = setEnv( env, "LOGNAME", user );
809 env = setEnv( env, "DISPLAY", displayName( td ) );
811 #ifdef HAVE_VTS
812 /* Support for assistive technologies. */
813 if (td->serverVT > 0) {
814 char vtstr[4];
815 sprintf( vtstr, "%d", td->serverVT );
816 env = setEnv( env, "WINDOWPATH", vtstr );
818 #endif
820 if (td->ctrl.path)
821 env = setEnv( env, "DM_CONTROL", fifoDir );
823 return env;
826 char **
827 systemEnv( char **env, const char *user )
829 env = baseEnv( env, user );
830 if (td->authFile)
831 env = setEnv( env, "XAUTHORITY", td->authFile );
832 env = setEnv( env, "PATH", td->systemPath );
833 env = setEnv( env, "SHELL", td->systemShell );
834 return env;