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
= displayName( d
);
96 cr
= &ctrl
, dname
= 0;
98 if (mkdir( fifoDir
, 0755 )) {
99 if (errno
!= EEXIST
) {
100 logError( "mkdir %\"s failed; no control FiFos will be available\n",
105 chmod( fifoDir
, 0755 ); /* override umask */
107 strApp( &sockdir
, fifoDir
, dname
? "/dmctl-" : "/dmctl",
110 strApp( &cr
->path
, sockdir
, "/socket", (char *)0 );
112 if (strlen( cr
->path
) >= sizeof(sa
.sun_path
))
113 logError( "path %\"s too long; no control sockets will be available\n",
115 else if (mkdir( sockdir
, 0755 ) && errno
!= EEXIST
)
116 logError( "mkdir %\"s failed; no control sockets will be available\n",
120 chown( sockdir
, -1, fifoGroup
);
121 chmod( sockdir
, 0750 );
122 if ((cr
->fd
= socket( PF_UNIX
, SOCK_STREAM
, 0 )) < 0)
123 logError( "Cannot create control socket\n" );
126 sa
.sun_family
= AF_UNIX
;
127 strcpy( sa
.sun_path
, cr
->path
);
128 if (!bind( cr
->fd
, (struct sockaddr
*)&sa
, sizeof(sa
) )) {
129 if (!listen( cr
->fd
, 5 )) {
130 chmod( cr
->path
, 0666 );
131 registerCloseOnFork( cr
->fd
);
132 registerInput( cr
->fd
);
137 logError( "Cannot listen on control socket %\"s\n",
140 logError( "Cannot bind control socket %\"s\n",
155 closeCtrl( struct display
*d
)
157 CtrlRec
*cr
= d
? &d
->ctrl
: &ctrl
;
160 unregisterInput( cr
->fd
);
161 closeNclearCloseOnFork( cr
->fd
);
164 *strrchr( cr
->path
, '/' ) = 0;
169 struct cmdsock
*cs
= cr
->css
;
177 chownCtrl( CtrlRec
*cr
, int uid
)
180 char *ptr
= strrchr( cr
->path
, '/' );
182 chown( cr
->path
, uid
, -1 );
194 for (ffl
= strlen( ctrl
.path
), slc
= 2; ;)
195 if (ctrl
.path
[--ffl
] == '/')
198 if (ffl
!= strlen( fifoDir
) || memcmp( fifoDir
, ctrl
.path
, ffl
) ||
199 ctrl
.gid
!= fifoGroup
)
208 fLog( struct display
*d
, int fd
, const char *sts
, const char *msg
, ... )
216 VASPrintf( &fmsg
, msg
, va
);
221 olen
= ASPrintf( &otxt
, "%s\t%\\s\n", sts
, fmsg
);
223 writer( fd
, otxt
, olen
);
230 debug( "control %s for %s: %s - %s", what
, d
->name
, sts
, fmsg
);
232 debug( "global control %s: %s - %s", what
, sts
, fmsg
);
238 unQuote( const char *str
)
242 if (!(ret
= Malloc( strlen( str
) + 1 )))
244 for (adp
= ret
; *str
; str
++, adp
++)
247 case 0: str
--; /* fallthrough */
248 case '\\': *adp
= '\\'; break;
249 case 'n': *adp
= '\n'; break;
250 case 't': *adp
= '\t'; break;
251 default: *adp
++ = '\\'; *adp
= *str
; break;
260 strCatL( char **bp
, const char *str
, int max
)
262 int dnl
= strnlen( str
, max
);
263 memcpy( *bp
, str
, dnl
);
268 strCat( char **bp
, const char *str
)
270 int dnl
= strlen( str
);
271 memcpy( *bp
, str
, dnl
);
276 sdCat( char **bp
, SdRec
*sdr
)
278 if (sdr
->how
== SHUT_HALT
)
279 strCat( bp
, "halt," );
281 strCat( bp
, "reboot," );
282 if (sdr
->start
== TO_INF
)
285 *bp
+= sprintf( *bp
, "%d,", sdr
->start
);
286 if (sdr
->timeout
== TO_INF
)
289 *bp
+= sprintf( *bp
, "%d,", sdr
->timeout
);
290 if (sdr
->force
== SHUT_ASK
)
292 else if (sdr
->force
== SHUT_FORCE
)
293 strCat( bp
, "force" );
294 else if (sdr
->force
== SHUT_FORCEMY
)
295 strCat( bp
, "forcemy" );
297 strCat( bp
, "cancel" );
298 *bp
+= sprintf( *bp
, ",%d,%s", sdr
->uid
, sdr
->osname
? sdr
->osname
: "-" );
302 emitXSessC( struct display
*di
, struct display
*d
, void *ctx
)
310 dname
= displayName( di
);
311 strCatL( &bp
, dname
, sizeof(cbuf
)/2 );
315 bp
+= sprintf( bp
, "vt%d", di
->serverVT
);
319 if (di
->status
== remoteLogin
) {
321 strCatL( &bp
, di
->remoteHost
, sizeof(cbuf
)/3 );
326 strCatL( &bp
, di
->userName
, sizeof(cbuf
)/5 );
329 strCatL( &bp
, di
->sessName
, sizeof(cbuf
)/5 );
334 if (di
->userSess
>= 0 &&
335 (d
? (d
->userSess
!= di
->userSess
&&
336 (d
->allowNuke
== SHUT_NONE
||
337 (d
->allowNuke
== SHUT_ROOT
&& d
->userSess
))) :
340 writer( (int)(long)ctx
, cbuf
, bp
- cbuf
);
344 emitTTYSessC( STRUCTUTMP
*ut
, struct display
*d
, void *ctx
)
349 char cbuf
[sizeof(ut
->ut_line
) + sizeof(ut
->ut_user
) + sizeof(ut
->ut_host
) + 16];
350 char user
[sizeof(ut
->ut_user
) + 1];
353 if (ut
->ut_type
!= USER_PROCESS
)
358 l
= strnlen( ut
->ut_user
, sizeof(ut
->ut_user
) );
359 memcpy( user
, ut
->ut_user
, l
);
364 strCatL( &bp
, ut
->ut_line
, sizeof(ut
->ut_line
) );
368 strCatL( &bp
, ut
->ut_host
, sizeof(ut
->ut_host
) );
371 else if ((vt
= TTYtoVT( ut
->ut_line
)))
372 bp
+= sprintf( bp
, "vt%d", vt
);
377 /* blank: session type unknown */
379 /* blank: certainly not querying display */
382 (d
? ((d
->allowNuke
== SHUT_NONE
||
383 (d
->allowNuke
== SHUT_ROOT
&& d
->userSess
)) &&
384 (!(pw
= getpwnam( user
)) || d
->userSess
!= (int)pw
->pw_uid
)) :
387 writer( (int)(long)ctx
, cbuf
, bp
- cbuf
);
391 processCtrl( const char *string
, int len
, int fd
, struct display
*d
)
393 #define Reply(t) writer( fd, t, strlen( t ) )
397 char **ar
, **ap
, *args
, *bp
;
401 if (!(ar
= initStrArr( 0 )))
403 for (word
= string
; ; string
++, len
--)
404 if (!len
|| *string
== '\t') {
405 if (!(ar
= addStrArr( ar
, word
, string
- word
)))
412 debug( "control socket for %s received %'[s\n", d
->name
, ar
);
414 debug( "global control socket received %'[s\n", ar
);
416 if (!strcmp( ar
[0], "caps" )) {
419 Reply( "ok\tkdm\tlist\t" );
420 if (bootManager
!= BO_NONE
)
421 Reply( "bootoptions\t" );
423 if ((d
->displayType
& d_location
) == dLocal
)
425 Reply( "local\tactivate\t" );
429 if (d
->allowShutdown
!= SHUT_NONE
) {
430 if (d
->allowShutdown
== SHUT_ROOT
&& d
->userSess
)
431 Reply( "shutdown root\t" );
433 Reply( "shutdown\t" );
434 Reply( "shutdown ask\t" );
435 if (d
->allowNuke
!= SHUT_NONE
) {
436 if (d
->allowNuke
== SHUT_ROOT
&& d
->userSess
)
437 Reply( "nuke root\t" );
442 if ((d
->displayType
& d_location
) == dLocal
&&
443 anyReserveDisplays())
444 writer( fd
, cbuf
, sprintf( cbuf
, "reserve %d\t",
445 idleReserveDisplays() ) );
446 Reply( "lock\tsuicide\n" );
448 if (fifoAllowShutdown
) {
449 Reply( "shutdown\t" );
453 if (anyReserveDisplays())
454 writer( fd
, cbuf
, sprintf( cbuf
, "reserve %d\t",
455 idleReserveDisplays() ) );
457 Reply( "login\tactivate\n" );
463 } else if (!strcmp( ar
[0], "list" )) {
464 int flags
= lstRemote
| lstTTY
;
466 if (!strcmp( ar
[1], "all" ))
467 flags
= lstRemote
| lstPassive
| lstTTY
;
468 else if (!strcmp( ar
[1], "alllocal" ))
469 flags
= lstPassive
| lstTTY
;
471 fLog( d
, fd
, "bad", "invalid list scope %\"s", ar
[1] );
478 listSessions( flags
, d
, (void *)(long)fd
, emitXSessC
, emitTTYSessC
);
481 } else if (!strcmp( ar
[0], "reserve" )) {
482 int lt
= 60; /* XXX make default timeout configurable? */
484 lt
= strtol( ar
[1], &bp
, 10 );
485 if (lt
< 15 || *bp
) {
486 fLog( d
, fd
, "bad", "invalid timeout %\"s", ar
[1] );
492 if (d
&& (d
->displayType
& d_location
) != dLocal
) {
493 fLog( d
, fd
, "perm", "display is not local" );
496 if (!startReserveDisplay( lt
)) {
497 fLog( d
, fd
, "noent", "no reserve display available" );
501 } else if (!strcmp( ar
[0], "activate" )) {
507 if (d
&& (d
->displayType
& d_location
) != dLocal
) {
508 fLog( d
, fd
, "perm", "display is not local" );
511 if (ar
[1][0] != 'v' || ar
[1][1] != 't' ||
512 (vt
= atoi( ar
[1] + 2 )) <= 0)
514 if (!(di
= findDisplayByName( ar
[1] ))) {
515 fLog( d
, fd
, "noent", "display not found" );
518 if ((di
->displayType
& d_location
) != dLocal
) {
519 fLog( d
, fd
, "inval", "target display is not local" );
523 fLog( d
, fd
, "noent", "target display has no VT assigned" );
528 if (!activateVT( vt
)) {
529 fLog( d
, fd
, "inval", "VT switch failed" );
533 } else if (!strcmp( ar
[0], "shutdown" )) {
537 sdr
.force
= SHUT_CANCEL
;
539 if (!strcmp( *ap
, "status" )) {
546 strCat( &bp
, "\tglobal," );
547 sdCat( &bp
, &sdRec
);
549 if (d
&& d
->sdRec
.how
) {
550 strCat( &bp
, "\tlocal," );
551 sdCat( &bp
, &d
->sdRec
);
554 writer( fd
, cbuf
, bp
- cbuf
);
556 } else if (!strcmp( *ap
, "cancel" )) {
562 if (!strcmp( *++ap
, "global" ))
564 else if (strcmp( *ap
, "local" )) {
565 fLog( d
, fd
, "bad", "invalid cancel scope %\"s", *ap
);
570 if (!strcmp( *ap
, "reboot" ))
571 sdr
.how
= SHUT_REBOOT
;
572 else if (!strcmp( *ap
, "halt" ))
575 fLog( d
, fd
, "bad", "invalid type %\"s", *ap
);
582 switch (setBootOption( *ap
+ 1, &sdr
)) {
584 fLog( d
, fd
, "notsup", "boot options unavailable" );
587 fLog( d
, fd
, "noent", "no such boot option" );
590 fLog( d
, fd
, "io", "io error" );
596 sdr
.start
= strtol( *ap
, &bp
, 10 );
597 if (bp
!= *ap
&& !*bp
) {
602 sdr
.timeout
= strtol( *ap
, &bp
, 10 );
603 if (bp
== *ap
|| *bp
) {
604 fLog( d
, fd
, "bad", "invalid timeout %\"s", ar
[3] );
608 sdr
.timeout
+= sdr
.start
? sdr
.start
: now
;
610 sdr
.timeout
= TO_INF
;
614 if (!strcmp( *ap
, "force" ))
615 sdr
.force
= SHUT_FORCE
;
616 else if (d
&& !strcmp( *ap
, "forcemy" ))
617 sdr
.force
= SHUT_FORCEMY
;
618 else if (strcmp( *ap
, "cancel" )) {
619 fLog( d
, fd
, "bad", "invalid timeout action %\"s",
626 if (d
&& !strcmp( *ap
, "ask" ))
627 sdr
.force
= SHUT_ASK
;
628 else if (!strcmp( *ap
, "forcenow" ))
629 sdr
.force
= SHUT_FORCE
;
630 else if (!strcmp( *ap
, "schedule" ))
631 sdr
.timeout
= TO_INF
;
632 else if (strcmp( *ap
, "trynow" )) {
633 fLog( d
, fd
, "bad", "invalid mode %\"s", *ap
);
641 sdr
.uid
= d
->userSess
>= 0 ? d
->userSess
: 0;
642 if (d
->allowShutdown
== SHUT_NONE
||
643 (d
->allowShutdown
== SHUT_ROOT
&& sdr
.uid
&&
644 sdr
.force
!= SHUT_ASK
))
646 fLog( d
, fd
, "perm", "shutdown forbidden" );
649 if (!sdr
.how
&& !sdr
.start
) {
651 free( d
->sdRec
.osname
);
654 if (sdRec
.how
&& sdRec
.force
== SHUT_FORCE
&&
655 ((d
->allowNuke
== SHUT_NONE
&& sdRec
.uid
!= sdr
.uid
) ||
656 (d
->allowNuke
== SHUT_ROOT
&& sdr
.uid
)))
658 fLog( d
, fd
, "perm", "overriding forced shutdown forbidden" );
661 if (sdr
.force
== SHUT_FORCE
&&
662 (d
->allowNuke
== SHUT_NONE
||
663 (d
->allowNuke
== SHUT_ROOT
&& sdr
.uid
)))
665 fLog( d
, fd
, "perm", "forced shutdown forbidden" );
670 free( d
->sdRec
.osname
);
677 free( sdRec
.osname
);
683 if (!fifoAllowShutdown
) {
684 fLog( d
, fd
, "perm", "shutdown forbidden" );
687 if (sdRec
.how
&& sdRec
.force
== SHUT_FORCE
&&
688 sdRec
.uid
!= -1 && !fifoAllowNuke
)
690 fLog( d
, fd
, "perm", "overriding forced shutdown forbidden" );
696 if (sdr
.force
!= SHUT_CANCEL
) {
697 if (!fifoAllowNuke
) {
698 fLog( d
, fd
, "perm", "forced shutdown forbidden" );
702 if (!sdr
.start
&& !sdr
.timeout
&& anyUserLogins( -1 )) {
703 fLog( d
, fd
, "busy", "user sessions running" );
709 free( sdRec
.osname
);
713 } else if (!strcmp( ar
[0], "listbootoptions" )) {
719 switch (getBootOptions( &opts
, &def
, &cur
)) {
721 fLog( d
, fd
, "notsup", "boot options unavailable" );
724 fLog( d
, fd
, "io", "io error" );
728 for (i
= 0; opts
[i
]; i
++) {
732 for (j
= 0; opts
[i
][j
]; j
++)
733 if (opts
[i
][j
] == ' ') {
738 writer( fd
, cbuf
, bp
- cbuf
);
741 writer( fd
, cbuf
, sprintf( cbuf
, "\t%d\t%d\n", def
, cur
) );
744 if (!strcmp( ar
[0], "lock" )) {
747 d
->hstent
->lock
= True
;
748 } else if (!strcmp( ar
[0], "unlock" )) {
751 d
->hstent
->lock
= False
;
752 } else if (!strcmp( ar
[0], "suicide" )) {
755 if (d
->status
== running
&& d
->pid
!= -1) {
756 terminateProcess( d
->pid
, SIGTERM
);
760 fLog( d
, fd
, "nosys", "unknown command" );
764 if (!strcmp( ar
[0], "login" )) {
766 if (arrLen( ar
) < 5) {
768 fLog( d
, fd
, "bad", "missing argument(s)" );
771 if (!(di
= findDisplayByName( ar
[1] ))) {
772 fLog( d
, fd
, "noent", "display %s not found", ar
[1] );
776 if (!(args
= unQuote( ar
[5] ))) {
777 fLog( d
, fd
, "nomem", "out of memory" );
783 fLog( d
, fd
, "bad", "excess argument(s)" );
786 setNLogin( di
, ar
[3], ar
[4], args
, 2 );
789 setNLogin( di
, ar
[3], ar
[4], 0, 2 );
790 nuke
= !strcmp( ar
[2], "now" );
791 switch (di
->status
) {
793 if (di
->pid
!= -1 && (di
->userSess
< 0 || nuke
)) {
794 terminateProcess( di
->pid
, SIGTERM
);
799 if (di
->serverPid
!= -1 && nuke
)
800 terminateProcess( di
->serverPid
, di
->termSignal
);
803 di
->status
= notRunning
;
814 fLog( d
, fd
, "nosys", "unknown command" );
825 handleChan( struct display
*d
, struct bsock
*cs
, int fd
, fd_set
*reads
)
827 char *bufp
, *nbuf
, *obuf
, *eol
;
833 if (bl
<= 0 && FD_ISSET( cs
->fd
, reads
)) {
834 FD_CLR( cs
->fd
, reads
);
836 memcpy( buf
, obuf
, bl
);
837 if ((len
= reader( cs
->fd
, buf
+ bl
, sizeof(buf
) - bl
)) <= 0)
846 if ((eol
= memchr( bufp
, '\n', bl
))) {
847 llen
= eol
- bufp
+ 1;
850 if (!(nbuf
= Malloc( bl
)))
852 memcpy( nbuf
, bufp
+ llen
, bl
);
857 processCtrl( bufp
, llen
- 1, fd
, d
);
865 fLog( d
, -1, "bad", "unterminated command" );
872 handleCtrl( fd_set
*reads
, struct display
*d
)
874 CtrlRec
*cr
= d
? &d
->ctrl
: &ctrl
;
875 struct cmdsock
*cs
, **csp
;
877 if (cr
->fd
>= 0 && FD_ISSET( cr
->fd
, reads
))
880 for (csp
= &cr
->css
; (cs
= *csp
); ) {
881 switch (handleChan( d
, &cs
->sock
, cs
->sock
.fd
, reads
)) {