Introduction
Introduction Statistics Contact Development Disclaimer Help
slock.c - slock - simple X display locker utility
git clone git://git.suckless.org/slock
Log
Files
Refs
README
LICENSE
---
slock.c (9634B)
---
1 /* See LICENSE file for license details. */
2 #define _XOPEN_SOURCE 500
3 #if HAVE_SHADOW_H
4 #include <shadow.h>
5 #endif
6
7 #include <ctype.h>
8 #include <errno.h>
9 #include <grp.h>
10 #include <pwd.h>
11 #include <stdarg.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <spawn.h>
17 #include <sys/types.h>
18 #include <X11/extensions/Xrandr.h>
19 #include <X11/keysym.h>
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22
23 #include "arg.h"
24 #include "util.h"
25
26 char *argv0;
27
28 enum {
29 INIT,
30 INPUT,
31 FAILED,
32 NUMCOLS
33 };
34
35 struct lock {
36 int screen;
37 Window root, win;
38 Pixmap pmap;
39 unsigned long colors[NUMCOLS];
40 };
41
42 struct xrandr {
43 int active;
44 int evbase;
45 int errbase;
46 };
47
48 #include "config.h"
49
50 static void
51 die(const char *errstr, ...)
52 {
53 va_list ap;
54
55 va_start(ap, errstr);
56 vfprintf(stderr, errstr, ap);
57 va_end(ap);
58 exit(1);
59 }
60
61 #ifdef __linux__
62 #include <fcntl.h>
63 #include <linux/oom.h>
64
65 static void
66 dontkillme(void)
67 {
68 FILE *f;
69 const char oomfile[] = "/proc/self/oom_score_adj";
70
71 if (!(f = fopen(oomfile, "w"))) {
72 if (errno == ENOENT)
73 return;
74 die("slock: fopen %s: %s\n", oomfile, strerror(errno));
75 }
76 fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
77 if (fclose(f)) {
78 if (errno == EACCES)
79 die("slock: unable to disable OOM killer. "
80 "Make sure to suid or sgid slock.\n");
81 else
82 die("slock: fclose %s: %s\n", oomfile, strerror(…
83 }
84 }
85 #endif
86
87 static const char *
88 gethash(void)
89 {
90 const char *hash;
91 struct passwd *pw;
92
93 /* Check if the current user has a password entry */
94 errno = 0;
95 if (!(pw = getpwuid(getuid()))) {
96 if (errno)
97 die("slock: getpwuid: %s\n", strerror(errno));
98 else
99 die("slock: cannot retrieve password entry\n");
100 }
101 hash = pw->pw_passwd;
102
103 #if HAVE_SHADOW_H
104 if (!strcmp(hash, "x")) {
105 struct spwd *sp;
106 if (!(sp = getspnam(pw->pw_name)))
107 die("slock: getspnam: cannot retrieve shadow ent…
108 "Make sure to suid or sgid slock.\n");
109 hash = sp->sp_pwdp;
110 }
111 #else
112 if (!strcmp(hash, "*")) {
113 #ifdef __OpenBSD__
114 if (!(pw = getpwuid_shadow(getuid())))
115 die("slock: getpwnam_shadow: cannot retrieve sha…
116 "Make sure to suid or sgid slock.\n");
117 hash = pw->pw_passwd;
118 #else
119 die("slock: getpwuid: cannot retrieve shadow entry. "
120 "Make sure to suid or sgid slock.\n");
121 #endif /* __OpenBSD__ */
122 }
123 #endif /* HAVE_SHADOW_H */
124
125 return hash;
126 }
127
128 static void
129 readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreen…
130 const char *hash)
131 {
132 XRRScreenChangeNotifyEvent *rre;
133 char buf[32], passwd[256], *inputhash;
134 int num, screen, running, failure, oldc;
135 unsigned int len, color;
136 KeySym ksym;
137 XEvent ev;
138
139 len = 0;
140 running = 1;
141 failure = 0;
142 oldc = INIT;
143
144 while (running && !XNextEvent(dpy, &ev)) {
145 if (ev.type == KeyPress) {
146 explicit_bzero(&buf, sizeof(buf));
147 num = XLookupString(&ev.xkey, buf, sizeof(buf), …
148 if (IsKeypadKey(ksym)) {
149 if (ksym == XK_KP_Enter)
150 ksym = XK_Return;
151 else if (ksym >= XK_KP_0 && ksym <= XK_K…
152 ksym = (ksym - XK_KP_0) + XK_0;
153 }
154 if (IsFunctionKey(ksym) ||
155 IsKeypadKey(ksym) ||
156 IsMiscFunctionKey(ksym) ||
157 IsPFKey(ksym) ||
158 IsPrivateKeypadKey(ksym))
159 continue;
160 switch (ksym) {
161 case XK_Return:
162 passwd[len] = '\0';
163 errno = 0;
164 if (!(inputhash = crypt(passwd, hash)))
165 fprintf(stderr, "slock: crypt: %…
166 else
167 running = !!strcmp(inputhash, ha…
168 if (running) {
169 XBell(dpy, 100);
170 failure = 1;
171 }
172 explicit_bzero(&passwd, sizeof(passwd));
173 len = 0;
174 break;
175 case XK_Escape:
176 explicit_bzero(&passwd, sizeof(passwd));
177 len = 0;
178 break;
179 case XK_BackSpace:
180 if (len)
181 passwd[--len] = '\0';
182 break;
183 default:
184 if (num && !iscntrl((int)buf[0]) &&
185 (len + num < sizeof(passwd))) {
186 memcpy(passwd + len, buf, num);
187 len += num;
188 }
189 break;
190 }
191 color = len ? INPUT : ((failure || failonclear) …
192 if (running && oldc != color) {
193 for (screen = 0; screen < nscreens; scre…
194 XSetWindowBackground(dpy,
195 locks[scree…
196 locks[scree…
197 XClearWindow(dpy, locks[screen]-…
198 }
199 oldc = color;
200 }
201 } else if (rr->active && ev.type == rr->evbase + RRScree…
202 rre = (XRRScreenChangeNotifyEvent*)&ev;
203 for (screen = 0; screen < nscreens; screen++) {
204 if (locks[screen]->win == rre->window) {
205 if (rre->rotation == RR_Rotate_9…
206 rre->rotation == RR_Rotate_2…
207 XResizeWindow(dpy, locks…
208 rre->heigh…
209 else
210 XResizeWindow(dpy, locks…
211 rre->width…
212 XClearWindow(dpy, locks[screen]-…
213 break;
214 }
215 }
216 } else {
217 for (screen = 0; screen < nscreens; screen++)
218 XRaiseWindow(dpy, locks[screen]->win);
219 }
220 }
221 }
222
223 static struct lock *
224 lockscreen(Display *dpy, struct xrandr *rr, int screen)
225 {
226 char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
227 int i, ptgrab, kbgrab;
228 struct lock *lock;
229 XColor color, dummy;
230 XSetWindowAttributes wa;
231 Cursor invisible;
232
233 if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct l…
234 return NULL;
235
236 lock->screen = screen;
237 lock->root = RootWindow(dpy, lock->screen);
238
239 for (i = 0; i < NUMCOLS; i++) {
240 XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
241 colorname[i], &color, &dummy);
242 lock->colors[i] = color.pixel;
243 }
244
245 /* init */
246 wa.override_redirect = 1;
247 wa.background_pixel = lock->colors[INIT];
248 lock->win = XCreateWindow(dpy, lock->root, 0, 0,
249 DisplayWidth(dpy, lock->screen),
250 DisplayHeight(dpy, lock->screen),
251 0, DefaultDepth(dpy, lock->screen),
252 CopyFromParent,
253 DefaultVisual(dpy, lock->screen),
254 CWOverrideRedirect | CWBackPixel, &wa);
255 lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
256 invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
257 &color, &color, 0, 0);
258 XDefineCursor(dpy, lock->win, invisible);
259
260 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail…
261 for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
262 if (ptgrab != GrabSuccess) {
263 ptgrab = XGrabPointer(dpy, lock->root, False,
264 ButtonPressMask | ButtonRe…
265 PointerMotionMask, GrabMod…
266 GrabModeAsync, None, invis…
267 }
268 if (kbgrab != GrabSuccess) {
269 kbgrab = XGrabKeyboard(dpy, lock->root, True,
270 GrabModeAsync, GrabModeAs…
271 }
272
273 /* input is grabbed: we can lock the screen */
274 if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
275 XMapRaised(dpy, lock->win);
276 if (rr->active)
277 XRRSelectInput(dpy, lock->win, RRScreenC…
278
279 XSelectInput(dpy, lock->root, SubstructureNotify…
280 return lock;
281 }
282
283 /* retry on AlreadyGrabbed but fail on other errors */
284 if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) …
285 (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
286 break;
287
288 usleep(100000);
289 }
290
291 /* we couldn't grab all input: fail out */
292 if (ptgrab != GrabSuccess)
293 fprintf(stderr, "slock: unable to grab mouse pointer for…
294 screen);
295 if (kbgrab != GrabSuccess)
296 fprintf(stderr, "slock: unable to grab keyboard for scre…
297 screen);
298 return NULL;
299 }
300
301 static void
302 usage(void)
303 {
304 die("usage: slock [-v] [cmd [arg ...]]\n");
305 }
306
307 int
308 main(int argc, char **argv) {
309 struct xrandr rr;
310 struct lock **locks;
311 struct passwd *pwd;
312 struct group *grp;
313 uid_t duid;
314 gid_t dgid;
315 const char *hash;
316 Display *dpy;
317 int s, nlocks, nscreens;
318
319 ARGBEGIN {
320 case 'v':
321 puts("slock-"VERSION);
322 return 0;
323 default:
324 usage();
325 } ARGEND
326
327 /* validate drop-user and -group */
328 errno = 0;
329 if (!(pwd = getpwnam(user)))
330 die("slock: getpwnam %s: %s\n", user,
331 errno ? strerror(errno) : "user entry not found");
332 duid = pwd->pw_uid;
333 errno = 0;
334 if (!(grp = getgrnam(group)))
335 die("slock: getgrnam %s: %s\n", group,
336 errno ? strerror(errno) : "group entry not found");
337 dgid = grp->gr_gid;
338
339 #ifdef __linux__
340 dontkillme();
341 #endif
342
343 hash = gethash();
344 errno = 0;
345 if (!crypt("", hash))
346 die("slock: crypt: %s\n", strerror(errno));
347
348 if (!(dpy = XOpenDisplay(NULL)))
349 die("slock: cannot open display\n");
350
351 /* drop privileges */
352 if (setgroups(0, NULL) < 0)
353 die("slock: setgroups: %s\n", strerror(errno));
354 if (setgid(dgid) < 0)
355 die("slock: setgid: %s\n", strerror(errno));
356 if (setuid(duid) < 0)
357 die("slock: setuid: %s\n", strerror(errno));
358
359 /* check for Xrandr support */
360 rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
361
362 /* get number of screens in display "dpy" and blank them */
363 nscreens = ScreenCount(dpy);
364 if (!(locks = calloc(nscreens, sizeof(struct lock *))))
365 die("slock: out of memory\n");
366 for (nlocks = 0, s = 0; s < nscreens; s++) {
367 if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
368 nlocks++;
369 else
370 break;
371 }
372 XSync(dpy, 0);
373
374 /* did we manage to lock everything? */
375 if (nlocks != nscreens)
376 return 1;
377
378 /* run post-lock command */
379 if (argc > 0) {
380 pid_t pid;
381 extern char **environ;
382 int err = posix_spawnp(&pid, argv[0], NULL, NULL, argv, …
383 if (err) {
384 die("slock: failed to execute post-lock command:…
385 argv[0], strerror(err));
386 }
387 }
388
389 /* everything is now blank. Wait for the correct password */
390 readpw(dpy, &rr, locks, nscreens, hash);
391
392 return 0;
393 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.