slock.c (17205B)
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 (num && !iscntrl((int)buf[0]) && 320 // (len + num < sizeof(passwd))) { 321 if (ksym == XK_u && ev.xkey.state & ControlMask) { 322 printf("Clearing!\n"); 323 explicit_bzero(&passwd, sizeof(passwd)); 324 len = 0; 325 continue; 326 } 327 if (controlkeyclear && iscntrl((int)buf[0])) 328 continue; 329 if (num && (len + num < sizeof(passwd))) { 330 memcpy(passwd + len, buf, num); 331 len += num; 332 } 333 break; 334 } 335 color = len ? (len%2 ? INPUT : INPUT_ALT) 336 : ((failure || failonclear) ? FAILED : INIT); 337 if (running && oldc != color) { 338 for (screen = 0; screen < nscreens; screen++) { 339 drawlogo(dpy, locks[screen], color); 340 writemessage(dpy, locks[screen]->win, screen); 341 } 342 oldc = color; 343 } 344 } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { 345 rre = (XRRScreenChangeNotifyEvent*)&ev; 346 for (screen = 0; screen < nscreens; screen++) { 347 if (locks[screen]->win == rre->window) { 348 if (rre->rotation == RR_Rotate_90 || 349 rre->rotation == RR_Rotate_270) 350 XResizeWindow(dpy, locks[screen]->win, 351 rre->height, rre->width); 352 else 353 XResizeWindow(dpy, locks[screen]->win, 354 rre->width, rre->height); 355 XClearWindow(dpy, locks[screen]->win); 356 break; 357 } 358 } 359 } else { 360 for (screen = 0; screen < nscreens; screen++) 361 XRaiseWindow(dpy, locks[screen]->win); 362 } 363 } 364 } 365 366 static struct lock * 367 lockscreen(Display *dpy, struct xrandr *rr, int screen) 368 { 369 char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; 370 int i, ptgrab, kbgrab; 371 struct lock *lock; 372 XColor color, dummy; 373 XSetWindowAttributes wa; 374 Cursor invisible; 375 #ifdef XINERAMA 376 XineramaScreenInfo *info; 377 int n; 378 #endif 379 380 if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock)))) 381 return NULL; 382 383 lock->screen = screen; 384 lock->root = RootWindow(dpy, lock->screen); 385 386 if(image) 387 { 388 lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen)); 389 imlib_context_set_image(image); 390 imlib_context_set_display(dpy); 391 imlib_context_set_visual(DefaultVisual(dpy, lock->screen)); 392 imlib_context_set_colormap(DefaultColormap(dpy, lock->screen)); 393 imlib_context_set_drawable(lock->bgmap); 394 imlib_render_image_on_drawable(0, 0); 395 imlib_free_image(); 396 } 397 398 for (i = 0; i < NUMCOLS; i++) { 399 XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), 400 colorname[i], &color, &dummy); 401 lock->colors[i] = color.pixel; 402 } 403 404 lock->x = DisplayWidth(dpy, lock->screen); 405 lock->y = DisplayHeight(dpy, lock->screen); 406 #ifdef XINERAMA 407 if ((info = XineramaQueryScreens(dpy, &n))) { 408 lock->xoff = info[0].x_org; 409 lock->yoff = info[0].y_org; 410 lock->mw = info[0].width; 411 lock->mh = info[0].height; 412 } else 413 #endif 414 { 415 lock->xoff = lock->yoff = 0; 416 lock->mw = lock->x; 417 lock->mh = lock->y; 418 } 419 lock->drawable = XCreatePixmap(dpy, lock->root, 420 lock->x, lock->y, DefaultDepth(dpy, screen)); 421 lock->gc = XCreateGC(dpy, lock->root, 0, NULL); 422 XSetLineAttributes(dpy, lock->gc, 1, LineSolid, CapButt, JoinMiter); 423 424 425 /* init */ 426 wa.override_redirect = 1; 427 lock->win = XCreateWindow(dpy, lock->root, 0, 0, 428 lock->x, lock->y, 429 0, DefaultDepth(dpy, lock->screen), 430 CopyFromParent, 431 DefaultVisual(dpy, lock->screen), 432 CWOverrideRedirect | CWBackPixel, &wa); 433 if(lock->bgmap) 434 XSetWindowBackgroundPixmap(dpy, lock->win, lock->bgmap); 435 lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); 436 invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, 437 &color, &color, 0, 0); 438 XDefineCursor(dpy, lock->win, invisible); 439 440 resizerectangles(lock); 441 442 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ 443 for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { 444 if (ptgrab != GrabSuccess) { 445 ptgrab = XGrabPointer(dpy, lock->root, False, 446 ButtonPressMask | ButtonReleaseMask | 447 PointerMotionMask, GrabModeAsync, 448 GrabModeAsync, None, invisible, CurrentTime); 449 } 450 if (kbgrab != GrabSuccess) { 451 kbgrab = XGrabKeyboard(dpy, lock->root, True, 452 GrabModeAsync, GrabModeAsync, CurrentTime); 453 } 454 455 /* input is grabbed: we can lock the screen */ 456 if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) { 457 XMapRaised(dpy, lock->win); 458 if (rr->active) 459 XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask); 460 461 XSelectInput(dpy, lock->root, SubstructureNotifyMask); 462 drawlogo(dpy, lock, INIT); 463 return lock; 464 } 465 466 /* retry on AlreadyGrabbed but fail on other errors */ 467 if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) || 468 (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess)) 469 break; 470 471 usleep(100000); 472 } 473 474 /* we couldn't grab all input: fail out */ 475 if (ptgrab != GrabSuccess) 476 fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", 477 screen); 478 if (kbgrab != GrabSuccess) 479 fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", 480 screen); 481 return NULL; 482 } 483 484 static void 485 usage(void) 486 { 487 die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n"); 488 489 } 490 491 int 492 main(int argc, char **argv) { 493 struct xrandr rr; 494 struct lock **locks; 495 struct passwd *pwd; 496 struct group *grp; 497 uid_t duid; 498 gid_t dgid; 499 const char *hash; 500 Display *dpy; 501 int i, s, nlocks, nscreens; 502 int count_fonts; 503 char **font_names; 504 CARD16 standby, suspend, off; 505 BOOL dpms_state; 506 507 ARGBEGIN { 508 case 'v': 509 puts("slock-"VERSION); 510 return 0; 511 case 'm': 512 message = EARGF(usage()); 513 break; 514 case 'f': 515 if (!(dpy = XOpenDisplay(NULL))) 516 die("slock: cannot open display\n"); 517 font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts); 518 for (i=0; i<count_fonts; i++) { 519 fprintf(stderr, "%s\n", *(font_names+i)); 520 } 521 return 0; 522 default: 523 usage(); 524 } ARGEND 525 526 /* validate drop-user and -group */ 527 errno = 0; 528 if (!(pwd = getpwnam(user))) 529 die("slock: getpwnam %s: %s\n", user, 530 errno ? strerror(errno) : "user entry not found"); 531 duid = pwd->pw_uid; 532 errno = 0; 533 if (!(grp = getgrnam(group))) 534 die("slock: getgrnam %s: %s\n", group, 535 errno ? strerror(errno) : "group entry not found"); 536 dgid = grp->gr_gid; 537 538 #ifdef __linux__ 539 dontkillme(); 540 #endif 541 542 hash = gethash(); 543 errno = 0; 544 if (!crypt("", hash)) 545 die("slock: crypt: %s\n", strerror(errno)); 546 547 if (!(dpy = XOpenDisplay(NULL))) 548 die("slock: cannot open display\n"); 549 550 /* drop privileges */ 551 if (setgroups(0, NULL) < 0) 552 die("slock: setgroups: %s\n", strerror(errno)); 553 if (setgid(dgid) < 0) 554 die("slock: setgid: %s\n", strerror(errno)); 555 if (setuid(duid) < 0) 556 die("slock: setuid: %s\n", strerror(errno)); 557 558 /*Create screenshot Image*/ 559 Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); 560 image = imlib_create_image(scr->width,scr->height); 561 imlib_context_set_image(image); 562 imlib_context_set_display(dpy); 563 imlib_context_set_visual(DefaultVisual(dpy,0)); 564 imlib_context_set_drawable(RootWindow(dpy,XScreenNumberOfScreen(scr))); 565 imlib_copy_drawable_to_image(0,0,0,scr->width,scr->height,0,0,1); 566 567 #ifdef BLUR 568 569 /*Blur function*/ 570 imlib_image_blur(blurRadius); 571 #endif // BLUR 572 573 #ifdef PIXELATION 574 /*Pixelation*/ 575 int width = scr->width; 576 int height = scr->height; 577 578 for(int y = 0; y < height; y += pixelSize) 579 { 580 for(int x = 0; x < width; x += pixelSize) 581 { 582 int red = 0; 583 int green = 0; 584 int blue = 0; 585 586 Imlib_Color pixel; 587 Imlib_Color* pp; 588 pp = &pixel; 589 for(int j = 0; j < pixelSize && j < height; j++) 590 { 591 for(int i = 0; i < pixelSize && i < width; i++) 592 { 593 imlib_image_query_pixel(x+i,y+j,pp); 594 red += pixel.red; 595 green += pixel.green; 596 blue += pixel.blue; 597 } 598 } 599 red /= (pixelSize*pixelSize); 600 green /= (pixelSize*pixelSize); 601 blue /= (pixelSize*pixelSize); 602 imlib_context_set_color(red,green,blue,pixel.alpha); 603 imlib_image_fill_rectangle(x,y,pixelSize,pixelSize); 604 red = 0; 605 green = 0; 606 blue = 0; 607 } 608 } 609 610 611 #endif 612 613 /* check for Xrandr support */ 614 rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); 615 616 /* get number of screens in display "dpy" and blank them */ 617 nscreens = ScreenCount(dpy); 618 if (!(locks = calloc(nscreens, sizeof(struct lock *)))) 619 die("slock: out of memory\n"); 620 for (nlocks = 0, s = 0; s < nscreens; s++) { 621 if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) { 622 writemessage(dpy, locks[s]->win, s); 623 nlocks++; 624 } else { 625 break; 626 } 627 } 628 XSync(dpy, 0); 629 630 /* did we manage to lock everything? */ 631 if (nlocks != nscreens) 632 return 1; 633 634 /* DPMS magic to disable the monitor */ 635 if (!DPMSCapable(dpy)) 636 die("slock: DPMSCapable failed\n"); 637 if (!DPMSInfo(dpy, &standby, &dpms_state)) 638 die("slock: DPMSInfo failed\n"); 639 if (!DPMSEnable(dpy) && !dpms_state) 640 die("slock: DPMSEnable failed\n"); 641 if (!DPMSGetTimeouts(dpy, &standby, &suspend, &off)) 642 die("slock: DPMSGetTimeouts failed\n"); 643 if (!standby || !suspend || !off) 644 die("slock: at least one DPMS variable is zero\n"); 645 if (!DPMSSetTimeouts(dpy, monitortime, monitortime, monitortime)) 646 die("slock: DPMSSetTimeouts failed\n"); 647 648 XSync(dpy, 0); 649 650 651 /* run post-lock command */ 652 if (argc > 0) { 653 switch (fork()) { 654 case -1: 655 die("slock: fork failed: %s\n", strerror(errno)); 656 case 0: 657 if (close(ConnectionNumber(dpy)) < 0) 658 die("slock: close: %s\n", strerror(errno)); 659 execvp(argv[0], argv); 660 fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno)); 661 _exit(1); 662 } 663 } 664 665 /* everything is now blank. Wait for the correct password */ 666 readpw(dpy, &rr, locks, nscreens, hash); 667 668 for (nlocks = 0, s = 0; s < nscreens; s++) { 669 XFreePixmap(dpy, locks[s]->drawable); 670 XFreeGC(dpy, locks[s]->gc); 671 } 672 673 /* reset DPMS values to inital ones */ 674 DPMSSetTimeouts(dpy, standby, suspend, off); 675 if (!dpms_state) 676 DPMSDisable(dpy); 677 XSync(dpy, 0); 678 679 XCloseDisplay(dpy); 680 681 return 0; 682 }