/* KDE Greeter module for xdm Copyright (C) 2001-2003 Oswald Buddenhagen 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 #include #include #include "kdm_greet.h" #include "kdmconfig.h" #include #include #include #include #include #include #include #include #include #ifdef _POSIX_PRIORITY_SCHEDULING # include #endif # include #if defined(HAVE_XTEST) || defined(HAVE_XKB) # include #endif #ifdef HAVE_XTEST # include #endif #ifdef HAVE_XKB #include #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 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 * XKB part: Copyright (C) 2001-2002 Oswald Buddenhagen * */ #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); } }