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