1 /* See LICENSE file for license details. */
2 #define _XOPEN_SOURCE 500
16 #include <sys/types.h>
17 #include <X11/extensions/Xrandr.h>
18 #include <X11/extensions/Xinerama.h>
19 #include <X11/keysym.h>
21 #include <X11/Xutil.h>
28 /* global count to prevent repeated error messages */
42 unsigned long colors
[NUMCOLS
];
54 die(const char *errstr
, ...)
59 vfprintf(stderr
, errstr
, ap
);
66 #include <linux/oom.h>
72 const char oomfile
[] = "/proc/self/oom_score_adj";
74 if (!(f
= fopen(oomfile
, "w"))) {
77 die("slock: fopen %s: %s\n", oomfile
, strerror(errno
));
79 fprintf(f
, "%d", OOM_SCORE_ADJ_MIN
);
82 die("slock: unable to disable OOM killer. "
83 "Make sure to suid or sgid slock.\n");
85 die("slock: fclose %s: %s\n", oomfile
, strerror(errno
));
91 readescapedint(const char *str
, int *i
) {
95 while(str
[*i
] && str
[*i
] != ';' && str
[*i
] != 'm') {
96 n
= 10 * n
+ str
[*i
] - '0';
103 writemessage(Display
*dpy
, Window win
, int screen
)
105 int len
, line_len
, width
, height
, s_width
, s_height
, i
, k
, tab_size
, r
, g
, b
, escaped_int
, curr_line_len
;
107 XFontStruct
*fontinfo
;
109 XineramaScreenInfo
*xsi
;
111 fontinfo
= XLoadQueryFont(dpy
, font_name
);
113 if (fontinfo
== NULL
) {
114 if (count_error
== 0) {
115 fprintf(stderr
, "slock: Unable to load font \"%s\"\n", font_name
);
116 fprintf(stderr
, "slock: Try listing fonts with 'slock -f'\n");
122 tab_size
= 8 * XTextWidth(fontinfo
, " ", 1);
124 XAllocNamedColor(dpy
, DefaultColormap(dpy
, screen
),
125 text_color
, &color
, &dummy
);
127 gr_values
.font
= fontinfo
->fid
;
128 gr_values
.foreground
= color
.pixel
;
129 gc
=XCreateGC(dpy
,win
,GCFont
+GCForeground
, &gr_values
);
131 /* To prevent "Uninitialized" warnings. */
135 * Start formatting and drawing text
138 len
= strlen(message
);
140 /* Max max line length (cut at '\n') */
141 line_len
= curr_line_len
= 0;
143 for (i
= 0; i
< len
; i
++) {
144 if (message
[i
] == '\n') {
147 } else if (message
[i
] == 0x1b) {
148 while (i
< len
&& message
[i
] != 'm') {
152 die("slock: unclosed escape sequence\n");
154 curr_line_len
+= XTextWidth(fontinfo
, message
+ i
, 1);
155 if (curr_line_len
> line_len
)
156 line_len
= curr_line_len
;
159 /* If there is only one line */
163 if (XineramaIsActive(dpy
)) {
164 xsi
= XineramaQueryScreens(dpy
, &i
);
165 s_width
= xsi
[0].width
;
166 s_height
= xsi
[0].height
;
168 s_width
= DisplayWidth(dpy
, screen
);
169 s_height
= DisplayHeight(dpy
, screen
);
171 height
= s_height
*3/7 - (k
*20)/3;
172 width
= (s_width
- line_len
)/2;
175 /* print the text while parsing 24 bit color ANSI escape codes*/
176 for (i
= k
= 0; i
< len
; i
++) {
177 switch (message
[i
]) {
180 while (message
[i
+ 1] == '\t') {
181 line_len
+= tab_size
;
188 if (message
[i
] == '[') {
189 escaped_int
= readescapedint(message
, &i
);
190 if (escaped_int
== 39)
192 if (escaped_int
!= 38)
193 die("slock: unknown escape sequence%d\n", escaped_int
);
194 if (readescapedint(message
, &i
) != 2)
195 die("slock: only 24 bit color supported\n");
196 r
= readescapedint(message
, &i
) & 0xff;
197 g
= readescapedint(message
, &i
) & 0xff;
198 b
= readescapedint(message
, &i
) & 0xff;
199 XSetForeground(dpy
, gc
, r
<< 16 | g
<< 8 | b
);
201 die("slock: unknown escape sequence\n");
204 XDrawString(dpy
, win
, gc
, width
+ line_len
, height
+ 20 * k
, message
+ i
, 1);
205 line_len
+= XTextWidth(fontinfo
, message
+ i
, 1);
209 /* xsi should not be NULL anyway if Xinerama is active, but to be safe */
210 if (XineramaIsActive(dpy
) && xsi
!= NULL
)
222 /* Check if the current user has a password entry */
224 if (!(pw
= getpwuid(getuid()))) {
226 die("slock: getpwuid: %s\n", strerror(errno
));
228 die("slock: cannot retrieve password entry\n");
230 hash
= pw
->pw_passwd
;
233 if (!strcmp(hash
, "x")) {
235 if (!(sp
= getspnam(pw
->pw_name
)))
236 die("slock: getspnam: cannot retrieve shadow entry. "
237 "Make sure to suid or sgid slock.\n");
241 if (!strcmp(hash
, "*")) {
243 if (!(pw
= getpwuid_shadow(getuid())))
244 die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
245 "Make sure to suid or sgid slock.\n");
246 hash
= pw
->pw_passwd
;
248 die("slock: getpwuid: cannot retrieve shadow entry. "
249 "Make sure to suid or sgid slock.\n");
250 #endif /* __OpenBSD__ */
252 #endif /* HAVE_SHADOW_H */
258 readpw(Display
*dpy
, struct xrandr
*rr
, struct lock
**locks
, int nscreens
,
261 XRRScreenChangeNotifyEvent
*rre
;
262 char buf
[32], passwd
[256], *inputhash
;
263 int num
, screen
, running
, failure
, oldc
;
264 unsigned int len
, color
;
273 while (running
&& !XNextEvent(dpy
, &ev
)) {
274 if (ev
.type
== KeyPress
) {
275 explicit_bzero(&buf
, sizeof(buf
));
276 num
= XLookupString(&ev
.xkey
, buf
, sizeof(buf
), &ksym
, 0);
277 if (IsKeypadKey(ksym
)) {
278 if (ksym
== XK_KP_Enter
)
280 else if (ksym
>= XK_KP_0
&& ksym
<= XK_KP_9
)
281 ksym
= (ksym
- XK_KP_0
) + XK_0
;
283 if (IsFunctionKey(ksym
) ||
285 IsMiscFunctionKey(ksym
) ||
287 IsPrivateKeypadKey(ksym
))
293 if (!(inputhash
= crypt(passwd
, hash
)))
294 fprintf(stderr
, "slock: crypt: %s\n", strerror(errno
));
296 running
= !!strcmp(inputhash
, hash
);
301 explicit_bzero(&passwd
, sizeof(passwd
));
305 explicit_bzero(&passwd
, sizeof(passwd
));
310 passwd
[--len
] = '\0';
313 if (num
&& !iscntrl((int)buf
[0]) &&
314 (len
+ num
< sizeof(passwd
))) {
315 memcpy(passwd
+ len
, buf
, num
);
320 color
= len
? INPUT
: ((failure
|| failonclear
) ? FAILED
: INIT
);
321 if (running
&& oldc
!= color
) {
322 for (screen
= 0; screen
< nscreens
; screen
++) {
323 XSetWindowBackground(dpy
,
325 locks
[screen
]->colors
[color
]);
326 XClearWindow(dpy
, locks
[screen
]->win
);
327 writemessage(dpy
, locks
[screen
]->win
, screen
);
331 } else if (rr
->active
&& ev
.type
== rr
->evbase
+ RRScreenChangeNotify
) {
332 rre
= (XRRScreenChangeNotifyEvent
*)&ev
;
333 for (screen
= 0; screen
< nscreens
; screen
++) {
334 if (locks
[screen
]->win
== rre
->window
) {
335 if (rre
->rotation
== RR_Rotate_90
||
336 rre
->rotation
== RR_Rotate_270
)
337 XResizeWindow(dpy
, locks
[screen
]->win
,
338 rre
->height
, rre
->width
);
340 XResizeWindow(dpy
, locks
[screen
]->win
,
341 rre
->width
, rre
->height
);
342 XClearWindow(dpy
, locks
[screen
]->win
);
347 for (screen
= 0; screen
< nscreens
; screen
++)
348 XRaiseWindow(dpy
, locks
[screen
]->win
);
354 lockscreen(Display
*dpy
, struct xrandr
*rr
, int screen
)
356 char curs
[] = {0, 0, 0, 0, 0, 0, 0, 0};
357 int i
, ptgrab
, kbgrab
;
360 XSetWindowAttributes wa
;
363 if (dpy
== NULL
|| screen
< 0 || !(lock
= malloc(sizeof(struct lock
))))
366 lock
->screen
= screen
;
367 lock
->root
= RootWindow(dpy
, lock
->screen
);
369 for (i
= 0; i
< NUMCOLS
; i
++) {
370 XAllocNamedColor(dpy
, DefaultColormap(dpy
, lock
->screen
),
371 colorname
[i
], &color
, &dummy
);
372 lock
->colors
[i
] = color
.pixel
;
376 wa
.override_redirect
= 1;
377 wa
.background_pixel
= lock
->colors
[INIT
];
378 lock
->win
= XCreateWindow(dpy
, lock
->root
, 0, 0,
379 DisplayWidth(dpy
, lock
->screen
),
380 DisplayHeight(dpy
, lock
->screen
),
381 0, DefaultDepth(dpy
, lock
->screen
),
383 DefaultVisual(dpy
, lock
->screen
),
384 CWOverrideRedirect
| CWBackPixel
, &wa
);
385 lock
->pmap
= XCreateBitmapFromData(dpy
, lock
->win
, curs
, 8, 8);
386 invisible
= XCreatePixmapCursor(dpy
, lock
->pmap
, lock
->pmap
,
387 &color
, &color
, 0, 0);
388 XDefineCursor(dpy
, lock
->win
, invisible
);
390 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
391 for (i
= 0, ptgrab
= kbgrab
= -1; i
< 6; i
++) {
392 if (ptgrab
!= GrabSuccess
) {
393 ptgrab
= XGrabPointer(dpy
, lock
->root
, False
,
394 ButtonPressMask
| ButtonReleaseMask
|
395 PointerMotionMask
, GrabModeAsync
,
396 GrabModeAsync
, None
, invisible
, CurrentTime
);
398 if (kbgrab
!= GrabSuccess
) {
399 kbgrab
= XGrabKeyboard(dpy
, lock
->root
, True
,
400 GrabModeAsync
, GrabModeAsync
, CurrentTime
);
403 /* input is grabbed: we can lock the screen */
404 if (ptgrab
== GrabSuccess
&& kbgrab
== GrabSuccess
) {
405 XMapRaised(dpy
, lock
->win
);
407 XRRSelectInput(dpy
, lock
->win
, RRScreenChangeNotifyMask
);
409 XSelectInput(dpy
, lock
->root
, SubstructureNotifyMask
);
413 /* retry on AlreadyGrabbed but fail on other errors */
414 if ((ptgrab
!= AlreadyGrabbed
&& ptgrab
!= GrabSuccess
) ||
415 (kbgrab
!= AlreadyGrabbed
&& kbgrab
!= GrabSuccess
))
421 /* we couldn't grab all input: fail out */
422 if (ptgrab
!= GrabSuccess
)
423 fprintf(stderr
, "slock: unable to grab mouse pointer for screen %d\n",
425 if (kbgrab
!= GrabSuccess
)
426 fprintf(stderr
, "slock: unable to grab keyboard for screen %d\n",
434 die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n");
438 main(int argc
, char **argv
) {
447 int i
, s
, nlocks
, nscreens
;
453 fprintf(stderr
, "slock-"VERSION
"\n");
456 message
= EARGF(usage());
459 if (!(dpy
= XOpenDisplay(NULL
)))
460 die("slock: cannot open display\n");
461 font_names
= XListFonts(dpy
, "*", 10000 /* list 10000 fonts*/, &count_fonts
);
462 for (i
=0; i
<count_fonts
; i
++) {
463 fprintf(stderr
, "%s\n", *(font_names
+i
));
470 /* validate drop-user and -group */
472 if (!(pwd
= getpwnam(user
)))
473 die("slock: getpwnam %s: %s\n", user
,
474 errno
? strerror(errno
) : "user entry not found");
477 if (!(grp
= getgrnam(group
)))
478 die("slock: getgrnam %s: %s\n", group
,
479 errno
? strerror(errno
) : "group entry not found");
488 if (!crypt("", hash
))
489 die("slock: crypt: %s\n", strerror(errno
));
491 if (!(dpy
= XOpenDisplay(NULL
)))
492 die("slock: cannot open display\n");
494 /* drop privileges */
495 if (setgroups(0, NULL
) < 0)
496 die("slock: setgroups: %s\n", strerror(errno
));
497 if (setgid(dgid
) < 0)
498 die("slock: setgid: %s\n", strerror(errno
));
499 if (setuid(duid
) < 0)
500 die("slock: setuid: %s\n", strerror(errno
));
502 /* check for Xrandr support */
503 rr
.active
= XRRQueryExtension(dpy
, &rr
.evbase
, &rr
.errbase
);
505 /* get number of screens in display "dpy" and blank them */
506 nscreens
= ScreenCount(dpy
);
507 if (!(locks
= calloc(nscreens
, sizeof(struct lock
*))))
508 die("slock: out of memory\n");
509 for (nlocks
= 0, s
= 0; s
< nscreens
; s
++) {
510 if ((locks
[s
] = lockscreen(dpy
, &rr
, s
)) != NULL
) {
511 writemessage(dpy
, locks
[s
]->win
, s
);
519 /* did we manage to lock everything? */
520 if (nlocks
!= nscreens
)
523 /* run post-lock command */
527 die("slock: fork failed: %s\n", strerror(errno
));
529 if (close(ConnectionNumber(dpy
)) < 0)
530 die("slock: close: %s\n", strerror(errno
));
531 execvp(argv
[0], argv
);
532 fprintf(stderr
, "slock: execvp %s: %s\n", argv
[0], strerror(errno
));
537 /* everything is now blank. Wait for the correct password */
538 readpw(dpy
, &rr
, locks
, nscreens
, hash
);