2 * xnlock -- Dan Heller, 1990
3 * "nlock" is a "new lockscreen" type program... something that prevents
4 * screen burnout by making most of it "black" while providing something
5 * of interest to be displayed in case anyone is watching.
6 * "xnlock" is the X11 version of the program.
7 * Original sunview version written by Dan Heller 1985 (not included here).
18 #include <X11/StringDefs.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/keysym.h>
21 #include <X11/Shell.h>
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
31 #define des_encrypt wingless_pigs_mostly_fail_to_fly
42 #if defined(KRB4) || defined(KRB5)
49 static char login
[16];
50 static char userprompt
[128];
52 static char name
[ANAME_SZ
];
53 static char inst
[INST_SZ
];
54 static char realm
[REALM_SZ
];
57 static krb5_context context
;
58 static krb5_principal client
;
61 #define font_height(font) (font->ascent + font->descent)
63 static char *SPACE_STRING
= " ";
64 static char STRING
[] = "****************";
66 #define STRING_LENGTH (sizeof(STRING))
67 #define MAX_PASSWD_LENGTH 256
68 /* (sizeof(STRING)) */
70 #define PROMPT "Password: "
71 #define FAIL_MSG "Sorry, try again"
80 #define XNLOCK_NOCTRL 0
82 static XtAppContext app
;
84 static unsigned short Width
, Height
;
87 static XtIntervalId timeout_id
;
90 static Pixel Black
, White
;
91 static XFontStruct
*font
;
92 static char root_cpass
[128];
93 static char user_cpass
[128];
94 static int time_left
, prompt_x
, prompt_y
, time_x
, time_y
;
95 static unsigned long interval
;
96 static Pixmap left0
, left1
, right0
, right1
, left_front
,
97 right_front
, front
, down
;
103 static int state
; /* indicates states: walking or getting passwd */
105 static int ALLOW_LOGOUT
= (60*10); /* Allow logout after nn seconds */
106 #define LOGOUT_PASSWD "enuHDmTo5Lq4g" /* when given password "LOGOUT" */
107 static time_t locked_at
;
113 Boolean ignore_passwd
;
116 char *text
, *text_prog
, *file
, *logoutPasswd
;
117 Boolean no_screensaver
;
118 Boolean destroytickets
;
121 static XtResource resources
[] = {
122 { XtNbackground
, XtCBackground
, XtRPixel
, sizeof(Pixel
),
123 XtOffsetOf(struct appres_t
, bg
), XtRString
, "black" },
125 { XtNforeground
, XtCForeground
, XtRPixel
, sizeof(Pixel
),
126 XtOffsetOf(struct appres_t
, fg
), XtRString
, "white" },
128 { XtNfont
, XtCFont
, XtRFontStruct
, sizeof (XFontStruct
*),
129 XtOffsetOf(struct appres_t
, font
),
130 XtRString
, "-*-new century schoolbook-*-*-*-18-*" },
132 { "ignorePasswd", "IgnorePasswd", XtRBoolean
, sizeof(Boolean
),
133 XtOffsetOf(struct appres_t
,ignore_passwd
),XtRImmediate
,(XtPointer
)False
},
135 { "acceptRootPasswd", "AcceptRootPasswd", XtRBoolean
, sizeof(Boolean
),
136 XtOffsetOf(struct appres_t
, accept_root
), XtRImmediate
, (XtPointer
)True
},
138 { "text", "Text", XtRString
, sizeof(String
),
139 XtOffsetOf(struct appres_t
, text
), XtRString
, "I'm out running around." },
141 { "program", "Program", XtRString
, sizeof(String
),
142 XtOffsetOf(struct appres_t
, text_prog
), XtRImmediate
, NULL
},
144 { "file", "File", XtRString
, sizeof(String
),
145 XtOffsetOf(struct appres_t
,file
), XtRImmediate
, NULL
},
147 { "logoutPasswd", "logoutPasswd", XtRString
, sizeof(String
),
148 XtOffsetOf(struct appres_t
, logoutPasswd
), XtRString
, LOGOUT_PASSWD
},
150 { "noScreenSaver", "NoScreenSaver", XtRBoolean
, sizeof(Boolean
),
151 XtOffsetOf(struct appres_t
,no_screensaver
), XtRImmediate
, (XtPointer
)True
},
153 { "destroyTickets", "DestroyTickets", XtRBoolean
, sizeof(Boolean
),
154 XtOffsetOf(struct appres_t
,destroytickets
), XtRImmediate
, (XtPointer
)True
},
157 static XrmOptionDescRec options
[] = {
158 { "-fg", ".foreground", XrmoptionSepArg
, NULL
},
159 { "-foreground", ".foreground", XrmoptionSepArg
, NULL
},
160 { "-fn", ".font", XrmoptionSepArg
, NULL
},
161 { "-font", ".font", XrmoptionSepArg
, NULL
},
162 { "-ip", ".ignorePasswd", XrmoptionNoArg
, "True" },
163 { "-noip", ".ignorePasswd", XrmoptionNoArg
, "False" },
164 { "-ar", ".acceptRootPasswd", XrmoptionNoArg
, "True" },
165 { "-noar", ".acceptRootPasswd", XrmoptionNoArg
, "False" },
166 { "-nonoscreensaver", ".noScreenSaver", XrmoptionNoArg
, "False" },
167 { "-nodestroytickets", ".destroyTickets", XrmoptionNoArg
, "False" },
174 static char buf
[512];
177 if (appres
.text_prog
) {
178 pp
= popen(appres
.text_prog
, "r");
180 warn("popen %s", appres
.text_prog
);
183 n
= fread(buf
, 1, sizeof(buf
) - 1, pp
);
189 pp
= fopen(appres
.file
, "r");
191 warn("fopen %s", appres
.file
);
194 n
= fread(buf
, 1, sizeof(buf
) - 1, pp
);
206 fprintf(stderr
, "usage: %s [options] [message]\n", getprogname());
207 fprintf(stderr
, "-fg color foreground color\n");
208 fprintf(stderr
, "-bg color background color\n");
209 fprintf(stderr
, "-rv reverse foreground/background colors\n");
210 fprintf(stderr
, "-nrv no reverse video\n");
211 fprintf(stderr
, "-ip ignore passwd\n");
212 fprintf(stderr
, "-nip don't ignore passwd\n");
213 fprintf(stderr
, "-ar accept root's passwd to unlock\n");
214 fprintf(stderr
, "-nar don't accept root's passwd\n");
215 fprintf(stderr
, "-f [file] message is read from file or ~/.msgfile\n");
216 fprintf(stderr
, "-prog program text is gotten from executing `program'\n");
217 fprintf(stderr
, "-nodestroytickets keep kerberos tickets\n");
218 fprintf(stderr
, "--version\n");
219 fprintf(stderr
, "--help\n");
224 init_words (int argc
, char **argv
)
229 if(strcmp(argv
[i
], "-p") == 0
230 || strcmp(argv
[i
], "-prog") == 0) {
233 appres
.text_prog
= argv
[i
];
236 warnx ("-p requires an argument");
239 } else if(strcmp(argv
[i
], "-f") == 0) {
242 appres
.file
= argv
[i
];
246 ret
= asprintf (&appres
.file
,
247 "%s/.msgfile", getenv("HOME"));
249 errx (1, "cannot allocate memory for message");
251 } else if(strcmp(argv
[i
], "--version") == 0) {
254 } else if(strcmp(argv
[i
], "--help") == 0) {
259 for(j
= i
; argv
[j
]; j
++)
260 len
+= strlen(argv
[j
]) + 1;
261 appres
.text
= malloc(len
);
262 if (appres
.text
== NULL
)
263 errx (1, "cannot allocate memory for message");
266 strlcat(appres
.text
, argv
[i
], len
);
267 strlcat(appres
.text
, " ", len
);
274 ScreenSaver(int save
)
276 static int timeout
, interval
, prefer_blank
, allow_exp
;
277 if(!appres
.no_screensaver
){
279 XGetScreenSaver(dpy
, &timeout
, &interval
,
280 &prefer_blank
, &allow_exp
);
281 XSetScreenSaver(dpy
, 0, interval
, prefer_blank
, allow_exp
);
284 XSetScreenSaver(dpy
, timeout
, interval
, prefer_blank
, allow_exp
);
288 /* Forward decls necessary */
289 static void talk(int force_erase
);
290 static unsigned long look(void);
297 warn ("zrefresh: fork");
301 execlp("zrefresh", "zrefresh", NULL
);
302 execl(BINDIR
"/zrefresh", "zrefresh", NULL
);
314 XUngrabPointer(dpy
, CurrentTime
);
315 XUngrabKeyboard(dpy
, CurrentTime
);
330 XSetForeground(dpy
, gc
, White
);
331 XSetBackground(dpy
, gc
, Black
);
332 if (dir
& (LEFT
|RIGHT
)) { /* left/right movement (mabye up/down too) */
333 up
= -up
; /* bouncing effect (even if hit a wall) */
336 frame
= (up
< 0) ? left0
: left1
;
339 frame
= (up
< 0) ? right0
: right1
;
341 if ((lastdir
== FRONT
|| lastdir
== DOWN
) && dir
& UP
) {
342 /* workaround silly bug that leaves screen dust when
343 * guy is facing forward or down and moves up-left/right.
345 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
, 0, 0, 64,64, x
, y
, 1L);
348 /* note that maybe neither UP nor DOWN is set! */
349 if (dir
& UP
&& y
> Y_INCR
)
351 else if (dir
& DOWN
&& y
< (int)Height
- 64)
354 /* Explicit up/down movement only (no left/right) */
356 XCopyPlane(dpy
, front
, XtWindow(widget
), gc
,
357 0,0, 64,64, x
, y
-= Y_INCR
, 1L);
358 else if (dir
== DOWN
)
359 XCopyPlane(dpy
, down
, XtWindow(widget
), gc
,
360 0,0, 64,64, x
, y
+= Y_INCR
, 1L);
361 else if (dir
== FRONT
&& frame
!= front
) {
366 else if (lastdir
& RIGHT
)
370 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
, 0, 0, 64,64, x
, y
, 1L);
374 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
,
375 0,0, 64,64, --x
, y
+up
, 1L);
378 else if (dir
& RIGHT
)
380 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
,
381 0,0, 64,64, ++x
, y
+up
, 1L);
402 if (my_random() & 1) {
410 move(XtPointer _p
, XtIntervalId
*_id
)
412 static int length
, dir
;
417 if ((my_random() & 1) && think()) {
418 talk(0); /* sets timeout to itself */
421 if (!(my_random() % 3) && (interval
= look())) {
422 timeout_id
= XtAppAddTimeOut(app
, interval
, move
, NULL
);
425 interval
= 20 + my_random() % 100;
428 length
= Width
/100 + my_random() % 90, tries
= 8;
431 switch (my_random() % 8) {
433 if (x
- X_INCR
*length
>= 5)
436 if (x
+ X_INCR
*length
<= (int)Width
- 70)
439 if (y
- (Y_INCR
*length
) >= 5)
440 dir
= UP
, interval
= 40;
442 if (y
+ Y_INCR
*length
<= (int)Height
- 70)
443 dir
= DOWN
, interval
= 20;
445 if (x
- X_INCR
*length
>= 5 && y
- (Y_INCR
*length
) >= 5)
448 if (x
+ X_INCR
* length
<= (int)Width
- 70 &&
449 y
-Y_INCR
* length
>= 5)
452 if (x
- X_INCR
* length
>= 5 &&
453 y
+ Y_INCR
* length
<= (int)Height
- 70)
456 if (x
+ X_INCR
*length
<= (int)Width
- 70 &&
457 y
+ Y_INCR
*length
<= (int)Height
- 70)
464 timeout_id
= XtAppAddTimeOut(app
, interval
, move
, NULL
);
468 post_prompt_box(Window window
)
470 int width
= (Width
/ 3);
471 int height
= font_height(font
) * 6;
474 /* make sure the entire nose icon fits in the box */
478 if(width
< 105 + font
->max_bounds
.width
*STRING_LENGTH
)
479 width
= 105 + font
->max_bounds
.width
*STRING_LENGTH
;
480 box_x
= (Width
- width
) / 2;
481 time_x
= prompt_x
= box_x
+ 105;
483 time_y
= prompt_y
= Height
/ 2;
484 box_y
= prompt_y
- 3 * font_height(font
);
486 /* erase current guy -- text message may still exist */
487 XSetForeground(dpy
, gc
, Black
);
488 XFillRectangle(dpy
, window
, gc
, x
, y
, 64, 64);
489 talk(1); /* forcefully erase message if one is being displayed */
490 /* Clear area in middle of screen for prompt box */
491 XSetForeground(dpy
, gc
, White
);
492 XFillRectangle(dpy
, window
, gc
, box_x
, box_y
, width
, height
);
494 /* make a box that's 5 pixels thick. Then add a thin box inside it */
495 XSetForeground(dpy
, gc
, Black
);
496 XSetLineAttributes(dpy
, gc
, 5, 0, 0, 0);
497 XDrawRectangle(dpy
, window
, gc
, box_x
+5, box_y
+5, width
-10, height
-10);
498 XSetLineAttributes(dpy
, gc
, 0, 0, 0, 0);
499 XDrawRectangle(dpy
, window
, gc
, box_x
+12, box_y
+12, width
-23, height
-23);
501 XDrawString(dpy
, window
, gc
,
502 prompt_x
, prompt_y
-font_height(font
),
503 userprompt
, strlen(userprompt
));
504 XDrawString(dpy
, window
, gc
, prompt_x
, prompt_y
, PROMPT
, strlen(PROMPT
));
505 /* set background for copyplane and DrawImageString; need reverse video */
506 XSetBackground(dpy
, gc
, White
);
507 XCopyPlane(dpy
, right0
, window
, gc
, 0,0, 64,64,
508 box_x
+ 20, box_y
+ (height
- 64)/2, 1L);
509 prompt_x
+= XTextWidth(font
, PROMPT
, strlen(PROMPT
));
510 time_y
+= 2*font_height(font
);
514 RaiseWindow(Widget w
, XEvent
*ev
, String
*s
, Cardinal
*n
)
520 XRaiseWindow(dpy
, XtWindow(x
));
525 ClearWindow(Widget w
, XEvent
*_event
, String
*_s
, Cardinal
*_n
)
527 XExposeEvent
*event
= (XExposeEvent
*)_event
;
528 if (!XtIsRealized(w
))
530 XClearArea(dpy
, XtWindow(w
), event
->x
, event
->y
,
531 event
->width
, event
->height
, False
);
532 if (state
== GET_PASSWD
)
533 post_prompt_box(XtWindow(w
));
534 if (timeout_id
== 0 && event
->count
== 0) {
535 timeout_id
= XtAppAddTimeOut(app
, 1000L, move
, NULL
);
536 /* first grab the input focus */
537 XSetInputFocus(dpy
, XtWindow(w
), RevertToPointerRoot
, CurrentTime
);
538 /* now grab the pointer and keyboard and contrain to this window */
539 XGrabPointer(dpy
, XtWindow(w
), TRUE
, 0, GrabModeAsync
,
540 GrabModeAsync
, XtWindow(w
), None
, CurrentTime
);
545 countdown(XtPointer _t
, XtIntervalId
*_d
)
547 int *timeout
= (int *)_t
;
551 if (--(*timeout
) < 0) {
553 XtRemoveTimeOut(timeout_id
);
555 event
.x
= event
.y
= 0;
556 event
.width
= Width
, event
.height
= Height
;
557 ClearWindow(widget
, (XEvent
*)&event
, 0, 0);
558 timeout_id
= XtAppAddTimeOut(app
, 200L, move
, NULL
);
561 seconds
= time(0) - locked_at
;
563 snprintf(buf
, sizeof(buf
),
564 "Locked for %d:%02d:%02d ",
565 (int)seconds
/3600, (int)seconds
/60%60, (int)seconds
%60);
567 snprintf(buf
, sizeof(buf
),
568 "Locked for %2d:%02d ",
569 (int)seconds
/60, (int)seconds
%60);
571 XDrawImageString(dpy
, XtWindow(widget
), gc
,
572 time_x
, time_y
, buf
, strlen(buf
));
573 XtAppAddTimeOut(app
, 1000L, countdown
, timeout
);
579 verify_krb5(const char *password
)
584 krb5_boolean get_v4_tgt
;
587 krb5_cc_default(context
, &id
);
588 ret
= krb5_verify_user(context
,
596 krb5_appdefault_boolean(context
, "xnlock",
597 krb5_principal_get_realm(context
, client
),
598 "krb4_get_tickets", FALSE
, &get_v4_tgt
);
601 krb5_creds mcred
, cred
;
603 krb5_cc_clear_mcred(&mcred
);
605 krb5_make_principal(context
, &mcred
.server
,
610 mcred
.client
= client
;
612 ret
= krb5_cc_retrieve_cred(context
, id
, 0, &mcred
, &cred
);
614 ret
= krb524_convert_creds_kdc_ccache(context
, id
, &cred
, &c
);
616 tf_setup(&c
, c
.pname
, c
.pinst
);
617 memset(&c
, 0, sizeof(c
));
618 krb5_free_cred_contents(context
, &cred
);
620 krb5_free_principal(context
, mcred
.server
);
624 krb5_afslog(context
, id
, NULL
, NULL
);
627 if (ret
!= KRB5KRB_AP_ERR_MODIFIED
)
628 krb5_warn(context
, ret
, "verify_krb5");
635 verify(char *password
)
638 * First try with root password, if allowed.
640 if ( appres
.accept_root
641 && strcmp(crypt(password
, root_cpass
), root_cpass
) == 0)
645 * Password that log out user
649 (time(0) - locked_at
) > ALLOW_LOGOUT
&&
650 strcmp(crypt(password
, appres
.logoutPasswd
), appres
.logoutPasswd
) == 0)
652 signal(SIGHUP
, SIG_IGN
);
655 /* If the X-server shut down then so will we, else
657 signal(SIGHUP
, SIG_DFL
);
661 * Try copy of users password.
663 if (strcmp(crypt(password
, user_cpass
), user_cpass
) == 0)
667 * Try to verify as user in case password change.
669 if (unix_verify_user(login
, password
) == 0)
674 * Try to verify as user with kerberos 5.
676 if(verify_krb5(password
) == 0)
684 * Try to verify as user with kerberos 4.
686 ret
= krb_verify_user(name
, inst
, realm
, password
,
687 KRB_VERIFY_NOT_SECURE
, NULL
);
688 if (ret
== KSUCCESS
){
690 krb_afslog(NULL
, NULL
);
693 if (ret
!= INTK_BADPW
)
694 warnx ("warning: %s",
695 (ret
< 0) ? strerror(ret
) : krb_get_err_text(ret
));
704 GetPasswd(Widget w
, XEvent
*_event
, String
*_s
, Cardinal
*_n
)
706 XKeyEvent
*event
= (XKeyEvent
*)_event
;
707 static char passwd
[MAX_PASSWD_LENGTH
];
709 static int is_ctrl
= XNLOCK_NOCTRL
;
713 int old_state
= state
;
715 if (event
->type
== ButtonPress
) {
716 x
= event
->x
, y
= event
->y
;
719 if (state
== IS_MOVING
) {
720 /* guy is running around--change to post prompt box. */
721 XtRemoveTimeOut(timeout_id
);
723 if (appres
.ignore_passwd
|| !strlen(user_cpass
))
725 post_prompt_box(XtWindow(w
));
728 countdown((XtPointer
)&time_left
, 0);
730 if (event
->type
== KeyRelease
) {
731 keysym
= XLookupKeysym(event
, 0);
732 if (keysym
== XK_Control_L
|| keysym
== XK_Control_R
) {
733 is_ctrl
= XNLOCK_NOCTRL
;
736 if (event
->type
!= KeyPress
)
741 keysym
= XLookupKeysym(event
, 0);
742 if (keysym
== XK_Control_L
|| keysym
== XK_Control_R
) {
743 is_ctrl
= XNLOCK_CTRL
;
746 if (!XLookupString(event
, &c
, 1, &keysym
, 0))
748 if (keysym
== XK_Return
|| keysym
== XK_Linefeed
) {
750 if(old_state
== IS_MOVING
)
752 XtRemoveTimeOut(timeout_id
);
754 if(verify(passwd
) == 0)
759 XDrawImageString(dpy
, XtWindow(widget
), gc
,
760 time_x
, time_y
, FAIL_MSG
, strlen(FAIL_MSG
));
762 timeout_id
= XtAppAddTimeOut(app
, 2000L, countdown
, &time_left
);
765 if (keysym
== XK_BackSpace
|| keysym
== XK_Delete
|| keysym
== XK_Left
) {
768 } else if (keysym
== XK_u
&& is_ctrl
== XNLOCK_CTRL
) {
771 echolen
= min(cnt
, STRING_LENGTH
);
772 XDrawImageString(dpy
, XtWindow(w
), gc
,
773 prompt_x
, prompt_y
, STRING
, echolen
);
774 XDrawImageString(dpy
, XtWindow(w
), gc
,
775 prompt_x
+ XTextWidth(font
, STRING
, echolen
),
776 prompt_y
, SPACE_STRING
, STRING_LENGTH
- echolen
+ 1);
778 } else if (isprint((unsigned char)c
)) {
779 if ((cnt
+ 1) >= MAX_PASSWD_LENGTH
)
785 echolen
= min(cnt
, STRING_LENGTH
);
786 XDrawImageString(dpy
, XtWindow(w
), gc
,
787 prompt_x
, prompt_y
, STRING
, echolen
);
788 XDrawImageString(dpy
, XtWindow(w
), gc
,
789 prompt_x
+ XTextWidth(font
, STRING
, echolen
),
790 prompt_y
, SPACE_STRING
, STRING_LENGTH
- echolen
+1);
793 #include "nose.0.left"
794 #include "nose.1.left"
795 #include "nose.0.right"
796 #include "nose.1.right"
797 #include "nose.left.front"
798 #include "nose.right.front"
799 #include "nose.front"
805 static Pixmap
*images
[] = {
806 &left0
, &left1
, &right0
, &right1
,
807 &left_front
, &right_front
, &front
, &down
809 static unsigned char *bits
[] = {
810 nose_0_left_bits
, nose_1_left_bits
, nose_0_right_bits
,
811 nose_1_right_bits
, nose_left_front_bits
, nose_right_front_bits
,
812 nose_front_bits
, nose_down_bits
816 for (i
= 0; i
< XtNumber(images
); i
++)
818 XCreatePixmapFromBitmapData(dpy
, DefaultRootWindow(dpy
),
819 (char*)(bits
[i
]), 64, 64, 1, 0, 1)))
820 XtError("Can't load nose images");
824 talk(int force_erase
)
826 int width
= 0, height
, Z
, total
= 0;
827 static int X
, Y
, talking
;
828 static struct { int x
, y
, width
, height
; } s_rect
;
830 char buf
[BUFSIZ
], args
[MAXLINES
][256];
832 /* clear what we've written */
833 if (talking
|| force_erase
) {
837 XSetForeground(dpy
, gc
, Black
);
838 XDrawString(dpy
, XtWindow(widget
), gc
, X
, Y
, words
, strlen(words
));
839 } else if (talking
== 1) {
840 XSetForeground(dpy
, gc
, Black
);
841 XFillRectangle(dpy
, XtWindow(widget
), gc
, s_rect
.x
-5, s_rect
.y
-5,
842 s_rect
.width
+10, s_rect
.height
+10);
846 timeout_id
= XtAppAddTimeOut(app
, 40L,
847 (XtTimerCallbackProc
)move
,
851 XSetForeground(dpy
, gc
, White
);
854 strlcpy (buf
, words
, sizeof(buf
));
857 /* possibly avoid a lot of work here
858 * if no CR or only one, then just print the line
860 if (!(p2
= strchr(p
, '\n')) || !p2
[1]) {
865 w
= XTextWidth(font
, words
, strlen(words
));
867 Y
= y
- 5 - font_height(font
);
868 /* give us a nice 5 pixel margin */
871 else if (X
+ w
+ 15 > (int)Width
+ 5)
874 Y
= y
+ 64 + 5 + font_height(font
);
875 XDrawString(dpy
, XtWindow(widget
), gc
, X
, Y
, words
, strlen(words
));
876 timeout_id
= XtAppAddTimeOut(app
, 5000L, (XtTimerCallbackProc
)talk
,
882 /* p2 now points to the first '\n' */
883 for (height
= 0; p
; height
++) {
886 if ((w
= XTextWidth(font
, p
, p2
- p
)) > width
)
888 total
+= p2
- p
; /* total chars; count to determine reading time */
889 strlcpy(args
[height
], p
, sizeof(args
[height
]));
890 if (height
== MAXLINES
- 1) {
891 puts("Message too long!");
895 if (!(p2
= strchr(p
, '\n')))
900 /* Figure out the height and width in pixels (height, width) extend
901 * the new box by 15 pixels on the sides (30 total) top and bottom.
903 s_rect
.width
= width
+ 30;
904 s_rect
.height
= height
* font_height(font
) + 30;
905 if (x
- s_rect
.width
- 10 < 5)
908 if ((s_rect
.x
= x
+32-(s_rect
.width
+15)/2)
909 + s_rect
.width
+15 > (int)Width
-5)
910 s_rect
.x
= Width
- 15 - s_rect
.width
;
911 if (y
- s_rect
.height
- 10 < 5)
912 s_rect
.y
= y
+ 64 + 5;
914 s_rect
.y
= y
- 5 - s_rect
.height
;
916 XSetForeground(dpy
, gc
, White
);
917 XFillRectangle(dpy
, XtWindow(widget
), gc
,
918 s_rect
.x
-5, s_rect
.y
-5, s_rect
.width
+10, s_rect
.height
+10);
920 /* make a box that's 5 pixels thick. Then add a thin box inside it */
921 XSetForeground(dpy
, gc
, Black
);
922 XSetLineAttributes(dpy
, gc
, 5, 0, 0, 0);
923 XDrawRectangle(dpy
, XtWindow(widget
), gc
,
924 s_rect
.x
, s_rect
.y
, s_rect
.width
-1, s_rect
.height
-1);
925 XSetLineAttributes(dpy
, gc
, 0, 0, 0, 0);
926 XDrawRectangle(dpy
, XtWindow(widget
), gc
,
927 s_rect
.x
+ 7, s_rect
.y
+ 7, s_rect
.width
- 15,
931 Y
= 15 + font_height(font
);
933 /* now print each string in reverse order (start at bottom of box) */
934 for (Z
= 0; Z
< height
; Z
++) {
935 XDrawString(dpy
, XtWindow(widget
), gc
, s_rect
.x
+X
, s_rect
.y
+Y
,
936 args
[Z
], strlen(args
[Z
]));
937 Y
+= font_height(font
);
939 timeout_id
= XtAppAddTimeOut(app
, (total
/15) * 1000,
940 (XtTimerCallbackProc
)talk
, NULL
);
946 XSetForeground(dpy
, gc
, White
);
947 XSetBackground(dpy
, gc
, Black
);
948 if (my_random() % 3) {
949 XCopyPlane(dpy
, (my_random() & 1)? down
: front
, XtWindow(widget
), gc
,
950 0, 0, 64,64, x
, y
, 1L);
953 if (!(my_random() % 5))
955 if (my_random() % 3) {
956 XCopyPlane(dpy
, (my_random() & 1)? left_front
: right_front
,
957 XtWindow(widget
), gc
, 0, 0, 64,64, x
, y
, 1L);
960 if (!(my_random() % 5))
962 XCopyPlane(dpy
, (my_random() & 1)? left0
: right0
, XtWindow(widget
), gc
,
963 0, 0, 64,64, x
, y
, 1L);
968 main (int argc
, char **argv
)
974 setprogname (argv
[0]);
977 * Must be setuid root to read /etc/shadow, copy encrypted
978 * passwords here and then switch to sane uid.
982 uid_t uid
= getuid();
983 if (!(pw
= k_getpwuid(0)))
984 errx (1, "can't get root's passwd!");
985 strlcpy(root_cpass
, pw
->pw_passwd
, sizeof(root_cpass
));
987 if (!(pw
= k_getpwuid(uid
)))
988 errx (1, "Can't get your password entry!");
989 strlcpy(user_cpass
, pw
->pw_passwd
, sizeof(user_cpass
));
991 if (uid
!= 0 && setuid(0) != -1) {
992 fprintf(stderr
, "Failed to drop privileges!\n");
995 /* Now we're no longer running setuid root. */
996 strlcpy(login
, pw
->pw_name
, sizeof(login
));
999 #if defined(HAVE_SRANDOMDEV)
1001 #elif defined(HAVE_RANDOM)
1002 srandom(time(NULL
));
1006 for (i
= 0; i
< STRING_LENGTH
; i
++)
1007 STRING
[i
] = ((unsigned long)my_random() % ('~' - ' ')) + ' ';
1009 locked_at
= time(0);
1011 snprintf(userprompt
, sizeof(userprompt
), "User: %s", login
);
1013 krb_get_default_principal(name
, inst
, realm
);
1014 snprintf(userprompt
, sizeof(userprompt
), "User: %s",
1015 krb_unparse_name_long(name
, inst
, realm
));
1019 krb5_error_code ret
;
1022 ret
= krb5_init_context(&context
);
1024 errx (1, "krb5_init_context failed: %d", ret
);
1025 krb5_get_default_principal(context
, &client
);
1026 krb5_unparse_name(context
, client
, &str
);
1027 snprintf(userprompt
, sizeof(userprompt
), "User: %s", str
);
1032 override
= XtVaAppInitialize(&app
, "XNlock", options
, XtNumber(options
),
1034 XtNoverrideRedirect
, True
,
1037 XtVaGetApplicationResources(override
,(XtPointer
)&appres
,
1038 resources
,XtNumber(resources
),
1040 /* the background is black and the little guy is white */
1044 if (appres
.destroytickets
) {
1048 dest_tkt(); /* Nuke old ticket file */
1049 /* but keep a place holder */
1050 fd
= open (TKT_FILE
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
1056 dpy
= XtDisplay(override
);
1059 errx (1, "Error: Can't open display");
1061 Width
= DisplayWidth(dpy
, DefaultScreen(dpy
)) + 2;
1062 Height
= DisplayHeight(dpy
, DefaultScreen(dpy
)) + 2;
1064 for(i
= 0; i
< ScreenCount(dpy
); i
++){
1071 XtResource Res
[] = {
1072 { XtNbackground
, XtCBackground
, XtRPixel
, sizeof(Pixel
),
1073 XtOffsetOf(struct xxx
, bg
), XtRString
, "black" }
1076 if(i
== DefaultScreen(dpy
))
1079 shell
= XtVaAppCreateShell(NULL
,NULL
, applicationShellWidgetClass
, dpy
,
1080 XtNscreen
, ScreenOfDisplay(dpy
, i
),
1081 XtNoverrideRedirect
, True
,
1086 XtVaGetApplicationResources(shell
, (XtPointer
)&res
,
1090 core
= XtVaCreateManagedWidget("_foo", widgetClass
, shell
,
1091 XtNwidth
, DisplayWidth(dpy
, i
),
1092 XtNheight
, DisplayHeight(dpy
, i
),
1093 XtNbackground
, res
.bg
,
1095 XtRealizeWidget(shell
);
1098 widget
= XtVaCreateManagedWidget("_foo", widgetClass
, override
,
1101 XtNbackground
, Black
,
1104 init_words(--argc
, ++argv
);
1107 gcvalues
.foreground
= Black
;
1108 gcvalues
.background
= White
;
1112 gcvalues
.font
= font
->fid
;
1113 gcvalues
.graphics_exposures
= False
;
1114 gc
= XCreateGC(dpy
, DefaultRootWindow(dpy
),
1115 GCForeground
| GCBackground
| GCGraphicsExposures
| GCFont
,
1124 static XtActionsRec actions
[] = {
1125 { "ClearWindow", ClearWindow
},
1126 { "GetPasswd", GetPasswd
},
1127 { "RaiseWindow", RaiseWindow
},
1129 XtAppAddActions(app
, actions
, XtNumber(actions
));
1130 XtOverrideTranslations(widget
,
1131 XtParseTranslationTable(
1132 "<Expose>: ClearWindow() \n"
1133 "<BtnDown>: GetPasswd() \n"
1134 "<Visible>: RaiseWindow() \n"
1135 "<KeyRelease>: GetPasswd() \n"
1136 "<KeyPress>: GetPasswd()"));
1139 XtRealizeWidget(override
);
1140 if((i
= XGrabPointer(dpy
, XtWindow(widget
), True
, 0, GrabModeAsync
,
1141 GrabModeAsync
, XtWindow(widget
),
1142 None
, CurrentTime
)) != 0)
1143 errx(1, "Failed to grab pointer (%d)", i
);
1145 if((i
= XGrabKeyboard(dpy
, XtWindow(widget
), True
, GrabModeAsync
,
1146 GrabModeAsync
, CurrentTime
)) != 0)
1147 errx(1, "Failed to grab keyboard (%d)", i
);