3 Copyright 1988, 1998 The Open Group
4 Copyright 2001-2005 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
38 #include "dm_socket.h"
46 acceptSock( CtrlRec
*cr
)
51 if ((fd
= accept( cr
->fd
, 0, 0 )) < 0) {
53 logError( "Error accepting command connection\n" );
56 if (!(cs
= Malloc( sizeof(*cs
) ))) {
65 fcntl( fd
, F_SETFL
, fcntl( fd
, F_GETFL
) | O_NONBLOCK
);
66 registerCloseOnFork( fd
);
71 nukeSock( struct cmdsock
*cs
)
73 unregisterInput( cs
->sock
.fd
);
74 closeNclearCloseOnFork( cs
->sock
.fd
);
76 free( cs
->sock
.buffer
);
81 static CtrlRec ctrl
= { 0, 0, -1, 0 };
84 openCtrl( struct display
*d
)
89 struct sockaddr_un sa
;
94 cr
= &d
->ctrl
, dname
= d
->name
;
95 if (!memcmp( dname
, "localhost:", 10 ))
98 cr
= &ctrl
, dname
= 0;
100 if (mkdir( fifoDir
, 0755 )) {
101 if (errno
!= EEXIST
) {
102 logError( "mkdir %\"s failed; no control FiFos will be available\n",
107 chmod( fifoDir
, 0755 ); /* override umask */
109 strApp( &sockdir
, fifoDir
, dname
? "/dmctl-" : "/dmctl",
112 strApp( &cr
->path
, sockdir
, "/socket", (char *)0 );
114 if (strlen( cr
->path
) >= sizeof(sa
.sun_path
))
115 logError( "path %\"s too long; no control sockets will be available\n",
117 else if (mkdir( sockdir
, 0755 ) && errno
!= EEXIST
)
118 logError( "mkdir %\"s failed; no control sockets will be available\n",
122 chown( sockdir
, -1, fifoGroup
);
123 chmod( sockdir
, 0750 );
124 if ((cr
->fd
= socket( PF_UNIX
, SOCK_STREAM
, 0 )) < 0)
125 logError( "Cannot create control socket\n" );
128 sa
.sun_family
= AF_UNIX
;
129 strcpy( sa
.sun_path
, cr
->path
);
130 if (!bind( cr
->fd
, (struct sockaddr
*)&sa
, sizeof(sa
) )) {
131 if (!listen( cr
->fd
, 5 )) {
132 chmod( cr
->path
, 0666 );
133 registerCloseOnFork( cr
->fd
);
134 registerInput( cr
->fd
);
139 logError( "Cannot listen on control socket %\"s\n",
142 logError( "Cannot bind control socket %\"s\n",
157 closeCtrl( struct display
*d
)
159 CtrlRec
*cr
= d
? &d
->ctrl
: &ctrl
;
162 unregisterInput( cr
->fd
);
163 closeNclearCloseOnFork( cr
->fd
);
166 *strrchr( cr
->path
, '/' ) = 0;
171 struct cmdsock
*cs
= cr
->css
;
179 chownCtrl( CtrlRec
*cr
, int uid
)
182 char *ptr
= strrchr( cr
->path
, '/' );
184 chown( cr
->path
, uid
, -1 );
196 for (ffl
= strlen( ctrl
.path
), slc
= 2; ;)
197 if (ctrl
.path
[--ffl
] == '/')
200 if (ffl
!= strlen( fifoDir
) || memcmp( fifoDir
, ctrl
.path
, ffl
) ||
201 ctrl
.gid
!= fifoGroup
)
210 fLog( struct display
*d
, int fd
, const char *sts
, const char *msg
, ... )
218 VASPrintf( &fmsg
, msg
, va
);
223 olen
= ASPrintf( &otxt
, "%s\t%\\s\n", sts
, fmsg
);
225 writer( fd
, otxt
, olen
);
232 debug( "control %s for %s: %s - %s", what
, d
->name
, sts
, fmsg
);
234 debug( "global control %s: %s - %s", what
, sts
, fmsg
);
240 unQuote( const char *str
)
244 if (!(ret
= Malloc( strlen( str
) + 1 )))
246 for (adp
= ret
; *str
; str
++, adp
++)
249 case 0: str
--; /* fallthrough */
250 case '\\': *adp
= '\\'; break;
251 case 'n': *adp
= '\n'; break;
252 case 't': *adp
= '\t'; break;
253 default: *adp
++ = '\\'; *adp
= *str
; break;
262 strCatL( char **bp
, const char *str
, int max
)
264 int dnl
= strnlen( str
, max
);
265 memcpy( *bp
, str
, dnl
);
270 strCat( char **bp
, const char *str
)
272 int dnl
= strlen( str
);
273 memcpy( *bp
, str
, dnl
);
278 sdCat( char **bp
, SdRec
*sdr
)
280 if (sdr
->how
== SHUT_HALT
)
281 strCat( bp
, "halt," );
283 strCat( bp
, "reboot," );
284 if (sdr
->start
== TO_INF
)
287 *bp
+= sprintf( *bp
, "%d,", sdr
->start
);
288 if (sdr
->timeout
== TO_INF
)
291 *bp
+= sprintf( *bp
, "%d,", sdr
->timeout
);
292 if (sdr
->force
== SHUT_ASK
)
294 else if (sdr
->force
== SHUT_FORCE
)
295 strCat( bp
, "force" );
296 else if (sdr
->force
== SHUT_FORCEMY
)
297 strCat( bp
, "forcemy" );
299 strCat( bp
, "cancel" );
300 *bp
+= sprintf( *bp
, ",%d,%s", sdr
->uid
, sdr
->osname
? sdr
->osname
: "-" );
304 emitXSessC( struct display
*di
, struct display
*d
, void *ctx
)
312 if (!memcmp( dname
, "localhost:", 10 ))
314 strCatL( &bp
, dname
, sizeof(cbuf
)/2 );
318 bp
+= sprintf( bp
, "vt%d", di
->serverVT
);
322 if (di
->status
== remoteLogin
) {
324 strCatL( &bp
, di
->remoteHost
, sizeof(cbuf
)/3 );
329 strCatL( &bp
, di
->userName
, sizeof(cbuf
)/5 );
332 strCatL( &bp
, di
->sessName
, sizeof(cbuf
)/5 );
337 if (di
->userSess
>= 0 &&
338 (d
? (d
->userSess
!= di
->userSess
&&
339 (d
->allowNuke
== SHUT_NONE
||
340 (d
->allowNuke
== SHUT_ROOT
&& d
->userSess
))) :
343 writer( (int)(long)ctx
, cbuf
, bp
- cbuf
);
347 emitTTYSessC( STRUCTUTMP
*ut
, struct display
*d
, void *ctx
)
352 char cbuf
[sizeof(ut
->ut_line
) + sizeof(ut
->ut_user
) + sizeof(ut
->ut_host
) + 16];
353 char user
[sizeof(ut
->ut_user
) + 1];
356 if (ut
->ut_type
!= USER_PROCESS
)
361 l
= strnlen( ut
->ut_user
, sizeof(ut
->ut_user
) );
362 memcpy( user
, ut
->ut_user
, l
);
367 strCatL( &bp
, ut
->ut_line
, sizeof(ut
->ut_line
) );
371 strCatL( &bp
, ut
->ut_host
, sizeof(ut
->ut_host
) );
374 else if ((vt
= TTYtoVT( ut
->ut_line
)))
375 bp
+= sprintf( bp
, "vt%d", vt
);
380 /* blank: session type unknown */
382 /* blank: certainly not querying display */
385 (d
? ((d
->allowNuke
== SHUT_NONE
||
386 (d
->allowNuke
== SHUT_ROOT
&& d
->userSess
)) &&
387 (!(pw
= getpwnam( user
)) || d
->userSess
!= (int)pw
->pw_uid
)) :
390 writer( (int)(long)ctx
, cbuf
, bp
- cbuf
);
394 processCtrl( const char *string
, int len
, int fd
, struct display
*d
)
396 #define Reply(t) writer( fd, t, strlen( t ) )
400 char **ar
, **ap
, *args
, *bp
;
404 if (!(ar
= initStrArr( 0 )))
406 for (word
= string
; ; string
++, len
--)
407 if (!len
|| *string
== '\t') {
408 if (!(ar
= addStrArr( ar
, word
, string
- word
)))
415 debug( "control socket for %s received %'[s\n", d
->name
, ar
);
417 debug( "global control socket received %'[s\n", ar
);
419 if (!strcmp( ar
[0], "caps" )) {
422 Reply( "ok\tkdm\tlist\t" );
423 if (bootManager
!= BO_NONE
)
424 Reply( "bootoptions\t" );
426 if ((d
->displayType
& d_location
) == dLocal
)
428 Reply( "local\tactivate\t" );
432 if (d
->allowShutdown
!= SHUT_NONE
) {
433 if (d
->allowShutdown
== SHUT_ROOT
&& d
->userSess
)
434 Reply( "shutdown root\t" );
436 Reply( "shutdown\t" );
437 Reply( "shutdown ask\t" );
438 if (d
->allowNuke
!= SHUT_NONE
) {
439 if (d
->allowNuke
== SHUT_ROOT
&& d
->userSess
)
440 Reply( "nuke root\t" );
445 if ((d
->displayType
& d_location
) == dLocal
&&
446 anyReserveDisplays())
447 writer( fd
, cbuf
, sprintf( cbuf
, "reserve %d\t",
448 idleReserveDisplays() ) );
449 Reply( "lock\tsuicide\n" );
451 if (fifoAllowShutdown
) {
452 Reply( "shutdown\t" );
456 if (anyReserveDisplays())
457 writer( fd
, cbuf
, sprintf( cbuf
, "reserve %d\t",
458 idleReserveDisplays() ) );
460 Reply( "login\tactivate\n" );
466 } else if (!strcmp( ar
[0], "list" )) {
467 int flags
= lstRemote
| lstTTY
;
469 if (!strcmp( ar
[1], "all" ))
470 flags
= lstRemote
| lstPassive
| lstTTY
;
471 else if (!strcmp( ar
[1], "alllocal" ))
472 flags
= lstPassive
| lstTTY
;
474 fLog( d
, fd
, "bad", "invalid list scope %\"s", ar
[1] );
481 listSessions( flags
, d
, (void *)(long)fd
, emitXSessC
, emitTTYSessC
);
484 } else if (!strcmp( ar
[0], "reserve" )) {
485 int lt
= 60; /* XXX make default timeout configurable? */
487 lt
= strtol( ar
[1], &bp
, 10 );
488 if (lt
< 15 || *bp
) {
489 fLog( d
, fd
, "bad", "invalid timeout %\"s", ar
[1] );
495 if (d
&& (d
->displayType
& d_location
) != dLocal
) {
496 fLog( d
, fd
, "perm", "display is not local" );
499 if (!startReserveDisplay( lt
)) {
500 fLog( d
, fd
, "noent", "no reserve display available" );
504 } else if (!strcmp( ar
[0], "activate" )) {
510 if (d
&& (d
->displayType
& d_location
) != dLocal
) {
511 fLog( d
, fd
, "perm", "display is not local" );
514 if (ar
[1][0] != 'v' || ar
[1][1] != 't' ||
515 (vt
= atoi( ar
[1] + 2 )) <= 0)
517 if (!(di
= findDisplayByName( ar
[1] ))) {
518 fLog( d
, fd
, "noent", "display not found" );
521 if ((di
->displayType
& d_location
) != dLocal
) {
522 fLog( d
, fd
, "inval", "target display is not local" );
526 fLog( d
, fd
, "noent", "target display has no VT assigned" );
531 if (!activateVT( vt
)) {
532 fLog( d
, fd
, "inval", "VT switch failed" );
536 } else if (!strcmp( ar
[0], "shutdown" )) {
540 sdr
.force
= SHUT_CANCEL
;
542 if (!strcmp( *ap
, "status" )) {
549 strCat( &bp
, "\tglobal," );
550 sdCat( &bp
, &sdRec
);
552 if (d
&& d
->sdRec
.how
) {
553 strCat( &bp
, "\tlocal," );
554 sdCat( &bp
, &d
->sdRec
);
557 writer( fd
, cbuf
, bp
- cbuf
);
559 } else if (!strcmp( *ap
, "cancel" )) {
565 if (!strcmp( *++ap
, "global" ))
567 else if (strcmp( *ap
, "local" )) {
568 fLog( d
, fd
, "bad", "invalid cancel scope %\"s", *ap
);
573 if (!strcmp( *ap
, "reboot" ))
574 sdr
.how
= SHUT_REBOOT
;
575 else if (!strcmp( *ap
, "halt" ))
578 fLog( d
, fd
, "bad", "invalid type %\"s", *ap
);
585 switch (setBootOption( *ap
+ 1, &sdr
)) {
587 fLog( d
, fd
, "notsup", "boot options unavailable" );
590 fLog( d
, fd
, "noent", "no such boot option" );
593 fLog( d
, fd
, "io", "io error" );
599 sdr
.start
= strtol( *ap
, &bp
, 10 );
600 if (bp
!= *ap
&& !*bp
) {
605 sdr
.timeout
= strtol( *ap
, &bp
, 10 );
606 if (bp
== *ap
|| *bp
) {
607 fLog( d
, fd
, "bad", "invalid timeout %\"s", ar
[3] );
611 sdr
.timeout
+= sdr
.start
? sdr
.start
: now
;
613 sdr
.timeout
= TO_INF
;
617 if (!strcmp( *ap
, "force" ))
618 sdr
.force
= SHUT_FORCE
;
619 else if (d
&& !strcmp( *ap
, "forcemy" ))
620 sdr
.force
= SHUT_FORCEMY
;
621 else if (strcmp( *ap
, "cancel" )) {
622 fLog( d
, fd
, "bad", "invalid timeout action %\"s",
629 if (d
&& !strcmp( *ap
, "ask" ))
630 sdr
.force
= SHUT_ASK
;
631 else if (!strcmp( *ap
, "forcenow" ))
632 sdr
.force
= SHUT_FORCE
;
633 else if (!strcmp( *ap
, "schedule" ))
634 sdr
.timeout
= TO_INF
;
635 else if (strcmp( *ap
, "trynow" )) {
636 fLog( d
, fd
, "bad", "invalid mode %\"s", *ap
);
644 sdr
.uid
= d
->userSess
>= 0 ? d
->userSess
: 0;
645 if (d
->allowShutdown
== SHUT_NONE
||
646 (d
->allowShutdown
== SHUT_ROOT
&& sdr
.uid
&&
647 sdr
.force
!= SHUT_ASK
))
649 fLog( d
, fd
, "perm", "shutdown forbidden" );
652 if (!sdr
.how
&& !sdr
.start
) {
654 free( d
->sdRec
.osname
);
657 if (sdRec
.how
&& sdRec
.force
== SHUT_FORCE
&&
658 ((d
->allowNuke
== SHUT_NONE
&& sdRec
.uid
!= sdr
.uid
) ||
659 (d
->allowNuke
== SHUT_ROOT
&& sdr
.uid
)))
661 fLog( d
, fd
, "perm", "overriding forced shutdown forbidden" );
664 if (sdr
.force
== SHUT_FORCE
&&
665 (d
->allowNuke
== SHUT_NONE
||
666 (d
->allowNuke
== SHUT_ROOT
&& sdr
.uid
)))
668 fLog( d
, fd
, "perm", "forced shutdown forbidden" );
673 free( d
->sdRec
.osname
);
680 free( sdRec
.osname
);
686 if (!fifoAllowShutdown
) {
687 fLog( d
, fd
, "perm", "shutdown forbidden" );
690 if (sdRec
.how
&& sdRec
.force
== SHUT_FORCE
&&
691 sdRec
.uid
!= -1 && !fifoAllowNuke
)
693 fLog( d
, fd
, "perm", "overriding forced shutdown forbidden" );
699 if (sdr
.force
!= SHUT_CANCEL
) {
700 if (!fifoAllowNuke
) {
701 fLog( d
, fd
, "perm", "forced shutdown forbidden" );
705 if (!sdr
.start
&& !sdr
.timeout
&& anyUserLogins( -1 )) {
706 fLog( d
, fd
, "busy", "user sessions running" );
712 free( sdRec
.osname
);
716 } else if (!strcmp( ar
[0], "listbootoptions" )) {
722 switch (getBootOptions( &opts
, &def
, &cur
)) {
724 fLog( d
, fd
, "notsup", "boot options unavailable" );
727 fLog( d
, fd
, "io", "io error" );
731 for (i
= 0; opts
[i
]; i
++) {
735 for (j
= 0; opts
[i
][j
]; j
++)
736 if (opts
[i
][j
] == ' ') {
741 writer( fd
, cbuf
, bp
- cbuf
);
744 writer( fd
, cbuf
, sprintf( cbuf
, "\t%d\t%d\n", def
, cur
) );
747 if (!strcmp( ar
[0], "lock" )) {
750 d
->hstent
->lock
= True
;
751 } else if (!strcmp( ar
[0], "unlock" )) {
754 d
->hstent
->lock
= False
;
755 } else if (!strcmp( ar
[0], "suicide" )) {
758 if (d
->status
== running
&& d
->pid
!= -1) {
759 terminateProcess( d
->pid
, SIGTERM
);
763 fLog( d
, fd
, "nosys", "unknown command" );
767 if (!strcmp( ar
[0], "login" )) {
769 if (arrLen( ar
) < 5) {
771 fLog( d
, fd
, "bad", "missing argument(s)" );
774 if (!(di
= findDisplayByName( ar
[1] ))) {
775 fLog( d
, fd
, "noent", "display %s not found", ar
[1] );
779 if (!(args
= unQuote( ar
[5] ))) {
780 fLog( d
, fd
, "nomem", "out of memory" );
786 fLog( d
, fd
, "bad", "excess argument(s)" );
789 setNLogin( di
, ar
[3], ar
[4], args
, 2 );
792 setNLogin( di
, ar
[3], ar
[4], 0, 2 );
793 nuke
= !strcmp( ar
[2], "now" );
794 switch (di
->status
) {
796 if (di
->pid
!= -1 && (di
->userSess
< 0 || nuke
)) {
797 terminateProcess( di
->pid
, SIGTERM
);
802 if (di
->serverPid
!= -1 && nuke
)
803 terminateProcess( di
->serverPid
, di
->termSignal
);
806 di
->status
= notRunning
;
817 fLog( d
, fd
, "nosys", "unknown command" );
828 handleChan( struct display
*d
, struct bsock
*cs
, int fd
, fd_set
*reads
)
830 char *bufp
, *nbuf
, *obuf
, *eol
;
836 if (bl
<= 0 && FD_ISSET( cs
->fd
, reads
)) {
837 FD_CLR( cs
->fd
, reads
);
839 memcpy( buf
, obuf
, bl
);
840 if ((len
= reader( cs
->fd
, buf
+ bl
, sizeof(buf
) - bl
)) <= 0)
849 if ((eol
= memchr( bufp
, '\n', bl
))) {
850 llen
= eol
- bufp
+ 1;
853 if (!(nbuf
= Malloc( bl
)))
855 memcpy( nbuf
, bufp
+ llen
, bl
);
860 processCtrl( bufp
, llen
- 1, fd
, d
);
868 fLog( d
, -1, "bad", "unterminated command" );
875 handleCtrl( fd_set
*reads
, struct display
*d
)
877 CtrlRec
*cr
= d
? &d
->ctrl
: &ctrl
;
878 struct cmdsock
*cs
, **csp
;
880 if (cr
->fd
>= 0 && FD_ISSET( cr
->fd
, reads
))
883 for (csp
= &cr
->css
; (cs
= *csp
); ) {
884 switch (handleChan( d
, &cs
->sock
, cs
->sock
.fd
, reads
)) {