kde-workspace/kdm/kfrontend/kdm_greet.c
2014-11-13 19:30:51 +02:00

689 lines
15 KiB
C

/*
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-workspace.h>
#include <config-kdm.h>
#include <config-X11.h>
#include "kdm_greet.h"
#include "kdmconfig.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>
#ifdef _POSIX_PRIORITY_SCHEDULING
# include <sched.h>
#endif
# include <X11/Xlib.h>
#if defined(HAVE_XTEST) || defined(HAVE_XKB)
# 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, mrfd, mwfd, srfd, swfd;
static int wfd;
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, (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("Cannot read from %s\n", who);
}
static void
gWrite(const void *buf, int count)
{
if (write(wfd, buf, count) != count)
logPanic("Cannot 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 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 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 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);
}
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(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)alarm((unsigned)_grabTimeout);
(void)signal(SIGALRM, syncTimeout);
if (setjmp(syncJump)) {
logError("Display %s could not be secured\n", dname);
sleep(10);
exit(EX_RESERVER_DPY);
}
debug("Before XGrabServer %s\n", dname);
XGrabServer(dpy);
debug("XGrabServer succeeded %s\n", dname);
(void)alarm(0);
(void)signal(SIGALRM, SIG_DFL);
pseudoReset(dpy);
if (!_grabServer) {
XUngrabServer(dpy);
XSync(dpy, False);
}
debug("secureDisplay %s done\n", dname);
}
void
unsecureDisplay(Display *dpy)
{
debug("Unsecure display %s\n", dname);
if (_grabServer) {
XUngrabServer(dpy);
XSync(dpy, False);
}
}
#define GRABEVENTS \
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \
EnterWindowMask | LeaveWindowMask
void
secureKeyboard(Display *dpy)
{
(void)alarm((unsigned)_grabTimeout);
(void)signal(SIGALRM, syncTimeout);
if (setjmp(syncJump) ||
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);
exit(EX_RESERVER_DPY);
}
(void)alarm(0);
(void)signal(SIGALRM, SIG_DFL);
}
void
securePointer(Display *dpy)
{
(void)alarm((unsigned)_grabTimeout);
(void)signal(SIGALRM, syncTimeout);
if (setjmp(syncJump) ||
XGrabPointer(dpy, DefaultRootWindow(dpy), True, GRABEVENTS,
GrabModeAsync, GrabModeAsync,
None, None, CurrentTime) != GrabSuccess) {
(void)alarm(0);
(void)signal(SIGALRM, SIG_DFL);
logError("Pointer on display %s could not be secured\n", dname);
exit(EX_RESERVER_DPY);
}
(void)alarm(0);
(void)signal(SIGALRM, SIG_DFL);
}
void
secureInputs(Display *dpy)
{
debug("secureInputs %s\n", dname);
secureKeyboard(dpy);
securePointer(dpy);
XSetInputFocus(dpy, None, None, CurrentTime);
debug("secureInputs %s done\n", dname);
}
void
unsecureInputs(Display *dpy)
{
debug("unsecureInputs %s\n", dname);
XSetInputFocus(dpy, PointerRoot, PointerRoot, CurrentTime);
XUngrabKeyboard(dpy, CurrentTime);
XUngrabPointer(dpy, CurrentTime);
XSync(dpy, False);
}
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, False);
} else {
debug("Server dead\n");
(void)alarm(0);
(void)signal(SIGALRM, SIG_DFL);
XSetIOErrorHandler(oldError);
return False;
}
(void)alarm(0);
(void)signal(SIGALRM, oldSig);
(void)alarm(oldAlarm);
debug("Server alive\n");
XSetIOErrorHandler(oldError);
return True;
}
/*
* 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
xkbInit(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
xkbModifierMaskWorker(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 != 0 && strcmp(name, modStr) == 0) {
unsigned int mask;
XkbVirtualModsToReal(xkb, 1 << i, &mask);
return mask;
}
}
return 0;
}
static unsigned int
xkbModifierMask(Display *dpy, const char *name)
{
XkbDescPtr xkb;
if ((xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd))) {
unsigned int mask = xkbModifierMaskWorker(xkb, name);
XkbFreeKeyboard(xkb, 0, True);
return mask;
}
return 0;
}
static int
xkbGetModifierState(Display *dpy, const char *name)
{
unsigned int mask;
XkbStateRec state;
if (!(mask = xkbModifierMask(dpy, name)))
return 0;
XkbGetState(dpy, XkbUseCoreKbd, &state);
return (mask & state.locked_mods) != 0;
}
static int
xkbSetModifier(Display *dpy, const char *name, int sts)
{
unsigned int mask;
if (!(mask = xkbModifierMask(dpy, name)))
return False;
XkbLockModifiers(dpy, XkbUseCoreKbd, mask, sts ? mask : 0);
return True;
}
#endif /* HAVE_XKB */
#ifdef HAVE_XTEST
static int
xtestGetModifierState(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
xtestFakeKeypress(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
setupModifiers(Display *mdpy, int numlock)
{
if (numlock == 2)
return;
newnumstate = numlock;
nummodified = True;
dpy = mdpy;
#ifdef HAVE_XKB
if (xkbInit(mdpy)) {
havexkb = True;
oldnumstate = xkbGetModifierState(mdpy, "NumLock");
xkbSetModifier(mdpy, "NumLock", numlock);
return;
}
#endif
#ifdef HAVE_XTEST
oldnumstate = xtestGetModifierState(mdpy, XK_Num_Lock);
if (oldnumstate != numlock)
xtestFakeKeypress(mdpy, XK_Num_Lock);
#endif
}
void
restoreModifiers(void)
{
#ifdef HAVE_XTEST
int numstat;
#endif
if (!nummodified)
return;
#ifdef HAVE_XKB
if (havexkb) {
if (xkbGetModifierState(dpy, "NumLock") == newnumstate)
xkbSetModifier(dpy, "NumLock", oldnumstate);
return;
}
#endif
#ifdef HAVE_XTEST
numstat = xtestGetModifierState(dpy, XK_Num_Lock);
if (numstat == newnumstate && newnumstate != oldnumstate)
xtestFakeKeypress(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);
}
}