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 (9755B)
---
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 } else if (buf[0] == '\025') { /* ctrl-u…
189 explicit_bzero(&passwd, sizeof(p…
190 len = 0;
191 }
192 break;
193 }
194 color = len ? INPUT : ((failure || failonclear) …
195 if (running && oldc != color) {
196 for (screen = 0; screen < nscreens; scre…
197 XSetWindowBackground(dpy,
198 locks[scree…
199 locks[scree…
200 XClearWindow(dpy, locks[screen]-…
201 }
202 oldc = color;
203 }
204 } else if (rr->active && ev.type == rr->evbase + RRScree…
205 rre = (XRRScreenChangeNotifyEvent*)&ev;
206 for (screen = 0; screen < nscreens; screen++) {
207 if (locks[screen]->win == rre->window) {
208 if (rre->rotation == RR_Rotate_9…
209 rre->rotation == RR_Rotate_2…
210 XResizeWindow(dpy, locks…
211 rre->heigh…
212 else
213 XResizeWindow(dpy, locks…
214 rre->width…
215 XClearWindow(dpy, locks[screen]-…
216 break;
217 }
218 }
219 } else {
220 for (screen = 0; screen < nscreens; screen++)
221 XRaiseWindow(dpy, locks[screen]->win);
222 }
223 }
224 }
225
226 static struct lock *
227 lockscreen(Display *dpy, struct xrandr *rr, int screen)
228 {
229 char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
230 int i, ptgrab, kbgrab;
231 struct lock *lock;
232 XColor color, dummy;
233 XSetWindowAttributes wa;
234 Cursor invisible;
235
236 if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct l…
237 return NULL;
238
239 lock->screen = screen;
240 lock->root = RootWindow(dpy, lock->screen);
241
242 for (i = 0; i < NUMCOLS; i++) {
243 XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
244 colorname[i], &color, &dummy);
245 lock->colors[i] = color.pixel;
246 }
247
248 /* init */
249 wa.override_redirect = 1;
250 wa.background_pixel = lock->colors[INIT];
251 lock->win = XCreateWindow(dpy, lock->root, 0, 0,
252 DisplayWidth(dpy, lock->screen),
253 DisplayHeight(dpy, lock->screen),
254 0, DefaultDepth(dpy, lock->screen),
255 CopyFromParent,
256 DefaultVisual(dpy, lock->screen),
257 CWOverrideRedirect | CWBackPixel, &wa);
258 lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
259 invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
260 &color, &color, 0, 0);
261 XDefineCursor(dpy, lock->win, invisible);
262
263 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail…
264 for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
265 if (ptgrab != GrabSuccess) {
266 ptgrab = XGrabPointer(dpy, lock->root, False,
267 ButtonPressMask | ButtonRe…
268 PointerMotionMask, GrabMod…
269 GrabModeAsync, None, invis…
270 }
271 if (kbgrab != GrabSuccess) {
272 kbgrab = XGrabKeyboard(dpy, lock->root, True,
273 GrabModeAsync, GrabModeAs…
274 }
275
276 /* input is grabbed: we can lock the screen */
277 if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
278 XMapRaised(dpy, lock->win);
279 if (rr->active)
280 XRRSelectInput(dpy, lock->win, RRScreenC…
281
282 XSelectInput(dpy, lock->root, SubstructureNotify…
283 return lock;
284 }
285
286 /* retry on AlreadyGrabbed but fail on other errors */
287 if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) …
288 (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
289 break;
290
291 usleep(100000);
292 }
293
294 /* we couldn't grab all input: fail out */
295 if (ptgrab != GrabSuccess)
296 fprintf(stderr, "slock: unable to grab mouse pointer for…
297 screen);
298 if (kbgrab != GrabSuccess)
299 fprintf(stderr, "slock: unable to grab keyboard for scre…
300 screen);
301 return NULL;
302 }
303
304 static void
305 usage(void)
306 {
307 die("usage: slock [-v] [cmd [arg ...]]\n");
308 }
309
310 int
311 main(int argc, char **argv) {
312 struct xrandr rr;
313 struct lock **locks;
314 struct passwd *pwd;
315 struct group *grp;
316 uid_t duid;
317 gid_t dgid;
318 const char *hash;
319 Display *dpy;
320 int s, nlocks, nscreens;
321
322 ARGBEGIN {
323 case 'v':
324 puts("slock-"VERSION);
325 return 0;
326 default:
327 usage();
328 } ARGEND
329
330 /* validate drop-user and -group */
331 errno = 0;
332 if (!(pwd = getpwnam(user)))
333 die("slock: getpwnam %s: %s\n", user,
334 errno ? strerror(errno) : "user entry not found");
335 duid = pwd->pw_uid;
336 errno = 0;
337 if (!(grp = getgrnam(group)))
338 die("slock: getgrnam %s: %s\n", group,
339 errno ? strerror(errno) : "group entry not found");
340 dgid = grp->gr_gid;
341
342 #ifdef __linux__
343 dontkillme();
344 #endif
345
346 hash = gethash();
347 errno = 0;
348 if (!crypt("", hash))
349 die("slock: crypt: %s\n", strerror(errno));
350
351 if (!(dpy = XOpenDisplay(NULL)))
352 die("slock: cannot open display\n");
353
354 /* drop privileges */
355 if (setgroups(0, NULL) < 0)
356 die("slock: setgroups: %s\n", strerror(errno));
357 if (setgid(dgid) < 0)
358 die("slock: setgid: %s\n", strerror(errno));
359 if (setuid(duid) < 0)
360 die("slock: setuid: %s\n", strerror(errno));
361
362 /* check for Xrandr support */
363 rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
364
365 /* get number of screens in display "dpy" and blank them */
366 nscreens = ScreenCount(dpy);
367 if (!(locks = calloc(nscreens, sizeof(struct lock *))))
368 die("slock: out of memory\n");
369 for (nlocks = 0, s = 0; s < nscreens; s++) {
370 if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
371 nlocks++;
372 else
373 break;
374 }
375 XSync(dpy, 0);
376
377 /* did we manage to lock everything? */
378 if (nlocks != nscreens)
379 return 1;
380
381 /* run post-lock command */
382 if (argc > 0) {
383 pid_t pid;
384 extern char **environ;
385 int err = posix_spawnp(&pid, argv[0], NULL, NULL, argv, …
386 if (err) {
387 die("slock: failed to execute post-lock command:…
388 argv[0], strerror(err));
389 }
390 }
391
392 /* everything is now blank. Wait for the correct password */
393 readpw(dpy, &rr, locks, nscreens, hash);
394
395 return 0;
396 }
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.