summaryrefslogtreecommitdiffstats
path: root/kdm/kfrontend/kdm_greet.c
diff options
context:
space:
mode:
Diffstat (limited to 'kdm/kfrontend/kdm_greet.c')
-rw-r--r--kdm/kfrontend/kdm_greet.c787
1 files changed, 787 insertions, 0 deletions
diff --git a/kdm/kfrontend/kdm_greet.c b/kdm/kfrontend/kdm_greet.c
new file mode 100644
index 000000000..6abc2c057
--- /dev/null
+++ b/kdm/kfrontend/kdm_greet.c
@@ -0,0 +1,787 @@
+/*
+
+KDE Greeter module for xdm
+
+Copyright (C) 2001-2003 Oswald Buddenhagen <ossi@kde.org>
+
+This file contains code from the old xdm core,
+Copyright 1988, 1998 Keith Packard, MIT X Consortium/The Open Group
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the 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 GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include <config.h>
+
+#include "kdm_greet.h"
+#include "kdmconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#if defined(HAVE_XTEST) || defined(HAVE_XKB)
+# include <X11/Xlib.h>
+# include <X11/keysym.h>
+#endif
+
+#ifdef HAVE_XTEST
+# include <X11/extensions/XTest.h>
+#endif
+
+#ifdef HAVE_XKB
+# include <X11/XKBlib.h>
+#endif
+
+extern void LogOutOfMem( void );
+
+static void *
+Realloc( void *ptr, size_t size )
+{
+ void *ret;
+
+ if (!(ret = realloc( ptr, size )) && size)
+ LogOutOfMem();
+ return ret;
+}
+
+#define PRINT_QUOTES
+#define PRINT_ARRAYS
+#define LOG_NAME "kdm_greet"
+#define LOG_DEBUG_MASK DEBUG_GREET
+#define LOG_PANIC_EXIT 1
+#define STATIC
+#include <printf.c>
+
+static void
+GDebug( const char *fmt, ... )
+{
+ va_list args;
+
+ if (debugLevel & DEBUG_HLPCON) {
+ va_start( args, fmt );
+ Logger( DM_DEBUG, fmt, args );
+ va_end( args );
+ }
+}
+
+
+char *dname;
+
+int rfd;
+static int wfd, mrfd, mwfd, srfd, swfd;
+static const char *who;
+
+void
+GSet( int master )
+{
+ if (master)
+ rfd = mrfd, wfd = mwfd, who = "core (master)";
+ else
+ rfd = srfd, wfd = swfd, who = "core";
+
+}
+
+static int
+Reader( void *buf, int count )
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = read( rfd, (void *)((char *)buf + rlen), count - rlen );
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+static void
+GRead( void *buf, int count )
+{
+ if (Reader( buf, count ) != count)
+ LogPanic( "Can't read from %s\n", who );
+}
+
+static void
+GWrite( const void *buf, int count )
+{
+ if (write( wfd, buf, count ) != count)
+ LogPanic( "Can't write to %s\n", who );
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ if ((debugLevel & DEBUG_HLPCON))
+ sched_yield();
+#endif
+}
+
+void
+GSendInt( int val )
+{
+ GDebug( "Sending int %d (%#x) to %s\n", val, val, who );
+ GWrite( &val, sizeof(val) );
+}
+
+void
+GSendStr( const char *buf )
+{
+ int len = buf ? strlen( buf ) + 1 : 0;
+ GDebug( "Sending string %'s to %s\n", buf, who );
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+}
+
+/*
+static void
+GSendNStr( const char *buf, int len )
+{
+ int tlen = len + 1;
+ GDebug( "Sending string %'.*s to %s\n", len, buf, who );
+ GWrite( &tlen, sizeof(tlen) );
+ GWrite( buf, len );
+ GWrite( "", 1 );
+}
+*/
+
+void
+GSendArr( int len, const char *buf )
+{
+ GDebug( "Sending array %02[:*hhx to %s\n", len, buf, who );
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+}
+
+int
+GRecvInt()
+{
+ int val;
+
+ GDebug( "Receiving int from %s ...\n", who );
+ GRead( &val, sizeof(val) );
+ GDebug( " -> %d (%#x)\n", val, val );
+ return val;
+}
+
+static char *
+iGRecvArr( int *rlen )
+{
+ int len;
+ char *buf;
+
+ GRead( &len, sizeof(len) );
+ *rlen = len;
+ GDebug( " -> %d bytes\n", len );
+ if (!len)
+ return (char *)0;
+ if (!(buf = malloc( len )))
+ LogPanic( "No memory for read buffer\n" );
+ GRead( buf, len );
+ return buf;
+}
+
+char *
+GRecvStr()
+{
+ int len;
+ char *buf;
+
+ GDebug( "Receiving string from %s ...\n", who );
+ buf = iGRecvArr( &len );
+ GDebug( " -> %'.*s\n", len, buf );
+ return buf;
+}
+
+char **
+GRecvStrArr( int *rnum )
+{
+ int num;
+ char **argv, **cargv;
+
+ GDebug( "Receiving string array from %s ...\n", who );
+ GRead( &num, sizeof(num) );
+ GDebug( " -> %d strings\n", num );
+ if (rnum)
+ *rnum = num;
+ if (!num)
+ return (char **)0;
+ if (!(argv = malloc( num * sizeof(char *))))
+ LogPanic( "No memory for read buffer\n" );
+ for (cargv = argv; --num >= 0; cargv++)
+ *cargv = GRecvStr();
+ return argv;
+}
+
+char *
+GRecvArr( int *num )
+{
+ char *arr;
+
+ GDebug( "Receiving array from %s ...\n", who );
+ GRead( num, sizeof(*num) );
+ GDebug( " -> %d bytes\n", *num );
+ if (!*num)
+ return (char *)0;
+ if (!(arr = malloc( *num )))
+ LogPanic( "No memory for read buffer\n" );
+ GRead( arr, *num );
+ GDebug( " -> %02[*hhx\n", *num, arr );
+ return arr;
+}
+
+static void
+ReqCfg( int id )
+{
+ GSendInt( G_GetCfg );
+ GSendInt( id );
+ switch (GRecvInt()) {
+ case GE_NoEnt:
+ LogPanic( "Config value %#x not available\n", id );
+ case GE_BadType:
+ LogPanic( "Core does not know type of config value %#x\n", id );
+ }
+}
+
+int
+GetCfgInt( int id )
+{
+ ReqCfg( id );
+ return GRecvInt();
+}
+
+char *
+GetCfgStr( int id )
+{
+ ReqCfg( id );
+ return GRecvStr();
+}
+
+char **
+GetCfgStrArr( int id, int *len )
+{
+ ReqCfg( id );
+ return GRecvStrArr( len );
+}
+
+static void
+disposeSession( dpySpec *sess )
+{
+ free( sess->display );
+ free( sess->from );
+ if (sess->user)
+ free( sess->user );
+ if (sess->session)
+ free( sess->session );
+}
+
+dpySpec *
+fetchSessions( int flags )
+{
+ dpySpec *sess, *sessions = 0, tsess;
+
+ GSet( 1 );
+ GSendInt( G_List );
+ GSendInt( flags );
+ next:
+ while ((tsess.display = GRecvStr())) {
+ tsess.from = GRecvStr();
+#ifdef HAVE_VTS
+ tsess.vt = GRecvInt();
+#endif
+ tsess.user = GRecvStr();
+ tsess.session = GRecvStr();
+ tsess.flags = GRecvInt();
+ if ((tsess.flags & isTTY) && *tsess.from)
+ for (sess = sessions; sess; sess = sess->next)
+ if (sess->user && !strcmp( sess->user, tsess.user ) &&
+ !strcmp( sess->from, tsess.from ))
+ {
+ sess->count++;
+ disposeSession( &tsess );
+ goto next;
+ }
+ if (!(sess = malloc( sizeof(*sess) )))
+ LogPanic( "Out of memory\n" );
+ tsess.count = 1;
+ tsess.next = sessions;
+ *sess = tsess;
+ sessions = sess;
+ }
+ GSet( 0 );
+ return sessions;
+}
+
+void
+disposeSessions( dpySpec *sess )
+{
+ while (sess) {
+ dpySpec *nsess = sess->next;
+ disposeSession( sess );
+ free( sess );
+ sess = nsess;
+ }
+}
+
+void
+freeStrArr( char **arr )
+{
+ char **tarr;
+
+ if (arr) {
+ for (tarr = arr; *tarr; tarr++)
+ free( *tarr );
+ free( arr );
+ }
+}
+
+
+static int
+ignoreErrors( Display *dpy ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED )
+{
+ Debug( "ignoring X error\n" );
+ return 0;
+}
+
+/*
+ * this is mostly bogus -- but quite useful. I wish the protocol
+ * had some way of enumerating and identifying clients, that way
+ * this code wouldn't have to be this kludgy.
+ */
+
+static void
+killWindows( Display *dpy, Window window )
+{
+ Window root, parent, *children;
+ unsigned child, nchildren = 0;
+
+ while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren )
+ && nchildren > 0)
+ {
+ for (child = 0; child < nchildren; child++) {
+ Debug( "XKillClient 0x%lx\n", (unsigned long)children[child] );
+ XKillClient( dpy, children[child] );
+ }
+ XFree( (char *)children );
+ }
+}
+
+static jmp_buf resetJmp;
+
+static void
+abortReset( int n ATTR_UNUSED )
+{
+ longjmp (resetJmp, 1);
+}
+
+/*
+ * this display connection better not have any windows...
+ */
+
+static void
+pseudoReset( Display *dpy )
+{
+ int screen;
+
+ if (setjmp( resetJmp )) {
+ LogError( "pseudoReset timeout\n" );
+ } else {
+ (void)signal( SIGALRM, abortReset );
+ (void)alarm( 30 );
+ XSetErrorHandler( ignoreErrors );
+ for (screen = 0; screen < ScreenCount( dpy ); screen++) {
+ Debug( "pseudoReset screen %d\n", screen );
+ killWindows( dpy, RootWindow( dpy, screen ) );
+ }
+ Debug( "before XSync\n" );
+ XSync( dpy, False );
+ (void)alarm( 0 );
+ }
+ signal( SIGALRM, SIG_DFL );
+ XSetErrorHandler( (XErrorHandler)0 );
+ Debug( "pseudoReset done\n" );
+}
+
+
+static jmp_buf syncJump;
+
+static void
+syncTimeout( int n ATTR_UNUSED )
+{
+ longjmp( syncJump, 1 );
+}
+
+void
+SecureDisplay( Display *dpy )
+{
+ Debug( "SecureDisplay %s\n", dname );
+ (void)signal( SIGALRM, syncTimeout );
+ if (setjmp( syncJump )) {
+ LogError( "Display %s could not be secured\n", dname );
+ exit( EX_RESERVER_DPY );
+ }
+ (void)alarm( (unsigned)_grabTimeout );
+ Debug( "Before XGrabServer %s\n", dname );
+ XGrabServer( dpy );
+ Debug( "XGrabServer succeeded %s\n", dname );
+ if (XGrabKeyboard( dpy, DefaultRootWindow( dpy ), True, GrabModeAsync,
+ GrabModeAsync, CurrentTime ) != GrabSuccess)
+ {
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, SIG_DFL );
+ LogError( "Keyboard on display %s could not be secured\n", dname );
+ sleep( 10 );
+ exit( EX_RESERVER_DPY );
+ }
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, SIG_DFL );
+ pseudoReset( dpy );
+ if (!_grabServer)
+ {
+ XUngrabServer( dpy );
+ XSync( dpy, 0 );
+ }
+ Debug( "done secure %s\n", dname );
+#ifdef HAVE_XKBSETPERCLIENTCONTROLS
+ /*
+ * Activate the correct mapping for modifiers in XKB extension as
+ * grabbed keyboard has its own mapping by default
+ */
+ {
+ int opcode, evbase, errbase, majret, minret;
+ unsigned int value = XkbPCF_GrabsUseXKBStateMask;
+ if (XkbQueryExtension( dpy, &opcode, &evbase,
+ &errbase, &majret, &minret ))
+ XkbSetPerClientControls( dpy, value, &value );
+ }
+#endif
+}
+
+void
+UnsecureDisplay( Display *dpy )
+{
+ Debug( "Unsecure display %s\n", dname );
+ if (_grabServer) {
+ XUngrabServer( dpy );
+ XSync( dpy, 0 );
+ }
+}
+
+static jmp_buf pingTime;
+
+static int
+PingLostIOErr( Display *dpy ATTR_UNUSED )
+{
+ longjmp( pingTime, 1 );
+}
+
+static void
+PingLostSig( int n ATTR_UNUSED )
+{
+ longjmp( pingTime, 1 );
+}
+
+int
+PingServer( Display *dpy )
+{
+ int (*oldError)( Display * );
+ void (*oldSig)( int );
+ int oldAlarm;
+
+ oldError = XSetIOErrorHandler( PingLostIOErr );
+ oldAlarm = alarm( 0 );
+ oldSig = signal( SIGALRM, PingLostSig );
+ (void)alarm( _pingTimeout * 60 );
+ if (!setjmp( pingTime )) {
+ Debug( "Ping server\n" );
+ XSync( dpy, 0 );
+ } else {
+ Debug( "Server dead\n" );
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, SIG_DFL );
+ XSetIOErrorHandler( oldError );
+ return 0;
+ }
+ (void)alarm( 0 );
+ (void)signal( SIGALRM, oldSig );
+ (void)alarm( oldAlarm );
+ Debug( "Server alive\n" );
+ XSetIOErrorHandler( oldError );
+ return 1;
+}
+
+/*
+ * Modifier changing code based on kdebase/kxkb/kcmmisc.cpp
+ *
+ * XTest part: Copyright (C) 2000-2001 Lubos Lunak <l.lunak@kde.org>
+ * XKB part: Copyright (C) 2001-2002 Oswald Buddenhagen <ossi@kde.org>
+ *
+ */
+
+#ifdef HAVE_XKB
+static int
+xkb_init( Display *dpy )
+{
+ int xkb_opcode, xkb_event, xkb_error;
+ int xkb_lmaj = XkbMajorVersion;
+ int xkb_lmin = XkbMinorVersion;
+ return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) &&
+ XkbQueryExtension( dpy, &xkb_opcode, &xkb_event,
+ &xkb_error, &xkb_lmaj, &xkb_lmin );
+}
+
+static unsigned int
+xkb_modifier_mask_work( XkbDescPtr xkb, const char *name )
+{
+ int i;
+
+ if (!xkb->names)
+ return 0;
+ for (i = 0; i < XkbNumVirtualMods; i++) {
+ char *modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] );
+ if (modStr != NULL && strcmp( name, modStr ) == 0) {
+ unsigned int mask;
+ XkbVirtualModsToReal( xkb, 1 << i, &mask );
+ return mask;
+ }
+ }
+ return 0;
+}
+
+static unsigned int
+xkb_modifier_mask( Display *dpy, const char *name )
+{
+ XkbDescPtr xkb;
+
+ if ((xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd ))) {
+ unsigned int mask = xkb_modifier_mask_work( xkb, name );
+ XkbFreeKeyboard( xkb, 0, True );
+ return mask;
+ }
+ return 0;
+}
+
+static int
+xkb_get_modifier_state( Display *dpy, const char *name )
+{
+ unsigned int mask;
+ XkbStateRec state;
+
+ if (!(mask = xkb_modifier_mask( dpy, name )))
+ return 0;
+ XkbGetState( dpy, XkbUseCoreKbd, &state );
+ return (mask & state.locked_mods) != 0;
+}
+
+static int
+xkb_set_modifier( Display *dpy, const char *name, int sts )
+{
+ unsigned int mask;
+
+ if (!(mask = xkb_modifier_mask( dpy, name )))
+ return 0;
+ XkbLockModifiers( dpy, XkbUseCoreKbd, mask, sts ? mask : 0 );
+ return 1;
+}
+#endif /* HAVE_XKB */
+
+#ifdef HAVE_XTEST
+static int
+xtest_get_modifier_state( Display *dpy, int key )
+{
+ XModifierKeymap *map;
+ KeyCode modifier_keycode;
+ unsigned int i, mask;
+ Window dummy1, dummy2;
+ int dummy3, dummy4, dummy5, dummy6;
+
+ if ((modifier_keycode = XKeysymToKeycode( dpy, key )) == NoSymbol)
+ return 0;
+ map = XGetModifierMapping( dpy );
+ for (i = 0; i < 8; ++i)
+ if (map->modifiermap[map->max_keypermod * i] == modifier_keycode) {
+ XFreeModifiermap( map );
+ XQueryPointer( dpy, DefaultRootWindow( dpy ),
+ &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
+ &mask );
+ return (mask & (1 << i)) != 0;
+ }
+ XFreeModifiermap( map );
+ return 0;
+}
+
+static void
+xtest_fake_keypress( Display *dpy, int key )
+{
+ XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), True, CurrentTime );
+ XTestFakeKeyEvent( dpy, XKeysymToKeycode( dpy, key ), False, CurrentTime );
+}
+#endif /* HAVE_XTEST */
+
+#ifdef HAVE_XKB
+static int havexkb;
+#endif
+static int nummodified, oldnumstate, newnumstate;
+static Display *dpy;
+
+void
+setup_modifiers( Display *mdpy, int numlock )
+{
+ if (numlock == 2)
+ return;
+ newnumstate = numlock;
+ nummodified = 1;
+ dpy = mdpy;
+#ifdef HAVE_XKB
+ if (xkb_init( mdpy )) {
+ havexkb = 1;
+ oldnumstate = xkb_get_modifier_state( mdpy, "NumLock" );
+ xkb_set_modifier( mdpy, "NumLock", numlock );
+ return;
+ }
+#endif
+#ifdef HAVE_XTEST
+ oldnumstate = xtest_get_modifier_state( mdpy, XK_Num_Lock );
+ if (oldnumstate != numlock)
+ xtest_fake_keypress( mdpy, XK_Num_Lock );
+#endif
+}
+
+void
+restore_modifiers( void )
+{
+#ifdef HAVE_XTEST
+ int numstat;
+#endif
+
+ if (!nummodified)
+ return;
+#ifdef HAVE_XKB
+ if (havexkb) {
+ if (xkb_get_modifier_state( dpy, "NumLock" ) == newnumstate)
+ xkb_set_modifier( dpy, "NumLock", oldnumstate );
+ return;
+ }
+#endif
+#ifdef HAVE_XTEST
+ numstat = xtest_get_modifier_state( dpy, XK_Num_Lock );
+ if (numstat == newnumstate && newnumstate != oldnumstate)
+ xtest_fake_keypress( dpy, XK_Num_Lock );
+#endif
+}
+
+void
+setCursor( Display *mdpy, int window, int shape )
+{
+ Cursor xcursor;
+
+ if ((xcursor = XCreateFontCursor( mdpy, shape ))) {
+ XDefineCursor( mdpy, window, xcursor );
+ XFreeCursor( mdpy, xcursor );
+ XFlush( mdpy );
+ }
+}
+
+static void
+sigterm( int n ATTR_UNUSED )
+{
+ exit( EX_NORMAL );
+}
+
+static char *savhome;
+
+static void
+cleanup( void )
+{
+ char buf[128];
+
+ if (strcmp( savhome, getenv( "HOME" ) ) || memcmp( savhome, "/tmp/", 5 ))
+ LogError( "Internal error: memory corruption detected\n" ); /* no panic: recursion */
+ else {
+ sprintf( buf, "rm -rf %s", savhome );
+ system( buf );
+ }
+}
+
+extern void kg_main( const char *argv0 );
+
+int
+main( int argc ATTR_UNUSED, char **argv )
+{
+ char *ci;
+ int i;
+ char qtrc[40];
+
+ if (!(ci = getenv( "CONINFO" ))) {
+ fprintf( stderr, "This program is part of kdm and should not be run manually.\n" );
+ return 1;
+ }
+ if (sscanf( ci, "%d %d %d %d", &srfd, &swfd, &mrfd, &mwfd ) != 4)
+ return 1;
+ fcntl( srfd, F_SETFD, FD_CLOEXEC );
+ fcntl( swfd, F_SETFD, FD_CLOEXEC );
+ fcntl( mrfd, F_SETFD, FD_CLOEXEC );
+ fcntl( mwfd, F_SETFD, FD_CLOEXEC );
+ GSet( 0 );
+
+ InitLog();
+
+ if ((debugLevel = GRecvInt()) & DEBUG_WGREET)
+ sleep( 100 );
+
+ signal( SIGTERM, sigterm );
+
+ dname = getenv( "DISPLAY" );
+
+ init_config();
+
+ /* for QSettings */
+ srand( time( 0 ) );
+ for (i = 0; i < 10000; i++) {
+ sprintf( qtrc, "/tmp/%010d", rand() );
+ if (!mkdir( qtrc, 0700 ))
+ goto okay;
+ }
+ LogPanic( "Cannot create $HOME\n" );
+ okay:
+ if (setenv( "HOME", qtrc, 1 ))
+ LogPanic( "Cannot set $HOME\n" );
+ if (!(savhome = strdup( qtrc )))
+ LogPanic( "Cannot save $HOME\n" );
+ atexit( cleanup );
+
+ setenv( "LC_ALL", _language, 1 );
+
+ kg_main( argv[0] );
+
+ return EX_NORMAL;
+}