diff options
Diffstat (limited to 'tdm/backend/ctrl.c')
-rw-r--r-- | tdm/backend/ctrl.c | 1015 |
1 files changed, 1015 insertions, 0 deletions
diff --git a/tdm/backend/ctrl.c b/tdm/backend/ctrl.c new file mode 100644 index 000000000..622c9370d --- /dev/null +++ b/tdm/backend/ctrl.c @@ -0,0 +1,1015 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * display manager + */ + +#include "dm.h" +#include "dm_socket.h" +#include "dm_error.h" + +#include <string.h> +#include <signal.h> +#include <pwd.h> + +static void +acceptSock( CtrlRec *cr ) +{ + struct cmdsock *cs; + int fd; + + if ((fd = accept( cr->fd, 0, 0 )) < 0) { + bust: + LogError( "Error accepting command connection\n" ); + return; + } + if (!(cs = Malloc( sizeof(*cs) ))) { + close( fd ); + goto bust; + } + cs->sock.fd = fd; + cs->sock.buffer = 0; + cs->sock.buflen = 0; + cs->next = cr->css; + cr->css = cs; + fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK ); + RegisterCloseOnFork( fd ); + RegisterInput( fd ); +} + +static void +nukeSock( struct cmdsock *cs ) +{ + UnregisterInput( cs->sock.fd ); + CloseNClearCloseOnFork( cs->sock.fd ); + if (cs->sock.buffer) + free( cs->sock.buffer ); + free( cs ); +} + +#ifdef HONORS_SOCKET_PERMS +static CtrlRec ctrl = { 0, 0, -1, 0, 0, { -1, 0, 0 } }; +#else +static CtrlRec ctrl = { 0, 0, 0, -1, 0, 0, { -1, 0, 0 } }; + +static int mkTempDir( char *dir ) +{ + int i, l = strlen( dir ) - 6; + + for (i = 0; i < 100; i++) { + randomStr( dir + l ); + if (!mkdir( dir, 0700 )) + return True; + if (errno != EEXIST) + break; + } + return False; +} +#endif + +void +openCtrl( struct display *d ) +{ + CtrlRec *cr; + const char *dname; + char *sockdir; + struct sockaddr_un sa; + + if (!*fifoDir) + return; + if (d) { + cr = &d->ctrl, dname = d->name; + if (!memcmp( dname, "localhost:", 10 )) + dname += 9; + } else + cr = &ctrl, dname = 0; + if (cr->fifo.fd < 0) { + if (mkdir( fifoDir, 0755 )) { + if (errno != EEXIST) { + LogError( "mkdir %\"s failed; no control FiFos will be available\n", + fifoDir ); + return; + } + } else + chmod( fifoDir, 0755 ); /* override umask */ + StrApp( &cr->fpath, fifoDir, dname ? "/xdmctl-" : "/xdmctl", + dname, (char *)0 ); + if (cr->fpath) { + unlink( cr->fpath ); + if (mkfifo( cr->fpath, 0 ) < 0) + LogError( "Cannot create control FiFo %\"s\n", cr->fpath ); + else { + cr->gid = fifoGroup; + if (!d) + chown( cr->fpath, -1, fifoGroup ); + chmod( cr->fpath, 0620 ); + if ((cr->fifo.fd = open( cr->fpath, O_RDWR | O_NONBLOCK )) >= 0) { + RegisterCloseOnFork( cr->fifo.fd ); + RegisterInput( cr->fifo.fd ); + goto fifok; + } + unlink( cr->fpath ); + LogError( "Cannot open control FiFo %\"s\n", cr->fpath ); + } + free( cr->fpath ); + cr->fpath = 0; + } + } + fifok: + if (cr->fd < 0) { + /* fifoDir is created above already */ + sockdir = 0; + StrApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl", + dname, (char *)0 ); + if (sockdir) { + StrApp( &cr->path, sockdir, "/socket", (char *)0 ); + if (cr->path) { + if (strlen( cr->path ) >= sizeof(sa.sun_path)) + LogError( "path %\"s too long; no control sockets will be available\n", + cr->path ); +#ifdef HONORS_SOCKET_PERMS + else if (mkdir( sockdir, 0700 ) && errno != EEXIST) + LogError( "mkdir %\"s failed; no control sockets will be available\n", + sockdir ); + else if (unlink( cr->path ) && errno != ENOENT) + LogError( "unlink %\"s failed: %m; control socket will not be available\n", + cr->path ); + else { +#else + else if (unlink( sockdir ) && errno != ENOENT) + LogError( "unlink %\"s failed: %m; control socket will not be available\n", + sockdir ); + else if (!StrApp( &cr->realdir, sockdir, "-XXXXXX", (char *)0)) + ; + else if (!mkTempDir( cr->realdir )) { + LogError( "mkdir %\"s failed: %m; control socket will not be available\n", + cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; + } else if (symlink( cr->realdir, sockdir )) { + LogError( "symlink %\"s => %\"s failed: %m; control socket will not be available\n", + sockdir, cr->realdir ); + rmdir( cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; + } else { + chown( sockdir, 0, d ? 0 : fifoGroup ); + chmod( sockdir, 0750 ); +#endif + if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) + LogError( "Cannot create control socket\n" ); + else { + sa.sun_family = AF_UNIX; + strcpy( sa.sun_path, cr->path ); + if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) { + if (!listen( cr->fd, 5 )) { +#ifdef HONORS_SOCKET_PERMS + chmod( cr->path, 0660 ); + if (!d) + chown( cr->path, -1, fifoGroup ); + chmod( sockdir, 0755 ); +#else + chmod( cr->path, 0666 ); +#endif + RegisterCloseOnFork( cr->fd ); + RegisterInput( cr->fd ); + free( sockdir ); + return; + } + unlink( cr->path ); + LogError( "Cannot listen on control socket %\"s\n", + cr->path ); + } else + LogError( "Cannot bind control socket %\"s\n", + cr->path ); + close( cr->fd ); + cr->fd = -1; + } +#ifdef HONORS_SOCKET_PERMS + rmdir( sockdir ); +#else + unlink( sockdir ); + rmdir( cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; +#endif + } + free( cr->path ); + cr->path = 0; + } + free( sockdir ); + } + } +} + +void +closeCtrl( struct display *d ) +{ + CtrlRec *cr = d ? &d->ctrl : &ctrl; + + if (cr->fd >= 0) { + UnregisterInput( cr->fd ); + CloseNClearCloseOnFork( cr->fd ); + cr->fd = -1; + unlink( cr->path ); + *strrchr( cr->path, '/' ) = 0; +#ifdef HONORS_SOCKET_PERMS + rmdir( cr->path ); +#else + unlink( cr->path ); + rmdir( cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; +#endif + free( cr->path ); + cr->path = 0; + while (cr->css) { + struct cmdsock *cs = cr->css; + cr->css = cs->next; + nukeSock( cs ); + } + } + if (cr->fifo.fd >= 0) { + UnregisterInput( cr->fifo.fd ); + CloseNClearCloseOnFork( cr->fifo.fd ); + cr->fifo.fd = -1; + unlink( cr->fpath ); + free( cr->fpath ); + cr->fpath = 0; + if (cr->fifo.buffer) + free( cr->fifo.buffer ); + cr->fifo.buffer = 0; + cr->fifo.buflen = 0; + } +} + +void +chownCtrl( CtrlRec *cr, int uid ) +{ + if (cr->fpath) + chown( cr->fpath, uid, -1 ); + if (cr->path) +#ifdef HONORS_SOCKET_PERMS + chown( cr->path, uid, -1 ); +#else + chown( cr->realdir, uid, -1 ); +#endif +} + +void +updateCtrl( void ) +{ + unsigned ffl, slc; + + ffl = 0; + if (ctrl.path) + for (ffl = strlen( ctrl.path ), slc = 2; ;) + if (ctrl.path[--ffl] == '/') + if (!--slc) + break; + if (ffl != strlen( fifoDir ) || memcmp( fifoDir, ctrl.path, ffl ) || + ctrl.gid != fifoGroup) + { + closeCtrl( 0 ); + openCtrl( 0 ); + } +} + + +static void +fLog( struct display *d, int fd, const char *sts, const char *msg, ... ) +{ + char *fmsg, *otxt; + const char *what; + int olen; + va_list va; + + va_start( va, msg ); + VASPrintf( &fmsg, msg, va ); + va_end( va ); + if (!fmsg) + return; + if (fd >= 0) { + olen = ASPrintf( &otxt, "%s\t%\\s\n", sts, fmsg ); + if (otxt) { + Writer( fd, otxt, olen ); + free( otxt ); + } + what = "socket"; + } else + what = "FiFo"; + if (d) + Debug( "control %s for %s: %s - %s", what, d->name, sts, fmsg ); + else + Debug( "global control %s: %s - %s", what, sts, fmsg ); + free( fmsg ); +} + + +static char * +unQuote( const char *str ) +{ + char *ret, *adp; + + if (!(ret = Malloc( strlen( str ) + 1 ))) + return 0; + for (adp = ret; *str; str++, adp++) + if (*str == '\\') + switch (*++str) { + case 0: str--; /* fallthrough */ + case '\\': *adp = '\\'; break; + case 'n': *adp = '\n'; break; + case 't': *adp = '\t'; break; + default: *adp++ = '\\'; *adp = *str; break; + } + else + *adp = *str; + *adp = 0; + return ret; +} + +static void +str_cat_l( char **bp, const char *str, int max ) +{ + int dnl = StrNLen( str, max ); + memcpy( *bp, str, dnl ); + *bp += dnl; +} + +static void +str_cat( char **bp, const char *str ) +{ + int dnl = strlen( str ); + memcpy( *bp, str, dnl ); + *bp += dnl; +} + +static void +sd_cat( char **bp, SdRec *sdr ) +{ + if (sdr->how == SHUT_HALT) + str_cat( bp, "halt," ); + else + str_cat( bp, "reboot," ); + if (sdr->start == TO_INF) + str_cat( bp, "0," ); + else + *bp += sprintf( *bp, "%d,", sdr->start ); + if (sdr->timeout == TO_INF) + str_cat( bp, "-1," ); + else + *bp += sprintf( *bp, "%d,", sdr->timeout ); + if (sdr->force == SHUT_ASK) + str_cat( bp, "ask" ); + else if (sdr->force == SHUT_FORCE) + str_cat( bp, "force" ); + else if (sdr->force == SHUT_FORCEMY) + str_cat( bp, "forcemy" ); + else + str_cat( bp, "cancel" ); + *bp += sprintf( *bp, ",%d,%s", sdr->uid, sdr->osname ? sdr->osname : "-" ); +} + +static void +emitXSessC( struct display *di, struct display *d, void *ctx ) +{ + char *dname, *bp; + char cbuf[1024]; + + bp = cbuf; + *bp++ = '\t'; + dname = di->name; + if (!memcmp( dname, "localhost:", 10 )) + dname += 9; + str_cat_l( &bp, dname, sizeof(cbuf)/2 ); + *bp++ = ','; +#ifdef HAVE_VTS + if (di->serverVT) + bp += sprintf( bp, "vt%d", di->serverVT ); +#endif + *bp++ = ','; +#ifdef XDMCP + if (di->status == remoteLogin) { + *bp++ = ','; + str_cat_l( &bp, di->remoteHost, sizeof(cbuf)/3 ); + } else +#endif + { + if (di->userName) + str_cat_l( &bp, di->userName, sizeof(cbuf)/5 ); + *bp++ = ','; + if (di->sessName) + str_cat_l( &bp, di->sessName, sizeof(cbuf)/5 ); + } + *bp++ = ','; + if (di == d) + *bp++ = '*'; + if (di->userSess >= 0 && + (d ? (d->userSess != di->userSess && + (d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && d->userSess))) : + !fifoAllowNuke)) + *bp++ = '!'; + Writer( (int)ctx, cbuf, bp - cbuf ); +} + +static void +emitTTYSessC( STRUCTUTMP *ut, struct display *d, void *ctx ) +{ + struct passwd *pw; + char *bp; + int vt, l; + char cbuf[sizeof(ut->ut_line) + sizeof(ut->ut_user) + sizeof(ut->ut_host) + 16]; + char user[sizeof(ut->ut_user) + 1]; + +#ifndef BSD_UTMP + if (ut->ut_type != USER_PROCESS) + l = 0; + else +#endif + { + l = StrNLen( ut->ut_user, sizeof(ut->ut_user) ); + memcpy( user, ut->ut_user, l ); + } + user[l] = 0; + bp = cbuf; + *bp++ = '\t'; + str_cat_l( &bp, ut->ut_line, sizeof(ut->ut_line) ); + *bp++ = ','; + if (*ut->ut_host) { + *bp++ = '@'; + str_cat_l( &bp, ut->ut_host, sizeof(ut->ut_host) ); + } +#ifdef HAVE_VTS + else if ((vt = TTYtoVT( ut->ut_line ))) + bp += sprintf( bp, "vt%d", vt ); +#endif + *bp++ = ','; + str_cat( &bp, user ); + *bp++ = ','; + /* blank: session type unknown */ + *bp++ = ','; + /* blank: certainly not querying display */ + *bp++ = 't'; + if (*user && + (d ? ((d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && d->userSess)) && + (!(pw = getpwnam( user )) || d->userSess != (int)pw->pw_uid)) : + !fifoAllowNuke)) + *bp++ = '!'; + Writer( (int)ctx, cbuf, bp - cbuf ); +} + +static void +processCtrl( const char *string, int len, int fd, struct display *d ) +{ +#define Reply(t) Writer (fd, t, strlen (t)) + + struct display *di; + const char *word; + char **ar, **ap, *args, *bp; + SdRec sdr; + char cbuf[1024]; + + if (!(ar = initStrArr( 0 ))) + return; + for (word = string; ; string++, len--) + if (!len || *string == '\t') { + if (!(ar = addStrArr( ar, word, string - word ))) + return; + if (!len) + break; + word = string + 1; + } + word = fd >= 0 ? "socket" : "FiFo"; + if (d) + Debug( "control %s for %s received %'[s\n", word, d->name, ar ); + else + Debug( "global control %s received %'[s\n", word, ar ); + if (ar[0]) { + if (fd >= 0 && !strcmp( ar[0], "caps" )) { + if (ar[1]) + goto exce; + Reply( "ok\ttdm\tlist\t" ); + if (bootManager != BO_NONE) + Reply( "bootoptions\t" ); + if (d) { + if ((d->displayType & d_location) == dLocal) +#ifdef HAVE_VTS + Reply( "local\tactivate\t" ); +#else + Reply( "local\t" ); +#endif + if (d->allowShutdown != SHUT_NONE) { + if (d->allowShutdown == SHUT_ROOT && d->userSess) + Reply( "shutdown root\t" ); + else + Reply( "shutdown\t" ); + Reply( "shutdown ask\t" ); + if (d->allowNuke != SHUT_NONE) { + if (d->allowNuke == SHUT_ROOT && d->userSess) + Reply( "nuke root\t" ); + else + Reply( "nuke\t" ); + } + } + if ((d->displayType & d_location) == dLocal && + AnyReserveDisplays()) + Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t", + idleReserveDisplays() ) ); + Reply( "lock\tsuicide\n" ); + } else { + if (fifoAllowShutdown) { + Reply( "shutdown\t" ); + if (fifoAllowNuke) + Reply( "nuke\t" ); + } + if (AnyReserveDisplays()) + Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t", + idleReserveDisplays() ) ); +#ifdef HAVE_VTS + Reply( "login\tactivate\n" ); +#else + Reply( "login\n" ); +#endif + } + goto bust; + } else if (fd >= 0 && !strcmp( ar[0], "list" )) { + int flags = lstRemote | lstTTY; + if (ar[1]) { + if (!strcmp( ar[1], "all" )) + flags = lstRemote | lstPassive | lstTTY; + else if (!strcmp( ar[1], "alllocal" )) + flags = lstPassive | lstTTY; + else { + fLog( d, fd, "bad", "invalid list scope %\"s", ar[1] ); + goto bust; + } + if (ar[2]) + goto exce; + } + Reply( "ok" ); + ListSessions( flags, d, (void *)fd, emitXSessC, emitTTYSessC ); + Reply( "\n" ); + goto bust; + } else if (!strcmp( ar[0], "reserve" )) { + int lt = 60; /* XXX make default timeout configurable? */ + if (ar[1]) { + lt = strtol( ar[1], &bp, 10 ); + if (lt < 15 || *bp) { + fLog( d, fd, "bad", "invalid timeout %\"s", ar[1] ); + goto bust; + } + if (ar[2]) + goto exce; + } + if (d && (d->displayType & d_location) != dLocal) { + fLog( d, fd, "perm", "display is not local" ); + goto bust; + } + if (!StartReserveDisplay( lt )) { + fLog( d, fd, "noent", "no reserve display available" ); + goto bust; + } +#ifdef HAVE_VTS + } else if (!strcmp( ar[0], "activate" )) { + int vt; + if (!ar[1]) + goto miss; + if (ar[2]) + goto exce; + if (d && (d->displayType & d_location) != dLocal) { + fLog( d, fd, "perm", "display is not local" ); + goto bust; + } + if (ar[1][0] != 'v' || ar[1][1] != 't' || + (vt = atoi( ar[1] + 2 )) <= 0) + { + if (!(di = FindDisplayByName( ar[1] ))) { + fLog( d, fd, "noent", "display not found" ); + goto bust; + } + if ((di->displayType & d_location) != dLocal) { + fLog( d, fd, "inval", "target display is not local" ); + goto bust; + } + if (!di->serverVT) { + fLog( d, fd, "noent", "target display has no VT assigned" ); + goto bust; + } + vt = di->serverVT; + } + if (!activateVT( vt )) { + fLog( d, fd, "inval", "VT switch failed" ); + goto bust; + } +#endif + } else if (!strcmp( ar[0], "shutdown" )) { + ap = ar; + if (!*++ap) + goto miss; + sdr.force = SHUT_CANCEL; + sdr.osname = 0; + if (!strcmp( *ap, "status" )) { + if (fd < 0) + goto bust; + if (*++ap) + goto exce; + bp = cbuf; + *bp++ = 'o'; + *bp++ = 'k'; + if (sdRec.how) { + str_cat( &bp, "\tglobal," ); + sd_cat( &bp, &sdRec ); + } + if (d && d->hstent->sdRec.how) { + str_cat( &bp, "\tlocal," ); + sd_cat( &bp, &d->hstent->sdRec ); + } + *bp++ = '\n'; + Writer( fd, cbuf, bp - cbuf ); + goto bust; + } else if (!strcmp( *ap, "cancel" )) { + sdr.how = 0; + sdr.start = 0; + if (ap[1]) { + if (!d) + goto exce; + if (!strcmp( *++ap, "global" )) + sdr.start = TO_INF; + else if (strcmp( *ap, "local" )) { + fLog( d, fd, "bad", "invalid cancel scope %\"s", *ap ); + goto bust; + } + } + } else { + if (!strcmp( *ap, "reboot" )) + sdr.how = SHUT_REBOOT; + else if (!strcmp( *ap, "halt" )) + sdr.how = SHUT_HALT; + else { + fLog( d, fd, "bad", "invalid type %\"s", *ap ); + goto bust; + } + sdr.uid = -1; + if (!*++ap) + goto miss; + if (**ap == '=') { + switch (setBootOption( *ap + 1, &sdr )) { + case BO_NOMAN: + fLog( d, fd, "notsup", "boot options unavailable" ); + goto bust; + case BO_NOENT: + fLog( d, fd, "noent", "no such boot option" ); + goto bust; + case BO_IO: + fLog( d, fd, "io", "io error" ); + goto bust; + } + if (!*++ap) + goto miss; + } + sdr.start = strtol( *ap, &bp, 10 ); + if (bp != *ap && !*bp) { + if (**ap == '+') + sdr.start += now; + if (!*++ap) + goto miss; + sdr.timeout = strtol( *ap, &bp, 10 ); + if (bp == *ap || *bp) { + fLog( d, fd, "bad", "invalid timeout %\"s", ar[3] ); + goto bust; + } + if (**ap == '+') + sdr.timeout += sdr.start ? sdr.start : now; + if (sdr.timeout < 0) + sdr.timeout = TO_INF; + else { + if (!*++ap) + goto miss; + if (!strcmp( *ap, "force" )) + sdr.force = SHUT_FORCE; + else if (d && !strcmp( *ap, "forcemy" )) + sdr.force = SHUT_FORCEMY; + else if (strcmp( *ap, "cancel" )) { + fLog( d, fd, "bad", "invalid timeout action %\"s", + *ap ); + goto bust; + } + } + } else { + sdr.timeout = 0; + if (d && !strcmp( *ap, "ask" )) + sdr.force = SHUT_ASK; + else if (!strcmp( *ap, "forcenow" )) + sdr.force = SHUT_FORCE; + else if (!strcmp( *ap, "schedule" )) + sdr.timeout = TO_INF; + else if (strcmp( *ap, "trynow" )) { + fLog( d, fd, "bad", "invalid mode %\"s", *ap ); + goto bust; + } + } + } + if (*++ap) + goto exce; + if (d) { + sdr.uid = d->userSess >= 0 ? d->userSess : 0; + if (d->allowShutdown == SHUT_NONE || + (d->allowShutdown == SHUT_ROOT && sdr.uid && + sdr.force != SHUT_ASK)) + { + fLog( d, fd, "perm", "shutdown forbidden" ); + goto bust; + } + if (!sdr.how && !sdr.start) { + if (d->hstent->sdRec.osname) + free( d->hstent->sdRec.osname ); + d->hstent->sdRec = sdr; + } else { + if (sdRec.how && sdRec.force == SHUT_FORCE && + ((d->allowNuke == SHUT_NONE && sdRec.uid != sdr.uid) || + (d->allowNuke == SHUT_ROOT && sdr.uid))) + { + fLog( d, fd, "perm", "overriding forced shutdown forbidden" ); + goto bust; + } + if (sdr.force == SHUT_FORCE && + (d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && sdr.uid))) + { + fLog( d, fd, "perm", "forced shutdown forbidden" ); + goto bust; + } + if (!sdr.start) { + if (d->hstent->sdRec.osname) + free( d->hstent->sdRec.osname ); + d->hstent->sdRec = sdr; + } else { + if (!sdr.how) + cancelShutdown(); + else { + if (sdRec.osname) + free( sdRec.osname ); + sdRec = sdr; + } + } + } + } else { + if (!fifoAllowShutdown) { + fLog( d, fd, "perm", "shutdown forbidden" ); + goto bust; + } + if (sdRec.how && sdRec.force == SHUT_FORCE && + sdRec.uid != -1 && !fifoAllowNuke) + { + fLog( d, fd, "perm", "overriding forced shutdown forbidden" ); + goto bust; + } + if (!sdr.how) + cancelShutdown(); + else { + if (sdr.force != SHUT_CANCEL) { + if (!fifoAllowNuke) { + fLog( d, fd, "perm", "forced shutdown forbidden" ); + goto bust; + } + } else { + if (!sdr.start && !sdr.timeout && AnyActiveDisplays()) { + fLog( d, fd, "busy", "user sessions running" ); + goto bust; + } + } + sdr.uid = -1; + if (sdRec.osname) + free( sdRec.osname ); + sdRec = sdr; + } + } + } else if (fd >= 0 && !strcmp( ar[0], "listbootoptions" )) { + char **opts; + int def, cur, i, j; + + if (ar[1]) + goto exce; + switch (getBootOptions( &opts, &def, &cur )) { + case BO_NOMAN: + fLog( d, fd, "notsup", "boot options unavailable" ); + goto bust; + case BO_IO: + fLog( d, fd, "io", "io error" ); + goto bust; + } + Reply( "ok\t" ); + for (i = 0; opts[i]; i++) { + bp = cbuf; + if (i) + *bp++ = ' '; + for (j = 0; opts[i][j]; j++) + if (opts[i][j] == ' ') { + *bp++ = '\\'; + *bp++ = 's'; + } else + *bp++ = opts[i][j]; + Writer( fd, cbuf, bp - cbuf ); + } + freeStrArr( opts ); + Writer( fd, cbuf, sprintf( cbuf, "\t%d\t%d\n", def, cur ) ); + goto bust; + } else if (d) { + if (!strcmp( ar[0], "lock" )) { + if (ar[1]) + goto exce; + d->hstent->lock = 1; + } else if (!strcmp( ar[0], "unlock" )) { + if (ar[1]) + goto exce; + d->hstent->lock = 0; + } else if (!strcmp( ar[0], "suicide" )) { + if (ar[1]) + goto exce; + if (d->status == running && d->pid != -1) { + TerminateProcess( d->pid, SIGTERM ); + d->status = raiser; + } + } else { + fLog( d, fd, "nosys", "unknown command" ); + goto bust; + } + } else { + if (!strcmp( ar[0], "login" )) { + int nuke; + if (arrLen( ar ) < 5) { + miss: + fLog( d, fd, "bad", "missing argument(s)" ); + goto bust; + } + if (!(di = FindDisplayByName( ar[1] ))) { + fLog( d, fd, "noent", "display %s not found", ar[1] ); + goto bust; + } + if (ar[5]) { + if (!(args = unQuote( ar[5] ))) { + fLog( d, fd, "nomem", "out of memory" ); + goto bust; + } + if (ar[6]) { + free( args ); + exce: + fLog( d, fd, "bad", "excess argument(s)" ); + goto bust; + } + setNLogin( di, ar[3], ar[4], args, 2 ); + free( args ); + } else + setNLogin( di, ar[3], ar[4], 0, 2 ); + nuke = !strcmp( ar[2], "now" ); + switch (di->status) { + case running: + if (di->pid != -1 && (di->userSess < 0 || nuke)) { + TerminateProcess( di->pid, SIGTERM ); + di->status = raiser; + } + break; + case remoteLogin: + if (di->serverPid != -1 && nuke) + TerminateProcess( di->serverPid, di->termSignal ); + break; + case reserve: + di->status = notRunning; + break; + case textMode: +#ifndef HAVE_VTS + SwitchToX( di ); +#endif + break; + default: + break; + } + } else { + fLog( d, fd, "nosys", "unknown command" ); + goto bust; + } + } + if (fd >= 0) + Reply( "ok\n" ); + } + bust: + freeStrArr( ar ); +} + +static int +handleChan( struct display *d, struct bsock *cs, int fd, FD_TYPE *reads ) +{ + char *bufp, *nbuf, *obuf, *eol; + int len, bl, llen; + char buf[1024]; + + bl = cs->buflen; + obuf = cs->buffer; + if (bl <= 0 && FD_ISSET( cs->fd, reads )) { + FD_CLR( cs->fd, reads ); + bl = -bl; + memcpy( buf, obuf, bl ); + if ((len = Reader( cs->fd, buf + bl, sizeof(buf) - bl )) <= 0) + return -1; + bl += len; + bufp = buf; + } else { + len = 0; + bufp = obuf; + } + if (bl > 0) { + if ((eol = memchr( bufp, '\n', bl ))) { + llen = eol - bufp + 1; + bl -= llen; + if (bl) { + if (!(nbuf = Malloc( bl ))) + return -1; + memcpy( nbuf, bufp + llen, bl ); + } else + nbuf = 0; + cs->buffer = nbuf; + cs->buflen = bl; + processCtrl( bufp, llen - 1, fd, d ); + if (obuf) + free( obuf ); + return 1; + } else if (!len) { + if (fd >= 0) + cs->buflen = -bl; + else + fLog( d, -1, "bad", "unterminated command" ); + } + } + return 0; +} + +int +handleCtrl( FD_TYPE *reads, struct display *d ) +{ + CtrlRec *cr = d ? &d->ctrl : &ctrl; + struct cmdsock *cs, **csp; + + if (cr->fifo.fd >= 0) { + switch (handleChan( d, &cr->fifo, -1, reads )) { + case -1: + if (cr->fifo.buffer) + free( cr->fifo.buffer ); + cr->fifo.buflen = 0; + break; + case 1: + return 1; + default: + break; + } + } + if (cr->fd >= 0 && FD_ISSET( cr->fd, reads )) + acceptSock( cr ); + else { + for (csp = &cr->css; (cs = *csp); ) { + switch (handleChan( d, &cs->sock, cs->sock.fd, reads )) { + case -1: + *csp = cs->next; + nukeSock( cs ); + continue; + case 1: + return 1; + default: + break; + } + csp = &cs->next; + } + } + return 0; +} |