slock.c (15672B)
1 /* See LICENSE file for license details. */ 2 #define _XOPEN_SOURCE 500 3 #define LENGTH(X) (sizeof X / sizeof X[0]) 4 #if HAVE_SHADOW_H 5 #include <shadow.h> 6 #endif 7 8 #include <ctype.h> 9 #include <errno.h> 10 #include <math.h> 11 #include <grp.h> 12 #include <pwd.h> 13 #include <stdarg.h> 14 #include <stdlib.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <unistd.h> 18 #include <sys/types.h> 19 #include <X11/extensions/Xrandr.h> 20 #include <X11/extensions/dpms.h> 21 #ifdef XINERAMA 22 #include <X11/extensions/Xinerama.h> 23 #endif 24 #include <X11/keysym.h> 25 #include <X11/Xlib.h> 26 #include <X11/Xutil.h> 27 #include <X11/Xresource.h> 28 #include <X11/Xft/Xft.h> 29 #include <Imlib2.h> 30 31 #include "arg.h" 32 #include "util.h" 33 34 char *argv0; 35 int failtrack = 0; 36 37 enum { 38 INIT, 39 INPUT, 40 FAILED, 41 NUMCOLS 42 }; 43 44 /* Xresources preferences */ 45 enum resource_type { 46 STRING = 0, 47 INTEGER = 1, 48 FLOAT = 2 49 }; 50 51 typedef struct { 52 char *name; 53 enum resource_type type; 54 void *dst; 55 } ResourcePref; 56 57 #include "config.h" 58 59 struct lock { 60 int screen; 61 Window root, win; 62 Pixmap pmap; 63 Pixmap bgmap; 64 unsigned long colors[NUMCOLS]; 65 unsigned int x, y; 66 unsigned int xoff, yoff, mw, mh; 67 Drawable drawable; 68 GC gc; 69 XRectangle rectangles[LENGTH(rectangles)]; 70 }; 71 72 struct xrandr { 73 int active; 74 int evbase; 75 int errbase; 76 }; 77 78 Imlib_Image image; 79 80 static void 81 die(const char *errstr, ...) 82 { 83 va_list ap; 84 85 va_start(ap, errstr); 86 vfprintf(stderr, errstr, ap); 87 va_end(ap); 88 exit(1); 89 } 90 91 #ifdef __linux__ 92 #include <fcntl.h> 93 #include <linux/oom.h> 94 95 static void 96 dontkillme(void) 97 { 98 FILE *f; 99 const char oomfile[] = "/proc/self/oom_score_adj"; 100 101 if (!(f = fopen(oomfile, "w"))) { 102 if (errno == ENOENT) 103 return; 104 die("slock: fopen %s: %s\n", oomfile, strerror(errno)); 105 } 106 fprintf(f, "%d", OOM_SCORE_ADJ_MIN); 107 if (fclose(f)) { 108 if (errno == EACCES) 109 die("slock: unable to disable OOM killer. " 110 "Make sure to suid or sgid slock.\n"); 111 else 112 die("slock: fclose %s: %s\n", oomfile, strerror(errno)); 113 } 114 } 115 #endif 116 117 static const char * 118 gethash(void) 119 { 120 const char *hash; 121 struct passwd *pw; 122 123 /* Check if the current user has a password entry */ 124 errno = 0; 125 if (!(pw = getpwuid(getuid()))) { 126 if (errno) 127 die("slock: getpwuid: %s\n", strerror(errno)); 128 else 129 die("slock: cannot retrieve password entry\n"); 130 } 131 hash = pw->pw_passwd; 132 133 #if HAVE_SHADOW_H 134 if (!strcmp(hash, "x")) { 135 struct spwd *sp; 136 if (!(sp = getspnam(pw->pw_name))) 137 die("slock: getspnam: cannot retrieve shadow entry. " 138 "Make sure to suid or sgid slock.\n"); 139 hash = sp->sp_pwdp; 140 } 141 #else 142 if (!strcmp(hash, "*")) { 143 #ifdef __OpenBSD__ 144 if (!(pw = getpwuid_shadow(getuid()))) 145 die("slock: getpwnam_shadow: cannot retrieve shadow entry. " 146 "Make sure to suid or sgid slock.\n"); 147 hash = pw->pw_passwd; 148 #else 149 die("slock: getpwuid: cannot retrieve shadow entry. " 150 "Make sure to suid or sgid slock.\n"); 151 #endif /* __OpenBSD__ */ 152 } 153 #endif /* HAVE_SHADOW_H */ 154 155 return hash; 156 } 157 158 static void 159 resizerectangles(struct lock *lock) 160 { 161 int i; 162 163 for (i = 0; i < LENGTH(rectangles); i++){ 164 lock->rectangles[i].x = (rectangles[i].x * logosize) 165 + lock->xoff + ((lock->mw) / 2) - (logow / 2 * logosize); 166 lock->rectangles[i].y = (rectangles[i].y * logosize) 167 + lock->yoff + ((lock->mh) / 2) - (logoh / 2 * logosize); 168 lock->rectangles[i].width = rectangles[i].width * logosize; 169 lock->rectangles[i].height = rectangles[i].height * logosize; 170 } 171 } 172 173 static void 174 drawlogo(Display *dpy, struct lock *lock, int color) 175 { 176 /* 177 XSetForeground(dpy, lock->gc, lock->colors[BACKGROUND]); 178 XFillRectangle(dpy, lock->drawable, lock->gc, 0, 0, lock->x, lock->y); */ 179 lock->drawable = lock->bgmap; 180 XSetForeground(dpy, lock->gc, lock->colors[color]); 181 XFillRectangles(dpy, lock->drawable, lock->gc, lock->rectangles, LENGTH(rectangles)); 182 XCopyArea(dpy, lock->drawable, lock->win, lock->gc, 0, 0, lock->x, lock->y, 0, 0); 183 XSync(dpy, False); 184 } 185 186 static void 187 readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, 188 const char *hash) 189 { 190 XRRScreenChangeNotifyEvent *rre; 191 char buf[32], passwd[256], *inputhash; 192 int num, screen, running, failure, oldc; 193 unsigned int len, color; 194 KeySym ksym; 195 XEvent ev; 196 197 len = 0; 198 running = 1; 199 failure = 0; 200 oldc = INIT; 201 202 while (running && !XNextEvent(dpy, &ev)) { 203 if (ev.type == KeyPress) { 204 explicit_bzero(&buf, sizeof(buf)); 205 num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); 206 if (IsKeypadKey(ksym)) { 207 if (ksym == XK_KP_Enter) 208 ksym = XK_Return; 209 else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) 210 ksym = (ksym - XK_KP_0) + XK_0; 211 } 212 if (IsFunctionKey(ksym) || 213 IsKeypadKey(ksym) || 214 IsMiscFunctionKey(ksym) || 215 IsPFKey(ksym) || 216 IsPrivateKeypadKey(ksym)) 217 continue; 218 switch (ksym) { 219 case XK_Return: 220 passwd[len] = '\0'; 221 errno = 0; 222 if (!(inputhash = crypt(passwd, hash))) 223 fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); 224 else 225 running = !!strcmp(inputhash, hash); 226 if (running) { 227 XBell(dpy, 100); 228 failure = 1; 229 failtrack++; 230 231 if (failtrack >= failcount && failcount != 0){ 232 system(failcommand); 233 } 234 } 235 explicit_bzero(&passwd, sizeof(passwd)); 236 len = 0; 237 break; 238 case XK_Escape: 239 explicit_bzero(&passwd, sizeof(passwd)); 240 len = 0; 241 break; 242 case XK_BackSpace: 243 if (len) 244 passwd[--len] = '\0'; 245 break; 246 default: 247 if (num && !iscntrl((int)buf[0]) && 248 (len + num < sizeof(passwd))) { 249 memcpy(passwd + len, buf, num); 250 len += num; 251 } 252 break; 253 } 254 color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); 255 if (running && oldc != color) { 256 for (screen = 0; screen < nscreens; screen++) { 257 drawlogo(dpy, locks[screen], color); 258 } 259 oldc = color; 260 } 261 } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { 262 rre = (XRRScreenChangeNotifyEvent*)&ev; 263 for (screen = 0; screen < nscreens; screen++) { 264 if (locks[screen]->win == rre->window) { 265 if (rre->rotation == RR_Rotate_90 || 266 rre->rotation == RR_Rotate_270) 267 XResizeWindow(dpy, locks[screen]->win, 268 rre->height, rre->width); 269 else 270 XResizeWindow(dpy, locks[screen]->win, 271 rre->width, rre->height); 272 XClearWindow(dpy, locks[screen]->win); 273 break; 274 } 275 } 276 } else { 277 for (screen = 0; screen < nscreens; screen++) 278 XRaiseWindow(dpy, locks[screen]->win); 279 } 280 } 281 } 282 283 static struct lock * 284 lockscreen(Display *dpy, struct xrandr *rr, int screen) 285 { 286 char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; 287 int i, ptgrab, kbgrab; 288 struct lock *lock; 289 XColor color, dummy; 290 XSetWindowAttributes wa; 291 Cursor invisible; 292 #ifdef XINERAMA 293 XineramaScreenInfo *info; 294 int n; 295 #endif 296 297 if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock)))) 298 return NULL; 299 300 lock->screen = screen; 301 lock->root = RootWindow(dpy, lock->screen); 302 303 if (image) { 304 lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen)); 305 imlib_context_set_image(image); 306 imlib_context_set_display(dpy); 307 imlib_context_set_visual(DefaultVisual(dpy, lock->screen)); 308 imlib_context_set_colormap(DefaultColormap(dpy, lock->screen)); 309 imlib_context_set_drawable(lock->bgmap); 310 imlib_render_image_on_drawable(0, 0); 311 imlib_free_image(); 312 } 313 314 for (i = 0; i < NUMCOLS; i++) { 315 XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), 316 colorname[i], &color, &dummy); 317 lock->colors[i] = color.pixel; 318 } 319 320 lock->x = DisplayWidth(dpy, lock->screen); 321 lock->y = DisplayHeight(dpy, lock->screen); 322 #ifdef XINERAMA 323 if ((info = XineramaQueryScreens(dpy, &n))) { 324 lock->xoff = info[0].x_org; 325 lock->yoff = info[0].y_org; 326 lock->mw = info[0].width; 327 lock->mh = info[0].height; 328 } else 329 #endif 330 { 331 lock->xoff = lock->yoff = 0; 332 lock->mw = lock->x; 333 lock->mh = lock->y; 334 } 335 lock->drawable = XCreatePixmap(dpy, lock->root, 336 lock->x, lock->y, DefaultDepth(dpy, screen)); 337 lock->gc = XCreateGC(dpy, lock->root, 0, NULL); 338 XSetLineAttributes(dpy, lock->gc, 1, LineSolid, CapButt, JoinMiter); 339 340 /* init */ 341 wa.override_redirect = 1; 342 lock->win = XCreateWindow(dpy, lock->root, 0, 0, 343 lock->x, lock->y, 344 0, DefaultDepth(dpy, lock->screen), 345 CopyFromParent, 346 DefaultVisual(dpy, lock->screen), 347 CWOverrideRedirect | CWBackPixel, &wa); 348 if (lock->bgmap) 349 XSetWindowBackgroundPixmap(dpy, lock->win, lock->bgmap); 350 351 lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); 352 invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, 353 &color, &color, 0, 0); 354 XDefineCursor(dpy, lock->win, invisible); 355 356 resizerectangles(lock); 357 358 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ 359 for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { 360 if (ptgrab != GrabSuccess) { 361 ptgrab = XGrabPointer(dpy, lock->root, False, 362 ButtonPressMask | ButtonReleaseMask | 363 PointerMotionMask, GrabModeAsync, 364 GrabModeAsync, None, invisible, CurrentTime); 365 } 366 if (kbgrab != GrabSuccess) { 367 kbgrab = XGrabKeyboard(dpy, lock->root, True, 368 GrabModeAsync, GrabModeAsync, CurrentTime); 369 } 370 371 /* input is grabbed: we can lock the screen */ 372 if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) { 373 XMapRaised(dpy, lock->win); 374 if (rr->active) 375 XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask); 376 377 XSelectInput(dpy, lock->root, SubstructureNotifyMask); 378 drawlogo(dpy, lock, INIT); 379 return lock; 380 } 381 382 /* retry on AlreadyGrabbed but fail on other errors */ 383 if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) || 384 (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess)) 385 break; 386 387 usleep(100000); 388 } 389 390 /* we couldn't grab all input: fail out */ 391 if (ptgrab != GrabSuccess) 392 fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", 393 screen); 394 if (kbgrab != GrabSuccess) 395 fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", 396 screen); 397 return NULL; 398 } 399 400 int 401 resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) 402 { 403 char **sdst = dst; 404 int *idst = dst; 405 float *fdst = dst; 406 407 char fullname[256]; 408 char fullclass[256]; 409 char *type; 410 XrmValue ret; 411 412 snprintf(fullname, sizeof(fullname), "%s.%s", "slock", name); 413 snprintf(fullclass, sizeof(fullclass), "%s.%s", "Slock", name); 414 fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; 415 416 XrmGetResource(db, fullname, fullclass, &type, &ret); 417 if (ret.addr == NULL || strncmp("String", type, 64)) 418 return 1; 419 420 switch (rtype) { 421 case STRING: 422 *sdst = ret.addr; 423 break; 424 case INTEGER: 425 *idst = strtoul(ret.addr, NULL, 10); 426 break; 427 case FLOAT: 428 *fdst = strtof(ret.addr, NULL); 429 break; 430 } 431 return 0; 432 } 433 434 void 435 config_init(Display *dpy) 436 { 437 char *resm; 438 XrmDatabase db; 439 ResourcePref *p; 440 441 XrmInitialize(); 442 resm = XResourceManagerString(dpy); 443 if (!resm) 444 return; 445 446 db = XrmGetStringDatabase(resm); 447 for (p = resources; p < resources + LEN(resources); p++) 448 resource_load(db, p->name, p->type, p->dst); 449 } 450 451 static void 452 usage(void) 453 { 454 die("usage: slock [-v] [cmd [arg ...]]\n"); 455 } 456 457 int 458 main(int argc, char **argv) { 459 struct xrandr rr; 460 struct lock **locks; 461 struct passwd *pwd; 462 struct group *grp; 463 uid_t duid; 464 gid_t dgid; 465 const char *hash; 466 Display *dpy; 467 int s, nlocks, nscreens; 468 CARD16 standby, suspend, off; 469 470 ARGBEGIN { 471 case 'v': 472 fprintf(stderr, "slock-"VERSION"\n"); 473 return 0; 474 default: 475 usage(); 476 } ARGEND 477 478 /* validate drop-user and -group */ 479 errno = 0; 480 if (!(pwd = getpwnam(user))) 481 die("slock: getpwnam %s: %s\n", user, 482 errno ? strerror(errno) : "user entry not found"); 483 duid = pwd->pw_uid; 484 errno = 0; 485 if (!(grp = getgrnam(group))) 486 die("slock: getgrnam %s: %s\n", group, 487 errno ? strerror(errno) : "group entry not found"); 488 dgid = grp->gr_gid; 489 490 #ifdef __linux__ 491 dontkillme(); 492 #endif 493 494 hash = gethash(); 495 errno = 0; 496 if (!crypt("", hash)) 497 die("slock: crypt: %s\n", strerror(errno)); 498 499 if (!(dpy = XOpenDisplay(NULL))) 500 die("slock: cannot open display\n"); 501 502 /* drop privileges */ 503 if (setgroups(0, NULL) < 0) 504 die("slock: setgroups: %s\n", strerror(errno)); 505 if (setgid(dgid) < 0) 506 die("slock: setgid: %s\n", strerror(errno)); 507 if (setuid(duid) < 0) 508 die("slock: setuid: %s\n", strerror(errno)); 509 510 /*Create screenshot Image*/ 511 Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); 512 image = imlib_create_image(scr->width,scr->height); 513 imlib_context_set_image(image); 514 imlib_context_set_display(dpy); 515 imlib_context_set_visual(DefaultVisual(dpy,0)); 516 imlib_context_set_drawable(RootWindow(dpy,XScreenNumberOfScreen(scr))); 517 imlib_copy_drawable_to_image(0,0,0,scr->width,scr->height,0,0,1); 518 519 #ifdef BLUR 520 521 /*Blur function*/ 522 imlib_image_blur(blurRadius); 523 #endif // BLUR 524 525 #ifdef PIXELATION 526 /*Pixelation*/ 527 int width = scr->width; 528 int height = scr->height; 529 530 for(int y = 0; y < height; y += pixelSize) 531 { 532 for(int x = 0; x < width; x += pixelSize) 533 { 534 int red = 0; 535 int green = 0; 536 int blue = 0; 537 538 Imlib_Color pixel; 539 Imlib_Color* pp; 540 pp = &pixel; 541 for(int j = 0; j < pixelSize && j < height; j++) 542 { 543 for(int i = 0; i < pixelSize && i < width; i++) 544 { 545 imlib_image_query_pixel(x+i,y+j,pp); 546 red += pixel.red; 547 green += pixel.green; 548 blue += pixel.blue; 549 } 550 } 551 red /= (pixelSize*pixelSize); 552 green /= (pixelSize*pixelSize); 553 blue /= (pixelSize*pixelSize); 554 imlib_context_set_color(red,green,blue,pixel.alpha); 555 imlib_image_fill_rectangle(x,y,pixelSize,pixelSize); 556 red = 0; 557 green = 0; 558 blue = 0; 559 } 560 } 561 562 563 #endif 564 565 config_init(dpy); 566 567 /* check for Xrandr support */ 568 rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); 569 570 /* get number of screens in display "dpy" and blank them */ 571 nscreens = ScreenCount(dpy); 572 if (!(locks = calloc(nscreens, sizeof(struct lock *)))) 573 die("slock: out of memory\n"); 574 for (nlocks = 0, s = 0; s < nscreens; s++) { 575 if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) 576 nlocks++; 577 else 578 break; 579 } 580 XSync(dpy, 0); 581 582 /* did we manage to lock everything? */ 583 if (nlocks != nscreens) 584 return 1; 585 586 /* DPMS magic to disable the monitor */ 587 if (!DPMSCapable(dpy)) 588 die("slock: DPMSCapable failed\n"); 589 if (!DPMSEnable(dpy)) 590 die("slock: DPMSEnable failed\n"); 591 if (!DPMSGetTimeouts(dpy, &standby, &suspend, &off)) 592 die("slock: DPMSGetTimeouts failed\n"); 593 if (!standby || !suspend || !off) 594 die("slock: at least one DPMS variable is zero\n"); 595 if (!DPMSSetTimeouts(dpy, monitortime, monitortime, monitortime)) 596 die("slock: DPMSSetTimeouts failed\n"); 597 598 XSync(dpy, 0); 599 600 /* run post-lock command */ 601 if (argc > 0) { 602 switch (fork()) { 603 case -1: 604 die("slock: fork failed: %s\n", strerror(errno)); 605 case 0: 606 if (close(ConnectionNumber(dpy)) < 0) 607 die("slock: close: %s\n", strerror(errno)); 608 execvp(argv[0], argv); 609 fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno)); 610 _exit(1); 611 } 612 } 613 614 /* everything is now blank. Wait for the correct password */ 615 readpw(dpy, &rr, locks, nscreens, hash); 616 617 for (nlocks = 0, s = 0; s < nscreens; s++) { 618 XFreePixmap(dpy, locks[s]->drawable); 619 XFreeGC(dpy, locks[s]->gc); 620 } 621 622 /* reset DPMS values to inital ones */ 623 DPMSSetTimeouts(dpy, standby, suspend, off); 624 XSync(dpy, 0); 625 XCloseDisplay(dpy); 626 return 0; 627 }