Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kdm / backend / ctrl.c
blob1fc9e5950a53cc35b42ab723aaba5ebd9ccc5c50
1 /*
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
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 * display manager
37 #include "dm.h"
38 #include "dm_socket.h"
39 #include "dm_error.h"
41 #include <string.h>
42 #include <signal.h>
43 #include <pwd.h>
45 static void
46 acceptSock( CtrlRec *cr )
48 struct cmdsock *cs;
49 int fd;
51 if ((fd = accept( cr->fd, 0, 0 )) < 0) {
52 bust:
53 logError( "Error accepting command connection\n" );
54 return;
56 if (!(cs = Malloc( sizeof(*cs) ))) {
57 close( fd );
58 goto bust;
60 cs->sock.fd = fd;
61 cs->sock.buffer = 0;
62 cs->sock.buflen = 0;
63 cs->next = cr->css;
64 cr->css = cs;
65 fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK );
66 registerCloseOnFork( fd );
67 registerInput( fd );
70 static void
71 nukeSock( struct cmdsock *cs )
73 unregisterInput( cs->sock.fd );
74 closeNclearCloseOnFork( cs->sock.fd );
75 if (cs->sock.buffer)
76 free( cs->sock.buffer );
77 free( cs );
81 static CtrlRec ctrl = { 0, 0, -1, 0 };
83 void
84 openCtrl( struct display *d )
86 CtrlRec *cr;
87 const char *dname;
88 char *sockdir;
89 struct sockaddr_un sa;
91 if (!*fifoDir)
92 return;
93 if (d) {
94 cr = &d->ctrl, dname = d->name;
95 if (!memcmp( dname, "localhost:", 10 ))
96 dname += 9;
97 } else
98 cr = &ctrl, dname = 0;
99 if (cr->fd < 0) {
100 if (mkdir( fifoDir, 0755 )) {
101 if (errno != EEXIST) {
102 logError( "mkdir %\"s failed; no control FiFos will be available\n",
103 fifoDir );
104 return;
106 } else
107 chmod( fifoDir, 0755 ); /* override umask */
108 sockdir = 0;
109 strApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl",
110 dname, (char *)0 );
111 if (sockdir) {
112 strApp( &cr->path, sockdir, "/socket", (char *)0 );
113 if (cr->path) {
114 if (strlen( cr->path ) >= sizeof(sa.sun_path))
115 logError( "path %\"s too long; no control sockets will be available\n",
116 cr->path );
117 else if (mkdir( sockdir, 0755 ) && errno != EEXIST)
118 logError( "mkdir %\"s failed; no control sockets will be available\n",
119 sockdir );
120 else {
121 if (!d)
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" );
126 else {
127 unlink( cr->path );
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 );
135 free( sockdir );
136 return;
138 unlink( cr->path );
139 logError( "Cannot listen on control socket %\"s\n",
140 cr->path );
141 } else
142 logError( "Cannot bind control socket %\"s\n",
143 cr->path );
144 close( cr->fd );
145 cr->fd = -1;
148 free( cr->path );
149 cr->path = 0;
151 free( sockdir );
156 void
157 closeCtrl( struct display *d )
159 CtrlRec *cr = d ? &d->ctrl : &ctrl;
161 if (cr->fd >= 0) {
162 unregisterInput( cr->fd );
163 closeNclearCloseOnFork( cr->fd );
164 cr->fd = -1;
165 unlink( cr->path );
166 *strrchr( cr->path, '/' ) = 0;
167 rmdir( cr->path );
168 free( cr->path );
169 cr->path = 0;
170 while (cr->css) {
171 struct cmdsock *cs = cr->css;
172 cr->css = cs->next;
173 nukeSock( cs );
178 void
179 chownCtrl( CtrlRec *cr, int uid )
181 if (cr->path) {
182 char *ptr = strrchr( cr->path, '/' );
183 *ptr = 0;
184 chown( cr->path, uid, -1 );
185 *ptr = '/';
189 void
190 updateCtrl( void )
192 unsigned ffl, slc;
194 ffl = 0;
195 if (ctrl.path)
196 for (ffl = strlen( ctrl.path ), slc = 2; ;)
197 if (ctrl.path[--ffl] == '/')
198 if (!--slc)
199 break;
200 if (ffl != strlen( fifoDir ) || memcmp( fifoDir, ctrl.path, ffl ) ||
201 ctrl.gid != fifoGroup)
203 closeCtrl( 0 );
204 openCtrl( 0 );
209 static void
210 fLog( struct display *d, int fd, const char *sts, const char *msg, ... )
212 char *fmsg, *otxt;
213 const char *what;
214 int olen;
215 va_list va;
217 va_start( va, msg );
218 VASPrintf( &fmsg, msg, va );
219 va_end( va );
220 if (!fmsg)
221 return;
222 if (fd >= 0) {
223 olen = ASPrintf( &otxt, "%s\t%\\s\n", sts, fmsg );
224 if (otxt) {
225 writer( fd, otxt, olen );
226 free( otxt );
228 what = "socket";
229 } else
230 what = "FiFo";
231 if (d)
232 debug( "control %s for %s: %s - %s", what, d->name, sts, fmsg );
233 else
234 debug( "global control %s: %s - %s", what, sts, fmsg );
235 free( fmsg );
239 static char *
240 unQuote( const char *str )
242 char *ret, *adp;
244 if (!(ret = Malloc( strlen( str ) + 1 )))
245 return 0;
246 for (adp = ret; *str; str++, adp++)
247 if (*str == '\\')
248 switch (*++str) {
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;
255 else
256 *adp = *str;
257 *adp = 0;
258 return ret;
261 static void
262 strCatL( char **bp, const char *str, int max )
264 int dnl = strnlen( str, max );
265 memcpy( *bp, str, dnl );
266 *bp += dnl;
269 static void
270 strCat( char **bp, const char *str )
272 int dnl = strlen( str );
273 memcpy( *bp, str, dnl );
274 *bp += dnl;
277 static void
278 sdCat( char **bp, SdRec *sdr )
280 if (sdr->how == SHUT_HALT)
281 strCat( bp, "halt," );
282 else
283 strCat( bp, "reboot," );
284 if (sdr->start == TO_INF)
285 strCat( bp, "0," );
286 else
287 *bp += sprintf( *bp, "%d,", sdr->start );
288 if (sdr->timeout == TO_INF)
289 strCat( bp, "-1," );
290 else
291 *bp += sprintf( *bp, "%d,", sdr->timeout );
292 if (sdr->force == SHUT_ASK)
293 strCat( bp, "ask" );
294 else if (sdr->force == SHUT_FORCE)
295 strCat( bp, "force" );
296 else if (sdr->force == SHUT_FORCEMY)
297 strCat( bp, "forcemy" );
298 else
299 strCat( bp, "cancel" );
300 *bp += sprintf( *bp, ",%d,%s", sdr->uid, sdr->osname ? sdr->osname : "-" );
303 static void
304 emitXSessC( struct display *di, struct display *d, void *ctx )
306 char *dname, *bp;
307 char cbuf[1024];
309 bp = cbuf;
310 *bp++ = '\t';
311 dname = di->name;
312 if (!memcmp( dname, "localhost:", 10 ))
313 dname += 9;
314 strCatL( &bp, dname, sizeof(cbuf)/2 );
315 *bp++ = ',';
316 #ifdef HAVE_VTS
317 if (di->serverVT)
318 bp += sprintf( bp, "vt%d", di->serverVT );
319 #endif
320 *bp++ = ',';
321 #ifdef XDMCP
322 if (di->status == remoteLogin) {
323 *bp++ = ',';
324 strCatL( &bp, di->remoteHost, sizeof(cbuf)/3 );
325 } else
326 #endif
328 if (di->userName)
329 strCatL( &bp, di->userName, sizeof(cbuf)/5 );
330 *bp++ = ',';
331 if (di->sessName)
332 strCatL( &bp, di->sessName, sizeof(cbuf)/5 );
334 *bp++ = ',';
335 if (di == d)
336 *bp++ = '*';
337 if (di->userSess >= 0 &&
338 (d ? (d->userSess != di->userSess &&
339 (d->allowNuke == SHUT_NONE ||
340 (d->allowNuke == SHUT_ROOT && d->userSess))) :
341 !fifoAllowNuke))
342 *bp++ = '!';
343 writer( (int)(long)ctx, cbuf, bp - cbuf );
346 static void
347 emitTTYSessC( STRUCTUTMP *ut, struct display *d, void *ctx )
349 struct passwd *pw;
350 char *bp;
351 int vt, l;
352 char cbuf[sizeof(ut->ut_line) + sizeof(ut->ut_user) + sizeof(ut->ut_host) + 16];
353 char user[sizeof(ut->ut_user) + 1];
355 #ifndef BSD_UTMP
356 if (ut->ut_type != USER_PROCESS)
357 l = 0;
358 else
359 #endif
361 l = strnlen( ut->ut_user, sizeof(ut->ut_user) );
362 memcpy( user, ut->ut_user, l );
364 user[l] = 0;
365 bp = cbuf;
366 *bp++ = '\t';
367 strCatL( &bp, ut->ut_line, sizeof(ut->ut_line) );
368 *bp++ = ',';
369 if (*ut->ut_host) {
370 *bp++ = '@';
371 strCatL( &bp, ut->ut_host, sizeof(ut->ut_host) );
373 #ifdef HAVE_VTS
374 else if ((vt = TTYtoVT( ut->ut_line )))
375 bp += sprintf( bp, "vt%d", vt );
376 #endif
377 *bp++ = ',';
378 strCat( &bp, user );
379 *bp++ = ',';
380 /* blank: session type unknown */
381 *bp++ = ',';
382 /* blank: certainly not querying display */
383 *bp++ = 't';
384 if (*user &&
385 (d ? ((d->allowNuke == SHUT_NONE ||
386 (d->allowNuke == SHUT_ROOT && d->userSess)) &&
387 (!(pw = getpwnam( user )) || d->userSess != (int)pw->pw_uid)) :
388 !fifoAllowNuke))
389 *bp++ = '!';
390 writer( (int)(long)ctx, cbuf, bp - cbuf );
393 static void
394 processCtrl( const char *string, int len, int fd, struct display *d )
396 #define Reply(t) writer( fd, t, strlen( t ) )
398 struct display *di;
399 const char *word;
400 char **ar, **ap, *args, *bp;
401 SdRec sdr;
402 char cbuf[1024];
404 if (!(ar = initStrArr( 0 )))
405 return;
406 for (word = string; ; string++, len--)
407 if (!len || *string == '\t') {
408 if (!(ar = addStrArr( ar, word, string - word )))
409 return;
410 if (!len)
411 break;
412 word = string + 1;
414 if (d)
415 debug( "control socket for %s received %'[s\n", d->name, ar );
416 else
417 debug( "global control socket received %'[s\n", ar );
418 if (ar[0]) {
419 if (!strcmp( ar[0], "caps" )) {
420 if (ar[1])
421 goto exce;
422 Reply( "ok\tkdm\tlist\t" );
423 if (bootManager != BO_NONE)
424 Reply( "bootoptions\t" );
425 if (d) {
426 if ((d->displayType & d_location) == dLocal)
427 #ifdef HAVE_VTS
428 Reply( "local\tactivate\t" );
429 #else
430 Reply( "local\t" );
431 #endif
432 if (d->allowShutdown != SHUT_NONE) {
433 if (d->allowShutdown == SHUT_ROOT && d->userSess)
434 Reply( "shutdown root\t" );
435 else
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" );
441 else
442 Reply( "nuke\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" );
450 } else {
451 if (fifoAllowShutdown) {
452 Reply( "shutdown\t" );
453 if (fifoAllowNuke)
454 Reply( "nuke\t" );
456 if (anyReserveDisplays())
457 writer( fd, cbuf, sprintf( cbuf, "reserve %d\t",
458 idleReserveDisplays() ) );
459 #ifdef HAVE_VTS
460 Reply( "login\tactivate\n" );
461 #else
462 Reply( "login\n" );
463 #endif
465 goto bust;
466 } else if (!strcmp( ar[0], "list" )) {
467 int flags = lstRemote | lstTTY;
468 if (ar[1]) {
469 if (!strcmp( ar[1], "all" ))
470 flags = lstRemote | lstPassive | lstTTY;
471 else if (!strcmp( ar[1], "alllocal" ))
472 flags = lstPassive | lstTTY;
473 else {
474 fLog( d, fd, "bad", "invalid list scope %\"s", ar[1] );
475 goto bust;
477 if (ar[2])
478 goto exce;
480 Reply( "ok" );
481 listSessions( flags, d, (void *)(long)fd, emitXSessC, emitTTYSessC );
482 Reply( "\n" );
483 goto bust;
484 } else if (!strcmp( ar[0], "reserve" )) {
485 int lt = 60; /* XXX make default timeout configurable? */
486 if (ar[1]) {
487 lt = strtol( ar[1], &bp, 10 );
488 if (lt < 15 || *bp) {
489 fLog( d, fd, "bad", "invalid timeout %\"s", ar[1] );
490 goto bust;
492 if (ar[2])
493 goto exce;
495 if (d && (d->displayType & d_location) != dLocal) {
496 fLog( d, fd, "perm", "display is not local" );
497 goto bust;
499 if (!startReserveDisplay( lt )) {
500 fLog( d, fd, "noent", "no reserve display available" );
501 goto bust;
503 #ifdef HAVE_VTS
504 } else if (!strcmp( ar[0], "activate" )) {
505 int vt;
506 if (!ar[1])
507 goto miss;
508 if (ar[2])
509 goto exce;
510 if (d && (d->displayType & d_location) != dLocal) {
511 fLog( d, fd, "perm", "display is not local" );
512 goto bust;
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" );
519 goto bust;
521 if ((di->displayType & d_location) != dLocal) {
522 fLog( d, fd, "inval", "target display is not local" );
523 goto bust;
525 if (!di->serverVT) {
526 fLog( d, fd, "noent", "target display has no VT assigned" );
527 goto bust;
529 vt = di->serverVT;
531 if (!activateVT( vt )) {
532 fLog( d, fd, "inval", "VT switch failed" );
533 goto bust;
535 #endif
536 } else if (!strcmp( ar[0], "shutdown" )) {
537 ap = ar;
538 if (!*++ap)
539 goto miss;
540 sdr.force = SHUT_CANCEL;
541 sdr.osname = 0;
542 if (!strcmp( *ap, "status" )) {
543 if (*++ap)
544 goto exce;
545 bp = cbuf;
546 *bp++ = 'o';
547 *bp++ = 'k';
548 if (sdRec.how) {
549 strCat( &bp, "\tglobal," );
550 sdCat( &bp, &sdRec );
552 if (d && d->sdRec.how) {
553 strCat( &bp, "\tlocal," );
554 sdCat( &bp, &d->sdRec );
556 *bp++ = '\n';
557 writer( fd, cbuf, bp - cbuf );
558 goto bust;
559 } else if (!strcmp( *ap, "cancel" )) {
560 sdr.how = 0;
561 sdr.start = 0;
562 if (ap[1]) {
563 if (!d)
564 goto exce;
565 if (!strcmp( *++ap, "global" ))
566 sdr.start = TO_INF;
567 else if (strcmp( *ap, "local" )) {
568 fLog( d, fd, "bad", "invalid cancel scope %\"s", *ap );
569 goto bust;
572 } else {
573 if (!strcmp( *ap, "reboot" ))
574 sdr.how = SHUT_REBOOT;
575 else if (!strcmp( *ap, "halt" ))
576 sdr.how = SHUT_HALT;
577 else {
578 fLog( d, fd, "bad", "invalid type %\"s", *ap );
579 goto bust;
581 sdr.uid = -1;
582 if (!*++ap)
583 goto miss;
584 if (**ap == '=') {
585 switch (setBootOption( *ap + 1, &sdr )) {
586 case BO_NOMAN:
587 fLog( d, fd, "notsup", "boot options unavailable" );
588 goto bust;
589 case BO_NOENT:
590 fLog( d, fd, "noent", "no such boot option" );
591 goto bust;
592 case BO_IO:
593 fLog( d, fd, "io", "io error" );
594 goto bust;
596 if (!*++ap)
597 goto miss;
599 sdr.start = strtol( *ap, &bp, 10 );
600 if (bp != *ap && !*bp) {
601 if (**ap == '+')
602 sdr.start += now;
603 if (!*++ap)
604 goto miss;
605 sdr.timeout = strtol( *ap, &bp, 10 );
606 if (bp == *ap || *bp) {
607 fLog( d, fd, "bad", "invalid timeout %\"s", ar[3] );
608 goto bust;
610 if (**ap == '+')
611 sdr.timeout += sdr.start ? sdr.start : now;
612 if (sdr.timeout < 0)
613 sdr.timeout = TO_INF;
614 else {
615 if (!*++ap)
616 goto miss;
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",
623 *ap );
624 goto bust;
627 } else {
628 sdr.timeout = 0;
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 );
637 goto bust;
641 if (*++ap)
642 goto exce;
643 if (d) {
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" );
650 goto bust;
652 if (!sdr.how && !sdr.start) {
653 if (d->sdRec.osname)
654 free( d->sdRec.osname );
655 d->sdRec = sdr;
656 } else {
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" );
662 goto bust;
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" );
669 goto bust;
671 if (!sdr.start) {
672 if (d->sdRec.osname)
673 free( d->sdRec.osname );
674 d->sdRec = sdr;
675 } else {
676 if (!sdr.how)
677 cancelShutdown();
678 else {
679 if (sdRec.osname)
680 free( sdRec.osname );
681 sdRec = sdr;
685 } else {
686 if (!fifoAllowShutdown) {
687 fLog( d, fd, "perm", "shutdown forbidden" );
688 goto bust;
690 if (sdRec.how && sdRec.force == SHUT_FORCE &&
691 sdRec.uid != -1 && !fifoAllowNuke)
693 fLog( d, fd, "perm", "overriding forced shutdown forbidden" );
694 goto bust;
696 if (!sdr.how)
697 cancelShutdown();
698 else {
699 if (sdr.force != SHUT_CANCEL) {
700 if (!fifoAllowNuke) {
701 fLog( d, fd, "perm", "forced shutdown forbidden" );
702 goto bust;
704 } else {
705 if (!sdr.start && !sdr.timeout && anyUserLogins( -1 )) {
706 fLog( d, fd, "busy", "user sessions running" );
707 goto bust;
710 sdr.uid = -1;
711 if (sdRec.osname)
712 free( sdRec.osname );
713 sdRec = sdr;
716 } else if (!strcmp( ar[0], "listbootoptions" )) {
717 char **opts;
718 int def, cur, i, j;
720 if (ar[1])
721 goto exce;
722 switch (getBootOptions( &opts, &def, &cur )) {
723 case BO_NOMAN:
724 fLog( d, fd, "notsup", "boot options unavailable" );
725 goto bust;
726 case BO_IO:
727 fLog( d, fd, "io", "io error" );
728 goto bust;
730 Reply( "ok\t" );
731 for (i = 0; opts[i]; i++) {
732 bp = cbuf;
733 if (i)
734 *bp++ = ' ';
735 for (j = 0; opts[i][j]; j++)
736 if (opts[i][j] == ' ') {
737 *bp++ = '\\';
738 *bp++ = 's';
739 } else
740 *bp++ = opts[i][j];
741 writer( fd, cbuf, bp - cbuf );
743 freeStrArr( opts );
744 writer( fd, cbuf, sprintf( cbuf, "\t%d\t%d\n", def, cur ) );
745 goto bust;
746 } else if (d) {
747 if (!strcmp( ar[0], "lock" )) {
748 if (ar[1])
749 goto exce;
750 d->hstent->lock = True;
751 } else if (!strcmp( ar[0], "unlock" )) {
752 if (ar[1])
753 goto exce;
754 d->hstent->lock = False;
755 } else if (!strcmp( ar[0], "suicide" )) {
756 if (ar[1])
757 goto exce;
758 if (d->status == running && d->pid != -1) {
759 terminateProcess( d->pid, SIGTERM );
760 d->status = raiser;
762 } else {
763 fLog( d, fd, "nosys", "unknown command" );
764 goto bust;
766 } else {
767 if (!strcmp( ar[0], "login" )) {
768 int nuke;
769 if (arrLen( ar ) < 5) {
770 miss:
771 fLog( d, fd, "bad", "missing argument(s)" );
772 goto bust;
774 if (!(di = findDisplayByName( ar[1] ))) {
775 fLog( d, fd, "noent", "display %s not found", ar[1] );
776 goto bust;
778 if (ar[5]) {
779 if (!(args = unQuote( ar[5] ))) {
780 fLog( d, fd, "nomem", "out of memory" );
781 goto bust;
783 if (ar[6]) {
784 free( args );
785 exce:
786 fLog( d, fd, "bad", "excess argument(s)" );
787 goto bust;
789 setNLogin( di, ar[3], ar[4], args, 2 );
790 free( args );
791 } else
792 setNLogin( di, ar[3], ar[4], 0, 2 );
793 nuke = !strcmp( ar[2], "now" );
794 switch (di->status) {
795 case running:
796 if (di->pid != -1 && (di->userSess < 0 || nuke)) {
797 terminateProcess( di->pid, SIGTERM );
798 di->status = raiser;
800 break;
801 case remoteLogin:
802 if (di->serverPid != -1 && nuke)
803 terminateProcess( di->serverPid, di->termSignal );
804 break;
805 case reserve:
806 di->status = notRunning;
807 break;
808 case textMode:
809 #ifndef HAVE_VTS
810 switchToX( di );
811 #endif
812 break;
813 default:
814 break;
816 } else {
817 fLog( d, fd, "nosys", "unknown command" );
818 goto bust;
821 Reply( "ok\n" );
823 bust:
824 freeStrArr( ar );
827 static int
828 handleChan( struct display *d, struct bsock *cs, int fd, fd_set *reads )
830 char *bufp, *nbuf, *obuf, *eol;
831 int len, bl, llen;
832 char buf[1024];
834 bl = cs->buflen;
835 obuf = cs->buffer;
836 if (bl <= 0 && FD_ISSET( cs->fd, reads )) {
837 FD_CLR( cs->fd, reads );
838 bl = -bl;
839 memcpy( buf, obuf, bl );
840 if ((len = reader( cs->fd, buf + bl, sizeof(buf) - bl )) <= 0)
841 return -1;
842 bl += len;
843 bufp = buf;
844 } else {
845 len = 0;
846 bufp = obuf;
848 if (bl > 0) {
849 if ((eol = memchr( bufp, '\n', bl ))) {
850 llen = eol - bufp + 1;
851 bl -= llen;
852 if (bl) {
853 if (!(nbuf = Malloc( bl )))
854 return -1;
855 memcpy( nbuf, bufp + llen, bl );
856 } else
857 nbuf = 0;
858 cs->buffer = nbuf;
859 cs->buflen = bl;
860 processCtrl( bufp, llen - 1, fd, d );
861 if (obuf)
862 free( obuf );
863 return 1;
864 } else if (!len) {
865 if (fd >= 0)
866 cs->buflen = -bl;
867 else
868 fLog( d, -1, "bad", "unterminated command" );
871 return 0;
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 ))
881 acceptSock( cr );
882 else {
883 for (csp = &cr->css; (cs = *csp); ) {
884 switch (handleChan( d, &cs->sock, cs->sock.fd, reads )) {
885 case -1:
886 *csp = cs->next;
887 nukeSock( cs );
888 continue;
889 case 1:
890 return True;
891 default:
892 break;
894 csp = &cs->next;
897 return False;