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