/* * xnlock -- Dan Heller, 1990 * "nlock" is a "new lockscreen" type program... something that prevents * screen burnout by making most of it "black" while providing something * of interest to be displayed in case anyone is watching. * "xnlock" is the X11 version of the program. * Original sunview version written by Dan Heller 1985 (not included here). */ #ifdef HAVE_CONFIG_H #include RCSID("$Id: xnlock.c,v 1.93.2.4 2004/09/08 09:16:00 joda Exp $"); #endif #include #include #include #include #include #include #include #include #include #ifdef strerror #undef strerror #endif #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_CRYPT_H #undef des_encrypt #define des_encrypt wingless_pigs_mostly_fail_to_fly #include #undef des_encrypt #endif #ifdef KRB5 #include #endif #ifdef KRB4 #include #endif #if defined(KRB4) || defined(KRB5) #include #endif #include #include static char login[16]; static char userprompt[128]; #ifdef KRB4 static char name[ANAME_SZ]; static char inst[INST_SZ]; static char realm[REALM_SZ]; #endif #ifdef KRB5 static krb5_context context; static krb5_principal client; #endif #define font_height(font) (font->ascent + font->descent) static char *SPACE_STRING = " "; static char STRING[] = "****************"; #define STRING_LENGTH (sizeof(STRING)) #define MAX_PASSWD_LENGTH 256 /* (sizeof(STRING)) */ #define PROMPT "Password: " #define FAIL_MSG "Sorry, try again" #define LEFT 001 #define RIGHT 002 #define DOWN 004 #define UP 010 #define FRONT 020 #define X_INCR 3 #define Y_INCR 2 #define XNLOCK_CTRL 1 #define XNLOCK_NOCTRL 0 static XtAppContext app; static Display *dpy; static unsigned short Width, Height; static Widget widget; static GC gc; static XtIntervalId timeout_id; static char *words; static int x, y; static Pixel Black, White; static XFontStruct *font; static char root_cpass[128]; static char user_cpass[128]; static int time_left, prompt_x, prompt_y, time_x, time_y; static unsigned long interval; static Pixmap left0, left1, right0, right1, left_front, right_front, front, down; #define MAXLINES 40 #define IS_MOVING 1 #define GET_PASSWD 2 static int state; /* indicates states: walking or getting passwd */ static int ALLOW_LOGOUT = (60*10); /* Allow logout after nn seconds */ #define LOGOUT_PASSWD "enuHDmTo5Lq4g" /* when given password "LOGOUT" */ static time_t locked_at; struct appres_t { Pixel bg; Pixel fg; XFontStruct *font; Boolean ignore_passwd; Boolean do_reverse; Boolean accept_root; char *text, *text_prog, *file, *logoutPasswd; Boolean no_screensaver; Boolean destroytickets; } appres; static XtResource resources[] = { { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel), XtOffsetOf(struct appres_t, bg), XtRString, "black" }, { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), XtOffsetOf(struct appres_t, fg), XtRString, "white" }, { XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *), XtOffsetOf(struct appres_t, font), XtRString, "-*-new century schoolbook-*-*-*-18-*" }, { "ignorePasswd", "IgnorePasswd", XtRBoolean, sizeof(Boolean), XtOffsetOf(struct appres_t,ignore_passwd),XtRImmediate,(XtPointer)False }, { "acceptRootPasswd", "AcceptRootPasswd", XtRBoolean, sizeof(Boolean), XtOffsetOf(struct appres_t, accept_root), XtRImmediate, (XtPointer)True }, { "text", "Text", XtRString, sizeof(String), XtOffsetOf(struct appres_t, text), XtRString, "I'm out running around." }, { "program", "Program", XtRString, sizeof(String), XtOffsetOf(struct appres_t, text_prog), XtRImmediate, NULL }, { "file", "File", XtRString, sizeof(String), XtOffsetOf(struct appres_t,file), XtRImmediate, NULL }, { "logoutPasswd", "logoutPasswd", XtRString, sizeof(String), XtOffsetOf(struct appres_t, logoutPasswd), XtRString, LOGOUT_PASSWD }, { "noScreenSaver", "NoScreenSaver", XtRBoolean, sizeof(Boolean), XtOffsetOf(struct appres_t,no_screensaver), XtRImmediate, (XtPointer)True }, { "destroyTickets", "DestroyTickets", XtRBoolean, sizeof(Boolean), XtOffsetOf(struct appres_t,destroytickets), XtRImmediate, (XtPointer)True }, }; static XrmOptionDescRec options[] = { { "-fg", ".foreground", XrmoptionSepArg, NULL }, { "-foreground", ".foreground", XrmoptionSepArg, NULL }, { "-fn", ".font", XrmoptionSepArg, NULL }, { "-font", ".font", XrmoptionSepArg, NULL }, { "-ip", ".ignorePasswd", XrmoptionNoArg, "True" }, { "-noip", ".ignorePasswd", XrmoptionNoArg, "False" }, { "-ar", ".acceptRootPasswd", XrmoptionNoArg, "True" }, { "-noar", ".acceptRootPasswd", XrmoptionNoArg, "False" }, { "-nonoscreensaver", ".noScreenSaver", XrmoptionNoArg, "False" }, { "-nodestroytickets", ".destroyTickets", XrmoptionNoArg, "False" }, }; static char* get_words(void) { FILE *pp = NULL; static char buf[512]; long n; if (appres.text_prog) { pp = popen(appres.text_prog, "r"); if (!pp) { warn("popen %s", appres.text_prog); return appres.text; } n = fread(buf, 1, sizeof(buf) - 1, pp); buf[n] = 0; pclose(pp); return buf; } if (appres.file) { pp = fopen(appres.file, "r"); if (!pp) { warn("fopen %s", appres.file); return appres.text; } n = fread(buf, 1, sizeof(buf) - 1, pp); buf[n] = 0; fclose(pp); return buf; } return appres.text; } static void usage(void) { fprintf(stderr, "usage: %s [options] [message]\n", getprogname()); fprintf(stderr, "-fg color foreground color\n"); fprintf(stderr, "-bg color background color\n"); fprintf(stderr, "-rv reverse foreground/background colors\n"); fprintf(stderr, "-nrv no reverse video\n"); fprintf(stderr, "-ip ignore passwd\n"); fprintf(stderr, "-nip don't ignore passwd\n"); fprintf(stderr, "-ar accept root's passwd to unlock\n"); fprintf(stderr, "-nar don't accept root's passwd\n"); fprintf(stderr, "-f [file] message is read from file or ~/.msgfile\n"); fprintf(stderr, "-prog program text is gotten from executing `program'\n"); fprintf(stderr, "-nodestroytickets keep kerberos tickets\n"); exit(1); } static void init_words (int argc, char **argv) { int i = 0; while(argv[i]) { if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "-prog") == 0) { i++; if(argv[i]) { appres.text_prog = argv[i]; i++; } else { warnx ("-p requires an argument"); usage(); } } else if(strcmp(argv[i], "-f") == 0) { i++; if(argv[i]) { appres.file = argv[i]; i++; } else { asprintf (&appres.file, "%s/.msgfile", getenv("HOME")); if (appres.file == NULL) errx (1, "cannot allocate memory for message"); } } else if(strcmp(argv[i], "--version") == 0) { print_version(NULL); exit(0); } else { int j; int len = 1; for(j = i; argv[j]; j++) len += strlen(argv[j]) + 1; appres.text = malloc(len); if (appres.text == NULL) errx (1, "cannot allocate memory for message"); appres.text[0] = 0; for(; i < j; i++){ strlcat(appres.text, argv[i], len); strlcat(appres.text, " ", len); } } } } static void ScreenSaver(int save) { static int timeout, interval, prefer_blank, allow_exp; if(!appres.no_screensaver){ if (save) { XGetScreenSaver(dpy, &timeout, &interval, &prefer_blank, &allow_exp); XSetScreenSaver(dpy, 0, interval, prefer_blank, allow_exp); } else /* restore state */ XSetScreenSaver(dpy, timeout, interval, prefer_blank, allow_exp); } } /* Forward decls necessary */ static void talk(int force_erase); static unsigned long look(void); static int zrefresh(void) { switch (fork()) { case -1: warn ("zrefresh: fork"); return -1; case 0: /* Child */ execlp("zrefresh", "zrefresh", 0); execl(BINDIR "/zrefresh", "zrefresh", 0); return -1; default: /* Parent */ break; } return 0; } static void leave(void) { XUngrabPointer(dpy, CurrentTime); XUngrabKeyboard(dpy, CurrentTime); ScreenSaver(0); XCloseDisplay(dpy); zrefresh(); exit(0); } static void walk(int dir) { int incr = 0; static int lastdir; static int up = 1; static Pixmap frame; XSetForeground(dpy, gc, White); XSetBackground(dpy, gc, Black); if (dir & (LEFT|RIGHT)) { /* left/right movement (mabye up/down too) */ up = -up; /* bouncing effect (even if hit a wall) */ if (dir & LEFT) { incr = X_INCR; frame = (up < 0) ? left0 : left1; } else { incr = -X_INCR; frame = (up < 0) ? right0 : right1; } if ((lastdir == FRONT || lastdir == DOWN) && dir & UP) { /* workaround silly bug that leaves screen dust when * guy is facing forward or down and moves up-left/right. */ XCopyPlane(dpy, frame, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L); XFlush(dpy); } /* note that maybe neither UP nor DOWN is set! */ if (dir & UP && y > Y_INCR) y -= Y_INCR; else if (dir & DOWN && y < (int)Height - 64) y += Y_INCR; } /* Explicit up/down movement only (no left/right) */ else if (dir == UP) XCopyPlane(dpy, front, XtWindow(widget), gc, 0,0, 64,64, x, y -= Y_INCR, 1L); else if (dir == DOWN) XCopyPlane(dpy, down, XtWindow(widget), gc, 0,0, 64,64, x, y += Y_INCR, 1L); else if (dir == FRONT && frame != front) { if (up > 0) up = -up; if (lastdir & LEFT) frame = left_front; else if (lastdir & RIGHT) frame = right_front; else frame = front; XCopyPlane(dpy, frame, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L); } if (dir & LEFT) while(--incr >= 0) { XCopyPlane(dpy, frame, XtWindow(widget), gc, 0,0, 64,64, --x, y+up, 1L); XFlush(dpy); } else if (dir & RIGHT) while(++incr <= 0) { XCopyPlane(dpy, frame, XtWindow(widget), gc, 0,0, 64,64, ++x, y+up, 1L); XFlush(dpy); } lastdir = dir; } static long my_random (void) { #ifdef HAVE_RANDOM return random(); #else return rand(); #endif } static int think(void) { if (my_random() & 1) walk(FRONT); if (my_random() & 1) { words = get_words(); return 1; } return 0; } static void move(XtPointer _p, XtIntervalId *_id) { static int length, dir; if (!length) { int tries = 0; dir = 0; if ((my_random() & 1) && think()) { talk(0); /* sets timeout to itself */ return; } if (!(my_random() % 3) && (interval = look())) { timeout_id = XtAppAddTimeOut(app, interval, move, NULL); return; } interval = 20 + my_random() % 100; do { if (!tries) length = Width/100 + my_random() % 90, tries = 8; else tries--; switch (my_random() % 8) { case 0: if (x - X_INCR*length >= 5) dir = LEFT; case 1: if (x + X_INCR*length <= (int)Width - 70) dir = RIGHT; case 2: if (y - (Y_INCR*length) >= 5) dir = UP, interval = 40; case 3: if (y + Y_INCR*length <= (int)Height - 70) dir = DOWN, interval = 20; case 4: if (x - X_INCR*length >= 5 && y - (Y_INCR*length) >= 5) dir = (LEFT|UP); case 5: if (x + X_INCR * length <= (int)Width - 70 && y-Y_INCR * length >= 5) dir = (RIGHT|UP); case 6: if (x - X_INCR * length >= 5 && y + Y_INCR * length <= (int)Height - 70) dir = (LEFT|DOWN); case 7: if (x + X_INCR*length <= (int)Width - 70 && y + Y_INCR*length <= (int)Height - 70) dir = (RIGHT|DOWN); } } while (!dir); } walk(dir); --length; timeout_id = XtAppAddTimeOut(app, interval, move, NULL); } static void post_prompt_box(Window window) { int width = (Width / 3); int height = font_height(font) * 6; int box_x, box_y; /* make sure the entire nose icon fits in the box */ if (height < 100) height = 100; if(width < 105 + font->max_bounds.width*STRING_LENGTH) width = 105 + font->max_bounds.width*STRING_LENGTH; box_x = (Width - width) / 2; time_x = prompt_x = box_x + 105; time_y = prompt_y = Height / 2; box_y = prompt_y - 3 * font_height(font); /* erase current guy -- text message may still exist */ XSetForeground(dpy, gc, Black); XFillRectangle(dpy, window, gc, x, y, 64, 64); talk(1); /* forcefully erase message if one is being displayed */ /* Clear area in middle of screen for prompt box */ XSetForeground(dpy, gc, White); XFillRectangle(dpy, window, gc, box_x, box_y, width, height); /* make a box that's 5 pixels thick. Then add a thin box inside it */ XSetForeground(dpy, gc, Black); XSetLineAttributes(dpy, gc, 5, 0, 0, 0); XDrawRectangle(dpy, window, gc, box_x+5, box_y+5, width-10, height-10); XSetLineAttributes(dpy, gc, 0, 0, 0, 0); XDrawRectangle(dpy, window, gc, box_x+12, box_y+12, width-23, height-23); XDrawString(dpy, window, gc, prompt_x, prompt_y-font_height(font), userprompt, strlen(userprompt)); XDrawString(dpy, window, gc, prompt_x, prompt_y, PROMPT, strlen(PROMPT)); /* set background for copyplane and DrawImageString; need reverse video */ XSetBackground(dpy, gc, White); XCopyPlane(dpy, right0, window, gc, 0,0, 64,64, box_x + 20, box_y + (height - 64)/2, 1L); prompt_x += XTextWidth(font, PROMPT, strlen(PROMPT)); time_y += 2*font_height(font); } static void RaiseWindow(Widget w, XEvent *ev, String *s, Cardinal *n) { Widget x; if(!XtIsRealized(w)) return; x = XtParent(w); XRaiseWindow(dpy, XtWindow(x)); } static void ClearWindow(Widget w, XEvent *_event, String *_s, Cardinal *_n) { XExposeEvent *event = (XExposeEvent *)_event; if (!XtIsRealized(w)) return; XClearArea(dpy, XtWindow(w), event->x, event->y, event->width, event->height, False); if (state == GET_PASSWD) post_prompt_box(XtWindow(w)); if (timeout_id == 0 && event->count == 0) { timeout_id = XtAppAddTimeOut(app, 1000L, move, NULL); /* first grab the input focus */ XSetInputFocus(dpy, XtWindow(w), RevertToPointerRoot, CurrentTime); /* now grab the pointer and keyboard and contrain to this window */ XGrabPointer(dpy, XtWindow(w), TRUE, 0, GrabModeAsync, GrabModeAsync, XtWindow(w), None, CurrentTime); } } static void countdown(XtPointer _t, XtIntervalId *_d) { int *timeout = (int *)_t; char buf[128]; time_t seconds; if (--(*timeout) < 0) { XExposeEvent event; XtRemoveTimeOut(timeout_id); state = IS_MOVING; event.x = event.y = 0; event.width = Width, event.height = Height; ClearWindow(widget, (XEvent *)&event, 0, 0); timeout_id = XtAppAddTimeOut(app, 200L, move, NULL); return; } seconds = time(0) - locked_at; if (seconds >= 3600) snprintf(buf, sizeof(buf), "Locked for %d:%02d:%02d ", (int)seconds/3600, (int)seconds/60%60, (int)seconds%60); else snprintf(buf, sizeof(buf), "Locked for %2d:%02d ", (int)seconds/60, (int)seconds%60); XDrawImageString(dpy, XtWindow(widget), gc, time_x, time_y, buf, strlen(buf)); XtAppAddTimeOut(app, 1000L, countdown, timeout); return; } #ifdef KRB5 static int verify_krb5(const char *password) { krb5_error_code ret; krb5_ccache id; krb5_boolean get_v4_tgt; krb5_cc_default(context, &id); ret = krb5_verify_user(context, client, id, password, 0, NULL); if (ret == 0){ #ifdef KRB4 krb5_appdefault_boolean(context, "xnlock", krb5_principal_get_realm(context, client), "krb4_get_tickets", FALSE, &get_v4_tgt); if(get_v4_tgt) { CREDENTIALS c; krb5_creds mcred, cred; krb5_make_principal(context, &mcred.server, client->realm, "krbtgt", client->realm, NULL); ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred); if(ret == 0) { ret = krb524_convert_creds_kdc_ccache(context, id, &cred, &c); if(ret == 0) tf_setup(&c, c.pname, c.pinst); memset(&c, 0, sizeof(c)); krb5_free_creds_contents(context, &cred); } krb5_free_principal(context, mcred.server); } #endif if (k_hasafs()) krb5_afslog(context, id, NULL, NULL); return 0; } if (ret != KRB5KRB_AP_ERR_MODIFIED) krb5_warn(context, ret, "verify_krb5"); return -1; } #endif static int verify(char *password) { /* * First try with root password, if allowed. */ if ( appres.accept_root && strcmp(crypt(password, root_cpass), root_cpass) == 0) return 0; /* * Password that log out user */ if (getuid() != 0 && geteuid() != 0 && (time(0) - locked_at) > ALLOW_LOGOUT && strcmp(crypt(password, appres.logoutPasswd), appres.logoutPasswd) == 0) { signal(SIGHUP, SIG_IGN); kill(-1, SIGHUP); sleep(5); /* If the X-server shut down then so will we, else * continue */ signal(SIGHUP, SIG_DFL); } /* * Try copy of users password. */ if (strcmp(crypt(password, user_cpass), user_cpass) == 0) return 0; /* * Try to verify as user in case password change. */ if (unix_verify_user(login, password) == 0) return 0; #ifdef KRB5 /* * Try to verify as user with kerberos 5. */ if(verify_krb5(password) == 0) return 0; #endif #ifdef KRB4 { int ret; /* * Try to verify as user with kerberos 4. */ ret = krb_verify_user(name, inst, realm, password, KRB_VERIFY_NOT_SECURE, NULL); if (ret == KSUCCESS){ if (k_hasafs()) krb_afslog(NULL, NULL); return 0; } if (ret != INTK_BADPW) warnx ("warning: %s", (ret < 0) ? strerror(ret) : krb_get_err_text(ret)); } #endif return -1; } static void GetPasswd(Widget w, XEvent *_event, String *_s, Cardinal *_n) { XKeyEvent *event = (XKeyEvent *)_event; static char passwd[MAX_PASSWD_LENGTH]; static int cnt; static int is_ctrl = XNLOCK_NOCTRL; char c; KeySym keysym; int echolen; int old_state = state; if (event->type == ButtonPress) { x = event->x, y = event->y; return; } if (state == IS_MOVING) { /* guy is running around--change to post prompt box. */ XtRemoveTimeOut(timeout_id); state = GET_PASSWD; if (appres.ignore_passwd || !strlen(user_cpass)) leave(); post_prompt_box(XtWindow(w)); cnt = 0; time_left = 30; countdown((XtPointer)&time_left, 0); } if (event->type == KeyRelease) { keysym = XLookupKeysym(event, 0); if (keysym == XK_Control_L || keysym == XK_Control_R) { is_ctrl = XNLOCK_NOCTRL; } } if (event->type != KeyPress) return; time_left = 30; keysym = XLookupKeysym(event, 0); if (keysym == XK_Control_L || keysym == XK_Control_R) { is_ctrl = XNLOCK_CTRL; return; } if (!XLookupString(event, &c, 1, &keysym, 0)) return; if (keysym == XK_Return || keysym == XK_Linefeed) { passwd[cnt] = 0; if(old_state == IS_MOVING) return; XtRemoveTimeOut(timeout_id); if(verify(passwd) == 0) leave(); cnt = 0; XDrawImageString(dpy, XtWindow(widget), gc, time_x, time_y, FAIL_MSG, strlen(FAIL_MSG)); time_left = 0; timeout_id = XtAppAddTimeOut(app, 2000L, countdown, &time_left); return; } if (keysym == XK_BackSpace || keysym == XK_Delete || keysym == XK_Left) { if (cnt) passwd[cnt--] = ' '; } else if (keysym == XK_u && is_ctrl == XNLOCK_CTRL) { while (cnt) { passwd[cnt--] = ' '; echolen = min(cnt, STRING_LENGTH); XDrawImageString(dpy, XtWindow(w), gc, prompt_x, prompt_y, STRING, echolen); XDrawImageString(dpy, XtWindow(w), gc, prompt_x + XTextWidth(font, STRING, echolen), prompt_y, SPACE_STRING, STRING_LENGTH - echolen + 1); } } else if (isprint((unsigned char)c)) { if ((cnt + 1) >= MAX_PASSWD_LENGTH) XBell(dpy, 50); else passwd[cnt++] = c; } else return; echolen = min(cnt, STRING_LENGTH); XDrawImageString(dpy, XtWindow(w), gc, prompt_x, prompt_y, STRING, echolen); XDrawImageString(dpy, XtWindow(w), gc, prompt_x + XTextWidth(font, STRING, echolen), prompt_y, SPACE_STRING, STRING_LENGTH - echolen +1); } #include "nose.0.left" #include "nose.1.left" #include "nose.0.right" #include "nose.1.right" #include "nose.left.front" #include "nose.right.front" #include "nose.front" #include "nose.down" static void init_images(void) { static Pixmap *images[] = { &left0, &left1, &right0, &right1, &left_front, &right_front, &front, &down }; static unsigned char *bits[] = { nose_0_left_bits, nose_1_left_bits, nose_0_right_bits, nose_1_right_bits, nose_left_front_bits, nose_right_front_bits, nose_front_bits, nose_down_bits }; int i; for (i = 0; i < XtNumber(images); i++) if (!(*images[i] = XCreatePixmapFromBitmapData(dpy, DefaultRootWindow(dpy), (char*)(bits[i]), 64, 64, 1, 0, 1))) XtError("Can't load nose images"); } static void talk(int force_erase) { int width = 0, height, Z, total = 0; static int X, Y, talking; static struct { int x, y, width, height; } s_rect; char *p, *p2; char buf[BUFSIZ], args[MAXLINES][256]; /* clear what we've written */ if (talking || force_erase) { if (!talking) return; if (talking == 2) { XSetForeground(dpy, gc, Black); XDrawString(dpy, XtWindow(widget), gc, X, Y, words, strlen(words)); } else if (talking == 1) { XSetForeground(dpy, gc, Black); XFillRectangle(dpy, XtWindow(widget), gc, s_rect.x-5, s_rect.y-5, s_rect.width+10, s_rect.height+10); } talking = 0; if (!force_erase) timeout_id = XtAppAddTimeOut(app, 40L, (XtTimerCallbackProc)move, NULL); return; } XSetForeground(dpy, gc, White); talking = 1; walk(FRONT); strlcpy (buf, words, sizeof(buf)); p = buf; /* possibly avoid a lot of work here * if no CR or only one, then just print the line */ if (!(p2 = strchr(p, '\n')) || !p2[1]) { int w; if (p2) *p2 = 0; w = XTextWidth(font, words, strlen(words)); X = x + 32 - w/2; Y = y - 5 - font_height(font); /* give us a nice 5 pixel margin */ if (X < 5) X = 5; else if (X + w + 15 > (int)Width + 5) X = Width - w - 5; if (Y < 5) Y = y + 64 + 5 + font_height(font); XDrawString(dpy, XtWindow(widget), gc, X, Y, words, strlen(words)); timeout_id = XtAppAddTimeOut(app, 5000L, (XtTimerCallbackProc)talk, NULL); talking++; return; } /* p2 now points to the first '\n' */ for (height = 0; p; height++) { int w; *p2 = 0; if ((w = XTextWidth(font, p, p2 - p)) > width) width = w; total += p2 - p; /* total chars; count to determine reading time */ strlcpy(args[height], p, sizeof(args[height])); if (height == MAXLINES - 1) { puts("Message too long!"); break; } p = p2+1; if (!(p2 = strchr(p, '\n'))) break; } height++; /* Figure out the height and width in pixels (height, width) extend * the new box by 15 pixels on the sides (30 total) top and bottom. */ s_rect.width = width + 30; s_rect.height = height * font_height(font) + 30; if (x - s_rect.width - 10 < 5) s_rect.x = 5; else if ((s_rect.x = x+32-(s_rect.width+15)/2) + s_rect.width+15 > (int)Width-5) s_rect.x = Width - 15 - s_rect.width; if (y - s_rect.height - 10 < 5) s_rect.y = y + 64 + 5; else s_rect.y = y - 5 - s_rect.height; XSetForeground(dpy, gc, White); XFillRectangle(dpy, XtWindow(widget), gc, s_rect.x-5, s_rect.y-5, s_rect.width+10, s_rect.height+10); /* make a box that's 5 pixels thick. Then add a thin box inside it */ XSetForeground(dpy, gc, Black); XSetLineAttributes(dpy, gc, 5, 0, 0, 0); XDrawRectangle(dpy, XtWindow(widget), gc, s_rect.x, s_rect.y, s_rect.width-1, s_rect.height-1); XSetLineAttributes(dpy, gc, 0, 0, 0, 0); XDrawRectangle(dpy, XtWindow(widget), gc, s_rect.x + 7, s_rect.y + 7, s_rect.width - 15, s_rect.height - 15); X = 15; Y = 15 + font_height(font); /* now print each string in reverse order (start at bottom of box) */ for (Z = 0; Z < height; Z++) { XDrawString(dpy, XtWindow(widget), gc, s_rect.x+X, s_rect.y+Y, args[Z], strlen(args[Z])); Y += font_height(font); } timeout_id = XtAppAddTimeOut(app, (total/15) * 1000, (XtTimerCallbackProc)talk, NULL); } static unsigned long look(void) { XSetForeground(dpy, gc, White); XSetBackground(dpy, gc, Black); if (my_random() % 3) { XCopyPlane(dpy, (my_random() & 1)? down : front, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L); return 1000L; } if (!(my_random() % 5)) return 0; if (my_random() % 3) { XCopyPlane(dpy, (my_random() & 1)? left_front : right_front, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L); return 1000L; } if (!(my_random() % 5)) return 0; XCopyPlane(dpy, (my_random() & 1)? left0 : right0, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L); return 1000L; } int main (int argc, char **argv) { int i; Widget override; XGCValues gcvalues; setprogname (argv[0]); /* * Must be setuid root to read /etc/shadow, copy encrypted * passwords here and then switch to sane uid. */ { struct passwd *pw; uid_t uid = getuid(); if (!(pw = k_getpwuid(0))) errx (1, "can't get root's passwd!"); strlcpy(root_cpass, pw->pw_passwd, sizeof(root_cpass)); if (!(pw = k_getpwuid(uid))) errx (1, "Can't get your password entry!"); strlcpy(user_cpass, pw->pw_passwd, sizeof(user_cpass)); setuid(uid); if (uid != 0 && setuid(0) != -1) { fprintf(stderr, "Failed to drop privileges!\n"); exit(1); } /* Now we're no longer running setuid root. */ strlcpy(login, pw->pw_name, sizeof(login)); } #if defined(HAVE_SRANDOMDEV) srandomdev(); #elif defined(HAVE_RANDOM) srandom(time(NULL)); #else srand (time(NULL)); #endif for (i = 0; i < STRING_LENGTH; i++) STRING[i] = ((unsigned long)my_random() % ('~' - ' ')) + ' '; locked_at = time(0); snprintf(userprompt, sizeof(userprompt), "User: %s", login); #ifdef KRB4 krb_get_default_principal(name, inst, realm); snprintf(userprompt, sizeof(userprompt), "User: %s", krb_unparse_name_long(name, inst, realm)); #endif #ifdef KRB5 { krb5_error_code ret; char *str; ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); krb5_get_default_principal(context, &client); krb5_unparse_name(context, client, &str); snprintf(userprompt, sizeof(userprompt), "User: %s", str); free(str); } #endif override = XtVaAppInitialize(&app, "XNlock", options, XtNumber(options), (Cardinal*)&argc, argv, NULL, XtNoverrideRedirect, True, NULL); XtVaGetApplicationResources(override,(XtPointer)&appres, resources,XtNumber(resources), NULL); /* the background is black and the little guy is white */ Black = appres.bg; White = appres.fg; if (appres.destroytickets) { #ifdef KRB4 int fd; dest_tkt(); /* Nuke old ticket file */ /* but keep a place holder */ fd = open (TKT_FILE, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd >= 0) close (fd); #endif } dpy = XtDisplay(override); if (dpy == 0) errx (1, "Error: Can't open display"); Width = DisplayWidth(dpy, DefaultScreen(dpy)) + 2; Height = DisplayHeight(dpy, DefaultScreen(dpy)) + 2; for(i = 0; i < ScreenCount(dpy); i++){ Widget shell, core; struct xxx{ Pixel bg; }res; XtResource Res[] = { { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel), XtOffsetOf(struct xxx, bg), XtRString, "black" } }; if(i == DefaultScreen(dpy)) continue; shell = XtVaAppCreateShell(NULL,NULL, applicationShellWidgetClass, dpy, XtNscreen, ScreenOfDisplay(dpy, i), XtNoverrideRedirect, True, XtNx, -1, XtNy, -1, NULL); XtVaGetApplicationResources(shell, (XtPointer)&res, Res, XtNumber(Res), NULL); core = XtVaCreateManagedWidget("_foo", widgetClass, shell, XtNwidth, DisplayWidth(dpy, i), XtNheight, DisplayHeight(dpy, i), XtNbackground, res.bg, NULL); XtRealizeWidget(shell); } widget = XtVaCreateManagedWidget("_foo", widgetClass, override, XtNwidth, Width, XtNheight, Height, XtNbackground, Black, NULL); init_words(--argc, ++argv); init_images(); gcvalues.foreground = Black; gcvalues.background = White; font = appres.font; gcvalues.font = font->fid; gcvalues.graphics_exposures = False; gc = XCreateGC(dpy, DefaultRootWindow(dpy), GCForeground | GCBackground | GCGraphicsExposures | GCFont, &gcvalues); x = Width / 2; y = Height / 2; srand (time(0)); state = IS_MOVING; { static XtActionsRec actions[] = { { "ClearWindow", ClearWindow }, { "GetPasswd", GetPasswd }, { "RaiseWindow", RaiseWindow }, }; XtAppAddActions(app, actions, XtNumber(actions)); XtOverrideTranslations(widget, XtParseTranslationTable( ": ClearWindow() \n" ": GetPasswd() \n" ": RaiseWindow() \n" ": GetPasswd() \n" ": GetPasswd()")); } XtRealizeWidget(override); if((i = XGrabPointer(dpy, XtWindow(widget), True, 0, GrabModeAsync, GrabModeAsync, XtWindow(widget), None, CurrentTime)) != 0) errx(1, "Failed to grab pointer (%d)", i); if((i = XGrabKeyboard(dpy, XtWindow(widget), True, GrabModeAsync, GrabModeAsync, CurrentTime)) != 0) errx(1, "Failed to grab keyboard (%d)", i); ScreenSaver(1); XtAppMainLoop(app); exit(0); }