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).
11 __RCSID("$Heimdal: xnlock.c 21720 2007-07-28 20:04:05Z lha $"
19 #include <X11/StringDefs.h>
20 #include <X11/Intrinsic.h>
21 #include <X11/keysym.h>
22 #include <X11/Shell.h>
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
32 #define des_encrypt wingless_pigs_mostly_fail_to_fly
43 #if defined(KRB4) || defined(KRB5)
50 static char login
[16];
51 static char userprompt
[128];
53 static char name
[ANAME_SZ
];
54 static char inst
[INST_SZ
];
55 static char realm
[REALM_SZ
];
58 static krb5_context context
;
59 static krb5_principal client
;
62 #define font_height(font) (font->ascent + font->descent)
64 static char *SPACE_STRING
= " ";
65 static char STRING
[] = "****************";
67 #define STRING_LENGTH (sizeof(STRING))
68 #define MAX_PASSWD_LENGTH 256
69 /* (sizeof(STRING)) */
71 #define PROMPT "Password: "
72 #define FAIL_MSG "Sorry, try again"
81 #define XNLOCK_NOCTRL 0
83 static XtAppContext app
;
85 static unsigned short Width
, Height
;
88 static XtIntervalId timeout_id
;
91 static Pixel Black
, White
;
92 static XFontStruct
*font
;
93 static char root_cpass
[128];
94 static char user_cpass
[128];
95 static int time_left
, prompt_x
, prompt_y
, time_x
, time_y
;
96 static unsigned long interval
;
97 static Pixmap left0
, left1
, right0
, right1
, left_front
,
98 right_front
, front
, down
;
104 static int state
; /* indicates states: walking or getting passwd */
106 static int ALLOW_LOGOUT
= (60*10); /* Allow logout after nn seconds */
107 #define LOGOUT_PASSWD "enuHDmTo5Lq4g" /* when given password "LOGOUT" */
108 static time_t locked_at
;
114 Boolean ignore_passwd
;
117 char *text
, *text_prog
, *file
, *logoutPasswd
;
118 Boolean no_screensaver
;
119 Boolean destroytickets
;
122 static XtResource resources
[] = {
123 { XtNbackground
, XtCBackground
, XtRPixel
, sizeof(Pixel
),
124 XtOffsetOf(struct appres_t
, bg
), XtRString
, "black" },
126 { XtNforeground
, XtCForeground
, XtRPixel
, sizeof(Pixel
),
127 XtOffsetOf(struct appres_t
, fg
), XtRString
, "white" },
129 { XtNfont
, XtCFont
, XtRFontStruct
, sizeof (XFontStruct
*),
130 XtOffsetOf(struct appres_t
, font
),
131 XtRString
, "-*-new century schoolbook-*-*-*-18-*" },
133 { "ignorePasswd", "IgnorePasswd", XtRBoolean
, sizeof(Boolean
),
134 XtOffsetOf(struct appres_t
,ignore_passwd
),XtRImmediate
,(XtPointer
)False
},
136 { "acceptRootPasswd", "AcceptRootPasswd", XtRBoolean
, sizeof(Boolean
),
137 XtOffsetOf(struct appres_t
, accept_root
), XtRImmediate
, (XtPointer
)True
},
139 { "text", "Text", XtRString
, sizeof(String
),
140 XtOffsetOf(struct appres_t
, text
), XtRString
, "I'm out running around." },
142 { "program", "Program", XtRString
, sizeof(String
),
143 XtOffsetOf(struct appres_t
, text_prog
), XtRImmediate
, NULL
},
145 { "file", "File", XtRString
, sizeof(String
),
146 XtOffsetOf(struct appres_t
,file
), XtRImmediate
, NULL
},
148 { "logoutPasswd", "logoutPasswd", XtRString
, sizeof(String
),
149 XtOffsetOf(struct appres_t
, logoutPasswd
), XtRString
, LOGOUT_PASSWD
},
151 { "noScreenSaver", "NoScreenSaver", XtRBoolean
, sizeof(Boolean
),
152 XtOffsetOf(struct appres_t
,no_screensaver
), XtRImmediate
, (XtPointer
)True
},
154 { "destroyTickets", "DestroyTickets", XtRBoolean
, sizeof(Boolean
),
155 XtOffsetOf(struct appres_t
,destroytickets
), XtRImmediate
, (XtPointer
)True
},
158 static XrmOptionDescRec options
[] = {
159 { "-fg", ".foreground", XrmoptionSepArg
, NULL
},
160 { "-foreground", ".foreground", XrmoptionSepArg
, NULL
},
161 { "-fn", ".font", XrmoptionSepArg
, NULL
},
162 { "-font", ".font", XrmoptionSepArg
, NULL
},
163 { "-ip", ".ignorePasswd", XrmoptionNoArg
, "True" },
164 { "-noip", ".ignorePasswd", XrmoptionNoArg
, "False" },
165 { "-ar", ".acceptRootPasswd", XrmoptionNoArg
, "True" },
166 { "-noar", ".acceptRootPasswd", XrmoptionNoArg
, "False" },
167 { "-nonoscreensaver", ".noScreenSaver", XrmoptionNoArg
, "False" },
168 { "-nodestroytickets", ".destroyTickets", XrmoptionNoArg
, "False" },
175 static char buf
[512];
178 if (appres
.text_prog
) {
179 pp
= popen(appres
.text_prog
, "r");
181 warn("popen %s", appres
.text_prog
);
184 n
= fread(buf
, 1, sizeof(buf
) - 1, pp
);
190 pp
= fopen(appres
.file
, "r");
192 warn("fopen %s", appres
.file
);
195 n
= fread(buf
, 1, sizeof(buf
) - 1, pp
);
207 fprintf(stderr
, "usage: %s [options] [message]\n", getprogname());
208 fprintf(stderr
, "-fg color foreground color\n");
209 fprintf(stderr
, "-bg color background color\n");
210 fprintf(stderr
, "-rv reverse foreground/background colors\n");
211 fprintf(stderr
, "-nrv no reverse video\n");
212 fprintf(stderr
, "-ip ignore passwd\n");
213 fprintf(stderr
, "-nip don't ignore passwd\n");
214 fprintf(stderr
, "-ar accept root's passwd to unlock\n");
215 fprintf(stderr
, "-nar don't accept root's passwd\n");
216 fprintf(stderr
, "-f [file] message is read from file or ~/.msgfile\n");
217 fprintf(stderr
, "-prog program text is gotten from executing `program'\n");
218 fprintf(stderr
, "-nodestroytickets keep kerberos tickets\n");
219 fprintf(stderr
, "--version\n");
220 fprintf(stderr
, "--help\n");
225 init_words (int argc
, char **argv
)
230 if(strcmp(argv
[i
], "-p") == 0
231 || strcmp(argv
[i
], "-prog") == 0) {
234 appres
.text_prog
= argv
[i
];
237 warnx ("-p requires an argument");
240 } else if(strcmp(argv
[i
], "-f") == 0) {
243 appres
.file
= argv
[i
];
247 ret
= asprintf (&appres
.file
,
248 "%s/.msgfile", getenv("HOME"));
250 errx (1, "cannot allocate memory for message");
252 } else if(strcmp(argv
[i
], "--version") == 0) {
255 } else if(strcmp(argv
[i
], "--help") == 0) {
260 for(j
= i
; argv
[j
]; j
++)
261 len
+= strlen(argv
[j
]) + 1;
262 appres
.text
= malloc(len
);
263 if (appres
.text
== NULL
)
264 errx (1, "cannot allocate memory for message");
267 strlcat(appres
.text
, argv
[i
], len
);
268 strlcat(appres
.text
, " ", len
);
275 ScreenSaver(int save
)
277 static int timeout
, interval
, prefer_blank
, allow_exp
;
278 if(!appres
.no_screensaver
){
280 XGetScreenSaver(dpy
, &timeout
, &interval
,
281 &prefer_blank
, &allow_exp
);
282 XSetScreenSaver(dpy
, 0, interval
, prefer_blank
, allow_exp
);
285 XSetScreenSaver(dpy
, timeout
, interval
, prefer_blank
, allow_exp
);
289 /* Forward decls necessary */
290 static void talk(int force_erase
);
291 static unsigned long look(void);
298 warn ("zrefresh: fork");
302 execlp("zrefresh", "zrefresh", NULL
);
303 execl(BINDIR
"/zrefresh", "zrefresh", NULL
);
315 XUngrabPointer(dpy
, CurrentTime
);
316 XUngrabKeyboard(dpy
, CurrentTime
);
331 XSetForeground(dpy
, gc
, White
);
332 XSetBackground(dpy
, gc
, Black
);
333 if (dir
& (LEFT
|RIGHT
)) { /* left/right movement (mabye up/down too) */
334 up
= -up
; /* bouncing effect (even if hit a wall) */
337 frame
= (up
< 0) ? left0
: left1
;
340 frame
= (up
< 0) ? right0
: right1
;
342 if ((lastdir
== FRONT
|| lastdir
== DOWN
) && dir
& UP
) {
343 /* workaround silly bug that leaves screen dust when
344 * guy is facing forward or down and moves up-left/right.
346 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
, 0, 0, 64,64, x
, y
, 1L);
349 /* note that maybe neither UP nor DOWN is set! */
350 if (dir
& UP
&& y
> Y_INCR
)
352 else if (dir
& DOWN
&& y
< (int)Height
- 64)
355 /* Explicit up/down movement only (no left/right) */
357 XCopyPlane(dpy
, front
, XtWindow(widget
), gc
,
358 0,0, 64,64, x
, y
-= Y_INCR
, 1L);
359 else if (dir
== DOWN
)
360 XCopyPlane(dpy
, down
, XtWindow(widget
), gc
,
361 0,0, 64,64, x
, y
+= Y_INCR
, 1L);
362 else if (dir
== FRONT
&& frame
!= front
) {
367 else if (lastdir
& RIGHT
)
371 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
, 0, 0, 64,64, x
, y
, 1L);
375 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
,
376 0,0, 64,64, --x
, y
+up
, 1L);
379 else if (dir
& RIGHT
)
381 XCopyPlane(dpy
, frame
, XtWindow(widget
), gc
,
382 0,0, 64,64, ++x
, y
+up
, 1L);
403 if (my_random() & 1) {
411 move(XtPointer _p
, XtIntervalId
*_id
)
413 static int length
, dir
;
418 if ((my_random() & 1) && think()) {
419 talk(0); /* sets timeout to itself */
422 if (!(my_random() % 3) && (interval
= look())) {
423 timeout_id
= XtAppAddTimeOut(app
, interval
, move
, NULL
);
426 interval
= 20 + my_random() % 100;
429 length
= Width
/100 + my_random() % 90, tries
= 8;
432 switch (my_random() % 8) {
434 if (x
- X_INCR
*length
>= 5)
437 if (x
+ X_INCR
*length
<= (int)Width
- 70)
440 if (y
- (Y_INCR
*length
) >= 5)
441 dir
= UP
, interval
= 40;
443 if (y
+ Y_INCR
*length
<= (int)Height
- 70)
444 dir
= DOWN
, interval
= 20;
446 if (x
- X_INCR
*length
>= 5 && y
- (Y_INCR
*length
) >= 5)
449 if (x
+ X_INCR
* length
<= (int)Width
- 70 &&
450 y
-Y_INCR
* length
>= 5)
453 if (x
- X_INCR
* length
>= 5 &&
454 y
+ Y_INCR
* length
<= (int)Height
- 70)
457 if (x
+ X_INCR
*length
<= (int)Width
- 70 &&
458 y
+ Y_INCR
*length
<= (int)Height
- 70)
465 timeout_id
= XtAppAddTimeOut(app
, interval
, move
, NULL
);
469 post_prompt_box(Window window
)
471 int width
= (Width
/ 3);
472 int height
= font_height(font
) * 6;
475 /* make sure the entire nose icon fits in the box */
479 if(width
< 105 + font
->max_bounds
.width
*STRING_LENGTH
)
480 width
= 105 + font
->max_bounds
.width
*STRING_LENGTH
;
481 box_x
= (Width
- width
) / 2;
482 time_x
= prompt_x
= box_x
+ 105;
484 time_y
= prompt_y
= Height
/ 2;
485 box_y
= prompt_y
- 3 * font_height(font
);
487 /* erase current guy -- text message may still exist */
488 XSetForeground(dpy
, gc
, Black
);
489 XFillRectangle(dpy
, window
, gc
, x
, y
, 64, 64);
490 talk(1); /* forcefully erase message if one is being displayed */
491 /* Clear area in middle of screen for prompt box */
492 XSetForeground(dpy
, gc
, White
);
493 XFillRectangle(dpy
, window
, gc
, box_x
, box_y
, width
, height
);
495 /* make a box that's 5 pixels thick. Then add a thin box inside it */
496 XSetForeground(dpy
, gc
, Black
);
497 XSetLineAttributes(dpy
, gc
, 5, 0, 0, 0);
498 XDrawRectangle(dpy
, window
, gc
, box_x
+5, box_y
+5, width
-10, height
-10);
499 XSetLineAttributes(dpy
, gc
, 0, 0, 0, 0);
500 XDrawRectangle(dpy
, window
, gc
, box_x
+12, box_y
+12, width
-23, height
-23);
502 XDrawString(dpy
, window
, gc
,
503 prompt_x
, prompt_y
-font_height(font
),
504 userprompt
, strlen(userprompt
));
505 XDrawString(dpy
, window
, gc
, prompt_x
, prompt_y
, PROMPT
, strlen(PROMPT
));
506 /* set background for copyplane and DrawImageString; need reverse video */
507 XSetBackground(dpy
, gc
, White
);
508 XCopyPlane(dpy
, right0
, window
, gc
, 0,0, 64,64,
509 box_x
+ 20, box_y
+ (height
- 64)/2, 1L);
510 prompt_x
+= XTextWidth(font
, PROMPT
, strlen(PROMPT
));
511 time_y
+= 2*font_height(font
);
515 RaiseWindow(Widget w
, XEvent
*ev
, String
*s
, Cardinal
*n
)
521 XRaiseWindow(dpy
, XtWindow(x
));
526 ClearWindow(Widget w
, XEvent
*_event
, String
*_s
, Cardinal
*_n
)
528 XExposeEvent
*event
= (XExposeEvent
*)_event
;
529 if (!XtIsRealized(w
))
531 XClearArea(dpy
, XtWindow(w
), event
->x
, event
->y
,
532 event
->width
, event
->height
, False
);
533 if (state
== GET_PASSWD
)
534 post_prompt_box(XtWindow(w
));
535 if (timeout_id
== 0 && event
->count
== 0) {
536 timeout_id
= XtAppAddTimeOut(app
, 1000L, move
, NULL
);
537 /* first grab the input focus */
538 XSetInputFocus(dpy
, XtWindow(w
), RevertToPointerRoot
, CurrentTime
);
539 /* now grab the pointer and keyboard and contrain to this window */
540 XGrabPointer(dpy
, XtWindow(w
), TRUE
, 0, GrabModeAsync
,
541 GrabModeAsync
, XtWindow(w
), None
, CurrentTime
);
546 countdown(XtPointer _t
, XtIntervalId
*_d
)
548 int *timeout
= (int *)_t
;
552 if (--(*timeout
) < 0) {
554 XtRemoveTimeOut(timeout_id
);
556 event
.x
= event
.y
= 0;
557 event
.width
= Width
, event
.height
= Height
;
558 ClearWindow(widget
, (XEvent
*)&event
, 0, 0);
559 timeout_id
= XtAppAddTimeOut(app
, 200L, move
, NULL
);
562 seconds
= time(0) - locked_at
;
564 snprintf(buf
, sizeof(buf
),
565 "Locked for %d:%02d:%02d ",
566 (int)seconds
/3600, (int)seconds
/60%60, (int)seconds
%60);
568 snprintf(buf
, sizeof(buf
),
569 "Locked for %2d:%02d ",
570 (int)seconds
/60, (int)seconds
%60);
572 XDrawImageString(dpy
, XtWindow(widget
), gc
,
573 time_x
, time_y
, buf
, strlen(buf
));
574 XtAppAddTimeOut(app
, 1000L, countdown
, timeout
);
580 verify_krb5(const char *password
)
585 krb5_boolean get_v4_tgt
;
588 krb5_cc_default(context
, &id
);
589 ret
= krb5_verify_user(context
,
597 krb5_appdefault_boolean(context
, "xnlock",
598 krb5_principal_get_realm(context
, client
),
599 "krb4_get_tickets", FALSE
, &get_v4_tgt
);
602 krb5_creds mcred
, cred
;
604 krb5_cc_clear_mcred(&mcred
);
606 krb5_make_principal(context
, &mcred
.server
,
611 mcred
.client
= client
;
613 ret
= krb5_cc_retrieve_cred(context
, id
, 0, &mcred
, &cred
);
615 ret
= krb524_convert_creds_kdc_ccache(context
, id
, &cred
, &c
);
617 tf_setup(&c
, c
.pname
, c
.pinst
);
618 memset(&c
, 0, sizeof(c
));
619 krb5_free_cred_contents(context
, &cred
);
621 krb5_free_principal(context
, mcred
.server
);
625 krb5_afslog(context
, id
, NULL
, NULL
);
628 if (ret
!= KRB5KRB_AP_ERR_MODIFIED
)
629 krb5_warn(context
, ret
, "verify_krb5");
636 verify(char *password
)
639 * First try with root password, if allowed.
641 if ( appres
.accept_root
642 && strcmp(crypt(password
, root_cpass
), root_cpass
) == 0)
646 * Password that log out user
650 (time(0) - locked_at
) > ALLOW_LOGOUT
&&
651 strcmp(crypt(password
, appres
.logoutPasswd
), appres
.logoutPasswd
) == 0)
653 signal(SIGHUP
, SIG_IGN
);
656 /* If the X-server shut down then so will we, else
658 signal(SIGHUP
, SIG_DFL
);
662 * Try copy of users password.
664 if (strcmp(crypt(password
, user_cpass
), user_cpass
) == 0)
668 * Try to verify as user in case password change.
670 if (unix_verify_user(login
, password
) == 0)
675 * Try to verify as user with kerberos 5.
677 if(verify_krb5(password
) == 0)
685 * Try to verify as user with kerberos 4.
687 ret
= krb_verify_user(name
, inst
, realm
, password
,
688 KRB_VERIFY_NOT_SECURE
, NULL
);
689 if (ret
== KSUCCESS
){
691 krb_afslog(NULL
, NULL
);
694 if (ret
!= INTK_BADPW
)
695 warnx ("warning: %s",
696 (ret
< 0) ? strerror(ret
) : krb_get_err_text(ret
));
705 GetPasswd(Widget w
, XEvent
*_event
, String
*_s
, Cardinal
*_n
)
707 XKeyEvent
*event
= (XKeyEvent
*)_event
;
708 static char passwd
[MAX_PASSWD_LENGTH
];
710 static int is_ctrl
= XNLOCK_NOCTRL
;
714 int old_state
= state
;
716 if (event
->type
== ButtonPress
) {
717 x
= event
->x
, y
= event
->y
;
720 if (state
== IS_MOVING
) {
721 /* guy is running around--change to post prompt box. */
722 XtRemoveTimeOut(timeout_id
);
724 if (appres
.ignore_passwd
|| !strlen(user_cpass
))
726 post_prompt_box(XtWindow(w
));
729 countdown((XtPointer
)&time_left
, 0);
731 if (event
->type
== KeyRelease
) {
732 keysym
= XLookupKeysym(event
, 0);
733 if (keysym
== XK_Control_L
|| keysym
== XK_Control_R
) {
734 is_ctrl
= XNLOCK_NOCTRL
;
737 if (event
->type
!= KeyPress
)
742 keysym
= XLookupKeysym(event
, 0);
743 if (keysym
== XK_Control_L
|| keysym
== XK_Control_R
) {
744 is_ctrl
= XNLOCK_CTRL
;
747 if (!XLookupString(event
, &c
, 1, &keysym
, 0))
749 if (keysym
== XK_Return
|| keysym
== XK_Linefeed
) {
751 if(old_state
== IS_MOVING
)
753 XtRemoveTimeOut(timeout_id
);
755 if(verify(passwd
) == 0)
760 XDrawImageString(dpy
, XtWindow(widget
), gc
,
761 time_x
, time_y
, FAIL_MSG
, strlen(FAIL_MSG
));
763 timeout_id
= XtAppAddTimeOut(app
, 2000L, countdown
, &time_left
);
766 if (keysym
== XK_BackSpace
|| keysym
== XK_Delete
|| keysym
== XK_Left
) {
769 } else if (keysym
== XK_u
&& is_ctrl
== XNLOCK_CTRL
) {
772 echolen
= min(cnt
, STRING_LENGTH
);
773 XDrawImageString(dpy
, XtWindow(w
), gc
,
774 prompt_x
, prompt_y
, STRING
, echolen
);
775 XDrawImageString(dpy
, XtWindow(w
), gc
,
776 prompt_x
+ XTextWidth(font
, STRING
, echolen
),
777 prompt_y
, SPACE_STRING
, STRING_LENGTH
- echolen
+ 1);
779 } else if (isprint((unsigned char)c
)) {
780 if ((cnt
+ 1) >= MAX_PASSWD_LENGTH
)
786 echolen
= min(cnt
, STRING_LENGTH
);
787 XDrawImageString(dpy
, XtWindow(w
), gc
,
788 prompt_x
, prompt_y
, STRING
, echolen
);
789 XDrawImageString(dpy
, XtWindow(w
), gc
,
790 prompt_x
+ XTextWidth(font
, STRING
, echolen
),
791 prompt_y
, SPACE_STRING
, STRING_LENGTH
- echolen
+1);
794 #include "nose.0.left"
795 #include "nose.1.left"
796 #include "nose.0.right"
797 #include "nose.1.right"
798 #include "nose.left.front"
799 #include "nose.right.front"
800 #include "nose.front"
806 static Pixmap
*images
[] = {
807 &left0
, &left1
, &right0
, &right1
,
808 &left_front
, &right_front
, &front
, &down
810 static unsigned char *bits
[] = {
811 nose_0_left_bits
, nose_1_left_bits
, nose_0_right_bits
,
812 nose_1_right_bits
, nose_left_front_bits
, nose_right_front_bits
,
813 nose_front_bits
, nose_down_bits
817 for (i
= 0; i
< XtNumber(images
); i
++)
819 XCreatePixmapFromBitmapData(dpy
, DefaultRootWindow(dpy
),
820 (char*)(bits
[i
]), 64, 64, 1, 0, 1)))
821 XtError("Can't load nose images");
825 talk(int force_erase
)
827 int width
= 0, height
, Z
, total
= 0;
828 static int X
, Y
, talking
;
829 static struct { int x
, y
, width
, height
; } s_rect
;
831 char buf
[BUFSIZ
], args
[MAXLINES
][256];
833 /* clear what we've written */
834 if (talking
|| force_erase
) {
838 XSetForeground(dpy
, gc
, Black
);
839 XDrawString(dpy
, XtWindow(widget
), gc
, X
, Y
, words
, strlen(words
));
840 } else if (talking
== 1) {
841 XSetForeground(dpy
, gc
, Black
);
842 XFillRectangle(dpy
, XtWindow(widget
), gc
, s_rect
.x
-5, s_rect
.y
-5,
843 s_rect
.width
+10, s_rect
.height
+10);
847 timeout_id
= XtAppAddTimeOut(app
, 40L,
848 (XtTimerCallbackProc
)move
,
852 XSetForeground(dpy
, gc
, White
);
855 strlcpy (buf
, words
, sizeof(buf
));
858 /* possibly avoid a lot of work here
859 * if no CR or only one, then just print the line
861 if (!(p2
= strchr(p
, '\n')) || !p2
[1]) {
866 w
= XTextWidth(font
, words
, strlen(words
));
868 Y
= y
- 5 - font_height(font
);
869 /* give us a nice 5 pixel margin */
872 else if (X
+ w
+ 15 > (int)Width
+ 5)
875 Y
= y
+ 64 + 5 + font_height(font
);
876 XDrawString(dpy
, XtWindow(widget
), gc
, X
, Y
, words
, strlen(words
));
877 timeout_id
= XtAppAddTimeOut(app
, 5000L, (XtTimerCallbackProc
)talk
,
883 /* p2 now points to the first '\n' */
884 for (height
= 0; p
; height
++) {
887 if ((w
= XTextWidth(font
, p
, p2
- p
)) > width
)
889 total
+= p2
- p
; /* total chars; count to determine reading time */
890 strlcpy(args
[height
], p
, sizeof(args
[height
]));
891 if (height
== MAXLINES
- 1) {
892 puts("Message too long!");
896 if (!(p2
= strchr(p
, '\n')))
901 /* Figure out the height and width in pixels (height, width) extend
902 * the new box by 15 pixels on the sides (30 total) top and bottom.
904 s_rect
.width
= width
+ 30;
905 s_rect
.height
= height
* font_height(font
) + 30;
906 if (x
- s_rect
.width
- 10 < 5)
909 if ((s_rect
.x
= x
+32-(s_rect
.width
+15)/2)
910 + s_rect
.width
+15 > (int)Width
-5)
911 s_rect
.x
= Width
- 15 - s_rect
.width
;
912 if (y
- s_rect
.height
- 10 < 5)
913 s_rect
.y
= y
+ 64 + 5;
915 s_rect
.y
= y
- 5 - s_rect
.height
;
917 XSetForeground(dpy
, gc
, White
);
918 XFillRectangle(dpy
, XtWindow(widget
), gc
,
919 s_rect
.x
-5, s_rect
.y
-5, s_rect
.width
+10, s_rect
.height
+10);
921 /* make a box that's 5 pixels thick. Then add a thin box inside it */
922 XSetForeground(dpy
, gc
, Black
);
923 XSetLineAttributes(dpy
, gc
, 5, 0, 0, 0);
924 XDrawRectangle(dpy
, XtWindow(widget
), gc
,
925 s_rect
.x
, s_rect
.y
, s_rect
.width
-1, s_rect
.height
-1);
926 XSetLineAttributes(dpy
, gc
, 0, 0, 0, 0);
927 XDrawRectangle(dpy
, XtWindow(widget
), gc
,
928 s_rect
.x
+ 7, s_rect
.y
+ 7, s_rect
.width
- 15,
932 Y
= 15 + font_height(font
);
934 /* now print each string in reverse order (start at bottom of box) */
935 for (Z
= 0; Z
< height
; Z
++) {
936 XDrawString(dpy
, XtWindow(widget
), gc
, s_rect
.x
+X
, s_rect
.y
+Y
,
937 args
[Z
], strlen(args
[Z
]));
938 Y
+= font_height(font
);
940 timeout_id
= XtAppAddTimeOut(app
, (total
/15) * 1000,
941 (XtTimerCallbackProc
)talk
, NULL
);
947 XSetForeground(dpy
, gc
, White
);
948 XSetBackground(dpy
, gc
, Black
);
949 if (my_random() % 3) {
950 XCopyPlane(dpy
, (my_random() & 1)? down
: front
, XtWindow(widget
), gc
,
951 0, 0, 64,64, x
, y
, 1L);
954 if (!(my_random() % 5))
956 if (my_random() % 3) {
957 XCopyPlane(dpy
, (my_random() & 1)? left_front
: right_front
,
958 XtWindow(widget
), gc
, 0, 0, 64,64, x
, y
, 1L);
961 if (!(my_random() % 5))
963 XCopyPlane(dpy
, (my_random() & 1)? left0
: right0
, XtWindow(widget
), gc
,
964 0, 0, 64,64, x
, y
, 1L);
969 main (int argc
, char **argv
)
975 setprogname (argv
[0]);
978 * Must be setuid root to read /etc/shadow, copy encrypted
979 * passwords here and then switch to sane uid.
983 uid_t uid
= getuid();
984 if (!(pw
= k_getpwuid(0)))
985 errx (1, "can't get root's passwd!");
986 strlcpy(root_cpass
, pw
->pw_passwd
, sizeof(root_cpass
));
988 if (!(pw
= k_getpwuid(uid
)))
989 errx (1, "Can't get your password entry!");
990 strlcpy(user_cpass
, pw
->pw_passwd
, sizeof(user_cpass
));
992 if (uid
!= 0 && setuid(0) != -1) {
993 fprintf(stderr
, "Failed to drop privileges!\n");
996 /* Now we're no longer running setuid root. */
997 strlcpy(login
, pw
->pw_name
, sizeof(login
));
1000 #if defined(HAVE_SRANDOMDEV)
1002 #elif defined(HAVE_RANDOM)
1003 srandom(time(NULL
));
1007 for (i
= 0; i
< STRING_LENGTH
; i
++)
1008 STRING
[i
] = ((unsigned long)my_random() % ('~' - ' ')) + ' ';
1010 locked_at
= time(0);
1012 snprintf(userprompt
, sizeof(userprompt
), "User: %s", login
);
1014 krb_get_default_principal(name
, inst
, realm
);
1015 snprintf(userprompt
, sizeof(userprompt
), "User: %s",
1016 krb_unparse_name_long(name
, inst
, realm
));
1020 krb5_error_code ret
;
1023 ret
= krb5_init_context(&context
);
1025 errx (1, "krb5_init_context failed: %d", ret
);
1026 krb5_get_default_principal(context
, &client
);
1027 krb5_unparse_name(context
, client
, &str
);
1028 snprintf(userprompt
, sizeof(userprompt
), "User: %s", str
);
1033 override
= XtVaAppInitialize(&app
, "XNlock", options
, XtNumber(options
),
1035 XtNoverrideRedirect
, True
,
1038 XtVaGetApplicationResources(override
,(XtPointer
)&appres
,
1039 resources
,XtNumber(resources
),
1041 /* the background is black and the little guy is white */
1045 if (appres
.destroytickets
) {
1049 dest_tkt(); /* Nuke old ticket file */
1050 /* but keep a place holder */
1051 fd
= open (TKT_FILE
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
1057 dpy
= XtDisplay(override
);
1060 errx (1, "Error: Can't open display");
1062 Width
= DisplayWidth(dpy
, DefaultScreen(dpy
)) + 2;
1063 Height
= DisplayHeight(dpy
, DefaultScreen(dpy
)) + 2;
1065 for(i
= 0; i
< ScreenCount(dpy
); i
++){
1072 XtResource Res
[] = {
1073 { XtNbackground
, XtCBackground
, XtRPixel
, sizeof(Pixel
),
1074 XtOffsetOf(struct xxx
, bg
), XtRString
, "black" }
1077 if(i
== DefaultScreen(dpy
))
1080 shell
= XtVaAppCreateShell(NULL
,NULL
, applicationShellWidgetClass
, dpy
,
1081 XtNscreen
, ScreenOfDisplay(dpy
, i
),
1082 XtNoverrideRedirect
, True
,
1087 XtVaGetApplicationResources(shell
, (XtPointer
)&res
,
1091 core
= XtVaCreateManagedWidget("_foo", widgetClass
, shell
,
1092 XtNwidth
, DisplayWidth(dpy
, i
),
1093 XtNheight
, DisplayHeight(dpy
, i
),
1094 XtNbackground
, res
.bg
,
1096 XtRealizeWidget(shell
);
1099 widget
= XtVaCreateManagedWidget("_foo", widgetClass
, override
,
1102 XtNbackground
, Black
,
1105 init_words(--argc
, ++argv
);
1108 gcvalues
.foreground
= Black
;
1109 gcvalues
.background
= White
;
1113 gcvalues
.font
= font
->fid
;
1114 gcvalues
.graphics_exposures
= False
;
1115 gc
= XCreateGC(dpy
, DefaultRootWindow(dpy
),
1116 GCForeground
| GCBackground
| GCGraphicsExposures
| GCFont
,
1125 static XtActionsRec actions
[] = {
1126 { "ClearWindow", ClearWindow
},
1127 { "GetPasswd", GetPasswd
},
1128 { "RaiseWindow", RaiseWindow
},
1130 XtAppAddActions(app
, actions
, XtNumber(actions
));
1131 XtOverrideTranslations(widget
,
1132 XtParseTranslationTable(
1133 "<Expose>: ClearWindow() \n"
1134 "<BtnDown>: GetPasswd() \n"
1135 "<Visible>: RaiseWindow() \n"
1136 "<KeyRelease>: GetPasswd() \n"
1137 "<KeyPress>: GetPasswd()"));
1140 XtRealizeWidget(override
);
1141 if((i
= XGrabPointer(dpy
, XtWindow(widget
), True
, 0, GrabModeAsync
,
1142 GrabModeAsync
, XtWindow(widget
),
1143 None
, CurrentTime
)) != 0)
1144 errx(1, "Failed to grab pointer (%d)", i
);
1146 if((i
= XGrabKeyboard(dpy
, XtWindow(widget
), True
, GrabModeAsync
,
1147 GrabModeAsync
, CurrentTime
)) != 0)
1148 errx(1, "Failed to grab keyboard (%d)", i
);