first
[dcc-suckless-config] / slock / slock.c.orig
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 <sys/types.h>
17 #include <X11/extensions/Xrandr.h>
18 #include <X11/keysym.h>
19 #include <X11/Xlib.h>
20 #include <X11/Xutil.h>
21 #include <X11/XF86keysym.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(errno));
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 entry. "
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 shadow entry. "
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 nscreens,
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), &ksym, 0);
148                         if (IsKeypadKey(ksym)) {
149                                 if (ksym == XK_KP_Enter)
150                                         ksym = XK_Return;
151                                 else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
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 XF86XK_AudioPlay:
162       case XF86XK_AudioStop:
163       case XF86XK_AudioPrev:
164       case XF86XK_AudioNext:
165       case XF86XK_AudioRaiseVolume:
166       case XF86XK_AudioLowerVolume:
167       case XF86XK_AudioMute:
168       case XF86XK_AudioMicMute:
169       case XF86XK_MonBrightnessDown:
170       case XF86XK_MonBrightnessUp:
171         XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev);
172         break;
173                         case XK_Return:
174                                 passwd[len] = '\0';
175                                 errno = 0;
176                                 if (!(inputhash = crypt(passwd, hash)))
177                                         fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
178                                 else
179                                         running = !!strcmp(inputhash, hash);
180                                 if (running) {
181                                         XBell(dpy, 100);
182                                         failure = 1;
183                                 }
184                                 explicit_bzero(&passwd, sizeof(passwd));
185                                 len = 0;
186                                 break;
187                         case XK_Escape:
188                                 explicit_bzero(&passwd, sizeof(passwd));
189                                 len = 0;
190                                 break;
191                         case XK_BackSpace:
192                                 if (len)
193                                         passwd[--len] = '\0';
194                                 break;
195                         default:
196                                 if (num && !iscntrl((int)buf[0]) &&
197                                     (len + num < sizeof(passwd))) {
198                                         memcpy(passwd + len, buf, num);
199                                         len += num;
200                                 }
201                                 break;
202                         }
203                         color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT);
204                         if (running && oldc != color) {
205                                 for (screen = 0; screen < nscreens; screen++) {
206                                         XSetWindowBackground(dpy,
207                                                              locks[screen]->win,
208                                                              locks[screen]->colors[color]);
209                                         XClearWindow(dpy, locks[screen]->win);
210                                 }
211                                 oldc = color;
212                         }
213                 } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
214                         rre = (XRRScreenChangeNotifyEvent*)&ev;
215                         for (screen = 0; screen < nscreens; screen++) {
216                                 if (locks[screen]->win == rre->window) {
217                                         if (rre->rotation == RR_Rotate_90 ||
218                                             rre->rotation == RR_Rotate_270)
219                                                 XResizeWindow(dpy, locks[screen]->win,
220                                                               rre->height, rre->width);
221                                         else
222                                                 XResizeWindow(dpy, locks[screen]->win,
223                                                               rre->width, rre->height);
224                                         XClearWindow(dpy, locks[screen]->win);
225                                         break;
226                                 }
227                         }
228                 } else {
229                         for (screen = 0; screen < nscreens; screen++)
230                                 XRaiseWindow(dpy, locks[screen]->win);
231                 }
232         }
233 }
234
235 static struct lock *
236 lockscreen(Display *dpy, struct xrandr *rr, int screen)
237 {
238         char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
239         int i, ptgrab, kbgrab;
240         struct lock *lock;
241         XColor color, dummy;
242         XSetWindowAttributes wa;
243         Cursor invisible;
244
245         if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
246                 return NULL;
247
248         lock->screen = screen;
249         lock->root = RootWindow(dpy, lock->screen);
250
251         for (i = 0; i < NUMCOLS; i++) {
252                 XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
253                                  colorname[i], &color, &dummy);
254                 lock->colors[i] = color.pixel;
255         }
256
257         /* init */
258         wa.override_redirect = 1;
259         wa.background_pixel = lock->colors[INIT];
260         lock->win = XCreateWindow(dpy, lock->root, 0, 0,
261                                   DisplayWidth(dpy, lock->screen),
262                                   DisplayHeight(dpy, lock->screen),
263                                   0, DefaultDepth(dpy, lock->screen),
264                                   CopyFromParent,
265                                   DefaultVisual(dpy, lock->screen),
266                                   CWOverrideRedirect | CWBackPixel, &wa);
267         lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
268         invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
269                                         &color, &color, 0, 0);
270         XDefineCursor(dpy, lock->win, invisible);
271
272         /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
273         for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
274                 if (ptgrab != GrabSuccess) {
275                         ptgrab = XGrabPointer(dpy, lock->root, False,
276                                               ButtonPressMask | ButtonReleaseMask |
277                                               PointerMotionMask, GrabModeAsync,
278                                               GrabModeAsync, None, invisible, CurrentTime);
279                 }
280                 if (kbgrab != GrabSuccess) {
281                         kbgrab = XGrabKeyboard(dpy, lock->root, True,
282                                                GrabModeAsync, GrabModeAsync, CurrentTime);
283                 }
284
285                 /* input is grabbed: we can lock the screen */
286                 if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
287                         XMapRaised(dpy, lock->win);
288                         if (rr->active)
289                                 XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
290
291                         XSelectInput(dpy, lock->root, SubstructureNotifyMask);
292                         return lock;
293                 }
294
295                 /* retry on AlreadyGrabbed but fail on other errors */
296                 if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
297                     (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
298                         break;
299
300                 usleep(100000);
301         }
302
303         /* we couldn't grab all input: fail out */
304         if (ptgrab != GrabSuccess)
305                 fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
306                         screen);
307         if (kbgrab != GrabSuccess)
308                 fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
309                         screen);
310         return NULL;
311 }
312
313 static void
314 usage(void)
315 {
316         die("usage: slock [-v] [cmd [arg ...]]\n");
317 }
318
319 int
320 main(int argc, char **argv) {
321         struct xrandr rr;
322         struct lock **locks;
323         struct passwd *pwd;
324         struct group *grp;
325         uid_t duid;
326         gid_t dgid;
327         const char *hash;
328         Display *dpy;
329         int s, nlocks, nscreens;
330
331         ARGBEGIN {
332         case 'v':
333                 fprintf(stderr, "slock-"VERSION"\n");
334                 return 0;
335         default:
336                 usage();
337         } ARGEND
338
339         /* validate drop-user and -group */
340         errno = 0;
341         if (!(pwd = getpwnam(user)))
342                 die("slock: getpwnam %s: %s\n", user,
343                     errno ? strerror(errno) : "user entry not found");
344         duid = pwd->pw_uid;
345         errno = 0;
346         if (!(grp = getgrnam(group)))
347                 die("slock: getgrnam %s: %s\n", group,
348                     errno ? strerror(errno) : "group entry not found");
349         dgid = grp->gr_gid;
350
351 #ifdef __linux__
352         dontkillme();
353 #endif
354
355         hash = gethash();
356         errno = 0;
357         if (!crypt("", hash))
358                 die("slock: crypt: %s\n", strerror(errno));
359
360         if (!(dpy = XOpenDisplay(NULL)))
361                 die("slock: cannot open display\n");
362
363         /* drop privileges */
364         if (setgroups(0, NULL) < 0)
365                 die("slock: setgroups: %s\n", strerror(errno));
366         if (setgid(dgid) < 0)
367                 die("slock: setgid: %s\n", strerror(errno));
368         if (setuid(duid) < 0)
369                 die("slock: setuid: %s\n", strerror(errno));
370
371         /* check for Xrandr support */
372         rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
373
374         /* get number of screens in display "dpy" and blank them */
375         nscreens = ScreenCount(dpy);
376         if (!(locks = calloc(nscreens, sizeof(struct lock *))))
377                 die("slock: out of memory\n");
378         for (nlocks = 0, s = 0; s < nscreens; s++) {
379                 if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
380                         nlocks++;
381                 else
382                         break;
383         }
384         XSync(dpy, 0);
385
386         /* did we manage to lock everything? */
387         if (nlocks != nscreens)
388                 return 1;
389
390         /* run post-lock command */
391         if (argc > 0) {
392                 switch (fork()) {
393                 case -1:
394                         die("slock: fork failed: %s\n", strerror(errno));
395                 case 0:
396                         if (close(ConnectionNumber(dpy)) < 0)
397                                 die("slock: close: %s\n", strerror(errno));
398                         execvp(argv[0], argv);
399                         fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno));
400                         _exit(1);
401                 }
402         }
403
404         /* everything is now blank. Wait for the correct password */
405         readpw(dpy, &rr, locks, nscreens, hash);
406
407         return 0;
408 }