summaryrefslogtreecommitdiffstats
path: root/src/dmctl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dmctl.cpp')
-rw-r--r--src/dmctl.cpp434
1 files changed, 434 insertions, 0 deletions
diff --git a/src/dmctl.cpp b/src/dmctl.cpp
new file mode 100644
index 0000000..9f24a52
--- /dev/null
+++ b/src/dmctl.cpp
@@ -0,0 +1,434 @@
+/*
+ Copyright (C) 2004 Oswald Buddenhagen <[email protected]>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the Lesser GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "dmctl.h"
+
+#ifdef Q_WS_X11
+
+#include <klocale.h>
+#include <dcopclient.h>
+
+#include <qregexp.h>
+
+#include <X11/Xauth.h>
+#include <X11/Xlib.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static enum { Dunno, NoDM, NewKDM, OldKDM, GDM } DMType = Dunno;
+static const char *ctl, *dpy;
+
+DM::DM() : fd( -1 )
+{
+ const char *ptr;
+ struct sockaddr_un sa;
+
+ if (DMType == Dunno) {
+ if (!(dpy = ::getenv( "DISPLAY" )))
+ DMType = NoDM;
+ else if ((ctl = ::getenv( "DM_CONTROL" )))
+ DMType = NewKDM;
+ else if ((ctl = ::getenv( "XDM_MANAGED" )) && ctl[0] == '/')
+ DMType = OldKDM;
+ else if (::getenv( "GDMSESSION" ))
+ DMType = GDM;
+ else
+ DMType = NoDM;
+ }
+ switch (DMType) {
+ default:
+ return;
+ case NewKDM:
+ case GDM:
+ if ((fd = ::socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
+ return;
+ sa.sun_family = AF_UNIX;
+ if (DMType == GDM)
+ strcpy( sa.sun_path, "/tmp/.gdm_socket" );
+ else {
+ if ((ptr = strchr( dpy, ':' )))
+ ptr = strchr( ptr, '.' );
+ snprintf( sa.sun_path, sizeof(sa.sun_path),
+ "%s/dmctl-%.*s/socket",
+ ctl, ptr ? ptr - dpy : 512, dpy );
+ }
+ if (::connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) {
+ ::close( fd );
+ fd = -1;
+ }
+ if (DMType == GDM)
+ GDMAuthenticate();
+ break;
+ case OldKDM:
+ {
+ QString tf( ctl );
+ tf.truncate( tf.find( ',' ) );
+ fd = ::open( tf.latin1(), O_WRONLY );
+ }
+ break;
+ }
+}
+
+DM::~DM()
+{
+ if (fd >= 0)
+ close( fd );
+}
+
+bool
+DM::exec( const char *cmd )
+{
+ QCString buf;
+
+ return exec( cmd, buf );
+}
+
+/**
+ * Execute a KDM/GDM remote control command.
+ * @param cmd the command to execute. FIXME: undocumented yet.
+ * @param buf the result buffer.
+ * @return result:
+ * @li If true, the command was successfully executed.
+ * @p ret might contain addional results.
+ * @li If false and @p ret is empty, a communication error occurred
+ * (most probably KDM is not running).
+ * @li If false and @p ret is non-empty, it contains the error message
+ * from KDM.
+ */
+bool
+DM::exec( const char *cmd, QCString &buf )
+{
+ bool ret = false;
+ int tl;
+ unsigned len = 0;
+
+ if (fd < 0)
+ goto busted;
+
+ if (::write( fd, cmd, (tl = strlen( cmd )) ) != tl) {
+ bust:
+ ::close( fd );
+ fd = -1;
+ busted:
+ buf.resize( 0 );
+ return false;
+ }
+ if (DMType == OldKDM) {
+ buf.resize( 0 );
+ return true;
+ }
+ for (;;) {
+ if (buf.size() < 128)
+ buf.resize( 128 );
+ else if (buf.size() < len * 2)
+ buf.resize( len * 2 );
+ if ((tl = ::read( fd, buf.data() + len, buf.size() - len)) <= 0) {
+ if (tl < 0 && errno == EINTR)
+ continue;
+ goto bust;
+ }
+ len += tl;
+ if (buf[len - 1] == '\n') {
+ buf[len - 1] = 0;
+ if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') &&
+ (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= 32)
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+bool
+DM::canShutdown()
+{
+ if (DMType == OldKDM)
+ return strstr( ctl, ",maysd" ) != 0;
+
+ QCString re;
+
+ if (DMType == GDM)
+ return exec( "QUERY_LOGOUT_ACTION\n", re ) && re.find("HALT") >= 0;
+
+ return exec( "caps\n", re ) && re.find( "\tshutdown" ) >= 0;
+}
+
+void
+DM::shutdown( KApplication::ShutdownType shutdownType,
+ KApplication::ShutdownMode shutdownMode, /* NOT Default */
+ const QString &bootOption )
+{
+ if (shutdownType == KApplication::ShutdownTypeNone)
+ return;
+
+ bool cap_ask;
+ if (DMType == NewKDM) {
+ QCString re;
+ cap_ask = exec( "caps\n", re ) && re.find( "\tshutdown ask" ) >= 0;
+ } else {
+ if (!bootOption.isEmpty())
+ return;
+ cap_ask = false;
+ }
+ if (!cap_ask && shutdownMode == KApplication::ShutdownModeInteractive)
+ shutdownMode = KApplication::ShutdownModeForceNow;
+
+ QCString cmd;
+ if (DMType == GDM) {
+ cmd.append( shutdownMode == KApplication::ShutdownModeForceNow ?
+ "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION " );
+ cmd.append( shutdownType == KApplication::ShutdownTypeReboot ?
+ "REBOOT\n" : "HALT\n" );
+ } else {
+ cmd.append( "shutdown\t" );
+ cmd.append( shutdownType == KApplication::ShutdownTypeReboot ?
+ "reboot\t" : "halt\t" );
+ if (!bootOption.isNull())
+ cmd.append( "=" ).append( bootOption.local8Bit() ).append( "\t" );
+ cmd.append( shutdownMode == KApplication::ShutdownModeInteractive ?
+ "ask\n" :
+ shutdownMode == KApplication::ShutdownModeForceNow ?
+ "forcenow\n" :
+ shutdownMode == KApplication::ShutdownModeTryNow ?
+ "trynow\n" : "schedule\n" );
+ }
+ exec( cmd.data() );
+}
+
+bool
+DM::bootOptions( QStringList &opts, int &defopt, int &current )
+{
+ if (DMType != NewKDM)
+ return false;
+
+ QCString re;
+ if (!exec( "listbootoptions\n", re ))
+ return false;
+
+ opts = QStringList::split( '\t', QString::fromLocal8Bit( re.data() ) );
+ if (opts.size() < 4)
+ return false;
+
+ bool ok;
+ defopt = opts[2].toInt( &ok );
+ if (!ok)
+ return false;
+ current = opts[3].toInt( &ok );
+ if (!ok)
+ return false;
+
+ opts = QStringList::split( ' ', opts[1] );
+ for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
+ (*it).replace( "\\s", " " );
+
+ return true;
+}
+
+void
+DM::setLock( bool on )
+{
+ if (DMType != GDM)
+ exec( on ? "lock\n" : "unlock\n" );
+}
+
+bool
+DM::isSwitchable()
+{
+ if (DMType == OldKDM)
+ return dpy[0] == ':';
+
+ if (DMType == GDM)
+ return exec( "QUERY_VT\n" );
+
+ QCString re;
+
+ return exec( "caps\n", re ) && re.find( "\tlocal" ) >= 0;
+}
+
+int
+DM::numReserve()
+{
+ if (DMType == GDM)
+ return 1; /* Bleh */
+
+ if (DMType == OldKDM)
+ return strstr( ctl, ",rsvd" ) ? 1 : -1;
+
+ QCString re;
+ int p;
+
+ if (!(exec( "caps\n", re ) && (p = re.find( "\treserve " )) >= 0))
+ return -1;
+ return atoi( re.data() + p + 9 );
+}
+
+void
+DM::startReserve()
+{
+ if (DMType == GDM)
+ exec("FLEXI_XSERVER\n");
+ else
+ exec("reserve\n");
+}
+
+bool
+DM::localSessions( SessList &list )
+{
+ if (DMType == OldKDM)
+ return false;
+
+ QCString re;
+
+ if (DMType == GDM) {
+ if (!exec( "CONSOLE_SERVERS\n", re ))
+ return false;
+ QStringList sess = QStringList::split( QChar(';'), re.data() + 3 );
+ for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
+ QStringList ts = QStringList::split( QChar(','), *it, true );
+ SessEnt se;
+ se.display = ts[0];
+ se.user = ts[1];
+ se.vt = ts[2].toInt();
+ se.session = "<unknown>";
+ se.self = ts[0] == ::getenv( "DISPLAY" ); /* Bleh */
+ se.tty = false;
+ list.append( se );
+ }
+ } else {
+ if (!exec( "list\talllocal\n", re ))
+ return false;
+ QStringList sess = QStringList::split( QChar('\t'), re.data() + 3 );
+ for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
+ QStringList ts = QStringList::split( QChar(','), *it, true );
+ SessEnt se;
+ se.display = ts[0];
+ if (ts[1][0] == '@')
+ se.from = ts[1].mid( 1 ), se.vt = 0;
+ else
+ se.vt = ts[1].mid( 2 ).toInt();
+ se.user = ts[2];
+ se.session = ts[3];
+ se.self = (ts[4].find( '*' ) >= 0);
+ se.tty = (ts[4].find( 't' ) >= 0);
+ list.append( se );
+ }
+ }
+ return true;
+}
+
+void
+DM::sess2Str2( const SessEnt &se, QString &user, QString &loc )
+{
+ if (se.tty) {
+ user = i18n("user: ...", "%1: TTY login").arg( se.user );
+ loc = se.vt ? QString("vt%1").arg( se.vt ) : se.display ;
+ } else {
+ user =
+ se.user.isEmpty() ?
+ se.session.isEmpty() ?
+ i18n("Unused") :
+ se.session == "<remote>" ?
+ i18n("X login on remote host") :
+ i18n("... host", "X login on %1").arg( se.session ) :
+ se.session == "<unknown>" ?
+ se.user :
+ i18n("user: session type", "%1: %2")
+ .arg( se.user ).arg( se.session );
+ loc =
+ se.vt ?
+ QString("%1, vt%2").arg( se.display ).arg( se.vt ) :
+ se.display;
+ }
+}
+
+QString
+DM::sess2Str( const SessEnt &se )
+{
+ QString user, loc;
+
+ sess2Str2( se, user, loc );
+ return i18n("session (location)", "%1 (%2)").arg( user ).arg( loc );
+}
+
+bool
+DM::switchVT( int vt )
+{
+ if (DMType == GDM)
+ return exec( QString("SET_VT %1\n").arg(vt).latin1() );
+
+ return exec( QString("activate\tvt%1\n").arg(vt).latin1() );
+}
+
+void
+DM::lockSwitchVT( int vt )
+{
+ if (switchVT( vt ))
+ kapp->dcopClient()->send( "kdesktop", "KScreensaverIface", "lock()", "" );
+}
+
+void
+DM::GDMAuthenticate()
+{
+ FILE *fp;
+ const char *dpy, *dnum, *dne;
+ int dnl;
+ Xauth *xau;
+
+ dpy = DisplayString( QPaintDevice::x11AppDisplay() );
+ if (!dpy) {
+ dpy = ::getenv( "DISPLAY" );
+ if (!dpy)
+ return;
+ }
+ dnum = strchr( dpy, ':' ) + 1;
+ dne = strchr( dpy, '.' );
+ dnl = dne ? dne - dnum : strlen( dnum );
+
+ /* XXX should do locking */
+ if (!(fp = fopen( XauFileName(), "r" )))
+ return;
+
+ while ((xau = XauReadAuth( fp ))) {
+ if (xau->family == FamilyLocal &&
+ xau->number_length == dnl && !memcmp( xau->number, dnum, dnl ) &&
+ xau->data_length == 16 &&
+ xau->name_length == 18 && !memcmp( xau->name, "MIT-MAGIC-COOKIE-1", 18 ))
+ {
+ QString cmd( "AUTH_LOCAL " );
+ for (int i = 0; i < 16; i++)
+ cmd += QString::number( (uchar)xau->data[i], 16 ).rightJustify( 2, '0');
+ cmd += "\n";
+ if (exec( cmd.latin1() )) {
+ XauDisposeAuth( xau );
+ break;
+ }
+ }
+ XauDisposeAuth( xau );
+ }
+
+ fclose (fp);
+}
+
+#endif // Q_WS_X11