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
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.
41 #include <X11/Xatom.h>
42 #include <X11/cursorfont.h>
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
;
62 catchAlrm( int n ATTR_UNUSED
)
64 Longjmp( pingTime
, 1 );
67 static Jmp_buf tenaciousClient
;
71 waitAbort( int n ATTR_UNUSED
)
73 Longjmp( tenaciousClient
, 1 );
77 abortClient( int pid
)
83 for (i
= 0; i
< 4; i
++) {
84 if (kill( -pid
, sig
) == -1) {
87 logError( "Cannot kill client\n" );
93 if (!Setjmp( tenaciousClient
)) {
94 (void)Signal( SIGALRM
, waitAbort
);
95 (void)alarm( (unsigned)10 );
97 (void)alarm( (unsigned)0 );
98 (void)Signal( SIGALRM
, SIG_DFL
);
102 (void)Signal( SIGALRM
, SIG_DFL
);
109 conv_auto( int what
, const char *prompt ATTR_UNUSED
)
118 logError( "Unknown authentication data type requested for autologin.\n" );
126 reStr( &curuser
, td
->autoUser
);
127 reStr( &curpass
, td
->autoPass
);
128 reStr( &curtype
, "classic" );
129 cursource
= PWSRC_AUTOLOGIN
;
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
;
150 } else if (*td
->autoUser
&& !td
->autoDelay
&&
151 ((tdiff
> 0 && ((td
->displayType
& d_lifetime
) == dTransient
||
152 !td
->hstent
->lastExit
)) ||
156 Window dummy1
, dummy2
;
157 int dummy3
, dummy4
, dummy5
, dummy6
;
158 XQueryPointer( dpy
, DefaultRootWindow( dpy
),
159 &dummy1
, &dummy2
, &dummy3
, &dummy4
, &dummy5
, &dummy6
,
161 if (lmask
& ShiftMask
)
170 static const struct {
171 int vcode
, echo
, ndelay
;
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 }
182 conv_interact( int what
, const char *prompt
)
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
);
196 gSendInt( grqs
[what
].echo
);
197 gSendInt( grqs
[what
].ndelay
);
203 /* assert( tag & V_IS_USER );*/
210 /* assert( tag & V_IS_PASSWORD );*/
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
);
232 GTalk mstrtalk
; /* make static; see dm.c */
235 ctrlGreeterWait( int wreply
)
237 int i
, cmd
, type
, rootok
;
244 if (Setjmp( mstrtalk
.errjmp
)) {
245 closeGreeter( True
);
246 sessionExit( EX_UNMANAGE_DPY
);
249 while (gRecvCmd( &cmd
)) {
253 debug( "G_Ready\n" );
256 /*debug( "G_GetCfg\n" );*/
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
)
265 i
= *consoleTTYs
!= 0;
267 i
= td
->console
!= 0;
269 else if (type
== C_isAuthorized
)
270 i
= td
->authorizations
!= 0;
274 /*debug( " -> bool %d\n", i );*/
278 if (!(avptr
= findCfgEnt( td
, type
))) {
279 /*debug( " -> not found\n" );*/
280 gSendInt( GE_NoEnt
);
283 switch (type
& C_TYPE_MASK
) {
285 /*debug( " -> unknown type\n" );*/
286 gSendInt( GE_BadType
);
295 switch (type
& C_TYPE_MASK
) {
297 /*debug( " -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr );*/
298 gSendInt( *(int *)avptr
);
301 /*debug( " -> string %\"s\n", *avptr );*/
302 gSendStr( *(char **)avptr
);
305 /*debug( " -> sending argv %\"[{s\n", *(char ***)avptr );*/
306 gSendArgv( *(char ***)avptr
);
310 aptr
= *(ARRAY8Ptr
*)avptr
;
311 /*debug( " -> sending array %02[*:hhx\n",
312 aptr->length, aptr->data );*/
313 gSendArr( aptr
->length
, (char *)aptr
->data
);
321 debug( "G_ReadDmrc\n" );
323 debug( " user %\"s\n", name
);
324 if (strCmp( dmrcuser
, name
)) {
325 if (curdmrc
) { free( curdmrc
); curdmrc
= 0; }
330 debug( " -> status %d\n", i
);
332 debug( " => %\"s\n", curdmrc
);
336 debug( " -> status " stringify( GE_Ok
) "\n" );
338 debug( " => keeping old\n" );
342 debug( "G_GetDmrc\n" );
344 debug( " key %\"s\n", name
);
345 pass
= iniEntry( curdmrc
, "Desktop", name
, 0 );
346 debug( " -> %\"s\n", pass
);
353 debug( "G_ResetDmrc\n" );
354 if (newdmrc) { free( newdmrc ); newdmrc = 0; }
357 debug( "G_PutDmrc\n" );
359 debug( " key %\"s\n", name
);
361 debug( " value %\"s\n", pass
);
362 newdmrc
= iniEntry( newdmrc
, "Desktop", name
, pass
);
367 debug( "G_VerifyRootOK\n" );
371 debug( "G_Verify\n" );
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" );
384 debug( " -> failure returned\n" );
387 debug( "G_AutoLogin\n" );
389 if (verify( conv_auto
, False
)) {
390 debug( " -> return success\n" );
393 debug( " -> failure returned\n" );
396 debug( "G_SetupDpy\n" );
407 debug( "lost connection to greeter\n" );
415 static time_t lastStart
;
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
);
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
);
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
);
452 closeGreeter( int force
)
456 if (grtproc
.pid
<= 0)
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();
474 if (grtproc
.pid
<= 0) {
476 gSendInt( G_ErrorGreet
);
487 if (grtproc
.pid
> 0) {
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
;
502 IdleTimeout( int n ATTR_UNUSED
)
504 Longjmp( idleTOJmp
, 1 );
508 static Jmp_buf abortSession
;
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.
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
);
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
);
549 manageSession( void )
552 volatile int clientPid
= -1;
553 volatile time_t tdiff
= 0;
556 debug( "manageSession %s\n", td
->name
);
557 if ((ex
= Setjmp( abortSession
))) {
558 closeGreeter( True
);
560 abortClient( clientPid
);
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 */
579 tdiff
= time( 0 ) - td
->hstent
->lastExit
- td
->openDelay
;
580 if (autoLogon( tdiff
)) {
581 if (!verify( conv_auto
, False
))
586 if (Setjmp( idleTOJmp
)) {
587 closeGreeter( True
);
588 sessionExit( EX_NORMAL
);
590 Signal( SIGALRM
, IdleTimeout
);
591 alarm( td
->idleTimeout
);
593 if (((td
->displayType
& d_location
) == dLocal
) &&
594 td
->loginMode
>= LOGIN_DEFAULT_REMOTE
)
598 debug( "manageSession, greeting, tdiff = %d\n", tdiff
);
599 gSendInt( (*td
->autoUser
&& td
->autoDelay
&&
600 (tdiff
> 0 || td
->autoAgain
)) ?
601 G_GreetTimed
: G_Greet
);
603 cmd
= ctrlGreeterWait( True
);
606 if (cmd
== G_DChoose
) {
618 closeGreeter( False
);
620 logError( "Received unknown command %d from greeter\n", cmd
);
621 closeGreeter( True
);
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,
640 if (!Setjmp( pingTime
)) {
641 (void)Signal( SIGALRM
, catchAlrm
);
642 (void)alarm( td
->pingInterval
* 60 ); /* may be 0 */
643 (void)Wait4( &clientPid
);
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
657 if (!pingServer( td
)) {
658 debug( "X server dead upon session exit.\n" );
659 if ((td
->displayType
& d_location
) == dLocal
)
661 sessionExit( EX_AL_RESERVER_DPY
);
665 sigaddset( &ss
, SIGTERM
);
666 sigprocmask( SIG_BLOCK
, &ss
, 0 );
668 sigprocmask( SIG_UNBLOCK
, &ss
, 0 );
671 gSendInt( D_UnUser
);
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
;
688 loadXloginResources()
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
);
708 setupDisplay( const char *arg
)
712 env
= systemEnv( 0, 0 );
713 (void)source( env
, td
->setup
, arg
);
718 deleteXloginResources()
726 prop
= XInternAtom( dpy
, "SCREEN_RESOURCES", True
);
727 XDeleteProperty( dpy
, RootWindow( dpy
, 0 ), XA_RESOURCE_MANAGER
);
729 for (i
= ScreenCount(dpy
); --i
>= 0; )
730 XDeleteProperty( dpy
, RootWindow( dpy
, i
), prop
);
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
] );
756 source( char **env
, const char *file
, const char *arg
)
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
);
777 int i
, l
= arrLen( environ
);
779 if (!(env
= Malloc( (l
+ 1) * sizeof(char *) )))
781 for (i
= 0; i
< l
; i
++)
782 strDup( env
+ i
, environ
[i
] );
788 inheritEnv( char **env
, const char **what
)
792 for (; *what
; ++what
)
793 if ((value
= getenv( *what
)))
794 env
= setEnv( env
, *what
, value
);
799 baseEnv( char **env
, const char *user
)
802 env
= inheritEnv( 0, (const char **)exportList
);
805 env
= setEnv( env
, "USER", user
);
806 env
= setEnv( env
, "LOGNAME", user
);
809 env
= setEnv( env
, "DISPLAY", displayName( td
) );
812 /* Support for assistive technologies. */
813 if (td
->serverVT
> 0) {
815 sprintf( vtstr
, "%d", td
->serverVT
);
816 env
= setEnv( env
, "WINDOWPATH", vtstr
);
821 env
= setEnv( env
, "DM_CONTROL", fifoDir
);
827 systemEnv( char **env
, const char *user
)
829 env
= baseEnv( env
, user
);
831 env
= setEnv( env
, "XAUTHORITY", td
->authFile
);
832 env
= setEnv( env
, "PATH", td
->systemPath
);
833 env
= setEnv( env
, "SHELL", td
->systemShell
);