slock

my customized slock build
git clone git://git.hanetzok.net/slock
Log | Files | Refs | README | LICENSE

slock.c (13845B)


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