This is my configuration of slock - the suckless X screen locker.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
10 KiB

9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
  1. /* See LICENSE file for license details. */
  2. #define _XOPEN_SOURCE 500
  3. #if HAVE_SHADOW_H
  4. #include <shadow.h>
  5. #endif
  6. #include <ctype.h>
  7. #include <errno.h>
  8. #include <grp.h>
  9. #include <pwd.h>
  10. #include <stdarg.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <sys/types.h>
  16. #include <X11/extensions/Xrandr.h>
  17. #include <X11/extensions/dpms.h>
  18. #include <X11/keysym.h>
  19. #include <X11/Xlib.h>
  20. #include <X11/Xutil.h>
  21. #include "arg.h"
  22. #include "util.h"
  23. char *argv0;
  24. enum {
  25. INIT,
  26. INPUT,
  27. FAILED,
  28. NUMCOLS
  29. };
  30. struct lock {
  31. int screen;
  32. Window root, win;
  33. Pixmap pmap;
  34. unsigned long colors[NUMCOLS];
  35. };
  36. struct xrandr {
  37. int active;
  38. int evbase;
  39. int errbase;
  40. };
  41. #include "config.h"
  42. static void
  43. die(const char *errstr, ...)
  44. {
  45. va_list ap;
  46. va_start(ap, errstr);
  47. vfprintf(stderr, errstr, ap);
  48. va_end(ap);
  49. exit(1);
  50. }
  51. #ifdef __linux__
  52. #include <fcntl.h>
  53. #include <linux/oom.h>
  54. static void
  55. dontkillme(void)
  56. {
  57. FILE *f;
  58. const char oomfile[] = "/proc/self/oom_score_adj";
  59. if (!(f = fopen(oomfile, "w"))) {
  60. if (errno == ENOENT)
  61. return;
  62. die("slock: fopen %s: %s\n", oomfile, strerror(errno));
  63. }
  64. fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
  65. if (fclose(f)) {
  66. if (errno == EACCES)
  67. die("slock: unable to disable OOM killer. "
  68. "Make sure to suid or sgid slock.\n");
  69. else
  70. die("slock: fclose %s: %s\n", oomfile, strerror(errno));
  71. }
  72. }
  73. #endif
  74. static const char *
  75. gethash(void)
  76. {
  77. const char *hash;
  78. struct passwd *pw;
  79. /* Check if the current user has a password entry */
  80. errno = 0;
  81. if (!(pw = getpwuid(getuid()))) {
  82. if (errno)
  83. die("slock: getpwuid: %s\n", strerror(errno));
  84. else
  85. die("slock: cannot retrieve password entry\n");
  86. }
  87. hash = pw->pw_passwd;
  88. #if HAVE_SHADOW_H
  89. if (!strcmp(hash, "x")) {
  90. struct spwd *sp;
  91. if (!(sp = getspnam(pw->pw_name)))
  92. die("slock: getspnam: cannot retrieve shadow entry. "
  93. "Make sure to suid or sgid slock.\n");
  94. hash = sp->sp_pwdp;
  95. }
  96. #else
  97. if (!strcmp(hash, "*")) {
  98. #ifdef __OpenBSD__
  99. if (!(pw = getpwuid_shadow(getuid())))
  100. die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
  101. "Make sure to suid or sgid slock.\n");
  102. hash = pw->pw_passwd;
  103. #else
  104. die("slock: getpwuid: cannot retrieve shadow entry. "
  105. "Make sure to suid or sgid slock.\n");
  106. #endif /* __OpenBSD__ */
  107. }
  108. #endif /* HAVE_SHADOW_H */
  109. return hash;
  110. }
  111. static void
  112. readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
  113. const char *hash)
  114. {
  115. XRRScreenChangeNotifyEvent *rre;
  116. char buf[32], passwd[256], *inputhash;
  117. int num, screen, running, failure, oldc;
  118. unsigned int len, color;
  119. KeySym ksym;
  120. XEvent ev;
  121. len = 0;
  122. running = 1;
  123. failure = 0;
  124. oldc = INIT;
  125. while (running && !XNextEvent(dpy, &ev)) {
  126. if (ev.type == KeyPress) {
  127. explicit_bzero(&buf, sizeof(buf));
  128. num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
  129. if (IsKeypadKey(ksym)) {
  130. if (ksym == XK_KP_Enter)
  131. ksym = XK_Return;
  132. else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
  133. ksym = (ksym - XK_KP_0) + XK_0;
  134. }
  135. if (IsFunctionKey(ksym) ||
  136. IsKeypadKey(ksym) ||
  137. IsMiscFunctionKey(ksym) ||
  138. IsPFKey(ksym) ||
  139. IsPrivateKeypadKey(ksym))
  140. continue;
  141. switch (ksym) {
  142. case XK_Return:
  143. passwd[len] = '\0';
  144. errno = 0;
  145. if (!(inputhash = crypt(passwd, hash)))
  146. fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
  147. else
  148. running = !!strcmp(inputhash, hash);
  149. if (running) {
  150. XBell(dpy, 100);
  151. failure = 1;
  152. }
  153. explicit_bzero(&passwd, sizeof(passwd));
  154. len = 0;
  155. break;
  156. case XK_Escape:
  157. explicit_bzero(&passwd, sizeof(passwd));
  158. len = 0;
  159. break;
  160. case XK_BackSpace:
  161. if (len)
  162. passwd[--len] = '\0';
  163. break;
  164. default:
  165. if (num && !iscntrl((int)buf[0]) &&
  166. (len + num < sizeof(passwd))) {
  167. memcpy(passwd + len, buf, num);
  168. len += num;
  169. }
  170. break;
  171. }
  172. color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT);
  173. if (running && oldc != color) {
  174. for (screen = 0; screen < nscreens; screen++) {
  175. XSetWindowBackground(dpy,
  176. locks[screen]->win,
  177. locks[screen]->colors[color]);
  178. XClearWindow(dpy, locks[screen]->win);
  179. }
  180. oldc = color;
  181. }
  182. } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
  183. rre = (XRRScreenChangeNotifyEvent*)&ev;
  184. for (screen = 0; screen < nscreens; screen++) {
  185. if (locks[screen]->win == rre->window) {
  186. if (rre->rotation == RR_Rotate_90 ||
  187. rre->rotation == RR_Rotate_270)
  188. XResizeWindow(dpy, locks[screen]->win,
  189. rre->height, rre->width);
  190. else
  191. XResizeWindow(dpy, locks[screen]->win,
  192. rre->width, rre->height);
  193. XClearWindow(dpy, locks[screen]->win);
  194. break;
  195. }
  196. }
  197. } else {
  198. for (screen = 0; screen < nscreens; screen++)
  199. XRaiseWindow(dpy, locks[screen]->win);
  200. }
  201. }
  202. }
  203. static struct lock *
  204. lockscreen(Display *dpy, struct xrandr *rr, int screen)
  205. {
  206. char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
  207. int i, ptgrab, kbgrab;
  208. struct lock *lock;
  209. XColor color, dummy;
  210. XSetWindowAttributes wa;
  211. Cursor invisible;
  212. if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
  213. return NULL;
  214. lock->screen = screen;
  215. lock->root = RootWindow(dpy, lock->screen);
  216. for (i = 0; i < NUMCOLS; i++) {
  217. XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
  218. colorname[i], &color, &dummy);
  219. lock->colors[i] = color.pixel;
  220. }
  221. /* init */
  222. wa.override_redirect = 1;
  223. wa.background_pixel = lock->colors[INIT];
  224. lock->win = XCreateWindow(dpy, lock->root, 0, 0,
  225. DisplayWidth(dpy, lock->screen),
  226. DisplayHeight(dpy, lock->screen),
  227. 0, DefaultDepth(dpy, lock->screen),
  228. CopyFromParent,
  229. DefaultVisual(dpy, lock->screen),
  230. CWOverrideRedirect | CWBackPixel, &wa);
  231. lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
  232. invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
  233. &color, &color, 0, 0);
  234. XDefineCursor(dpy, lock->win, invisible);
  235. /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
  236. for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
  237. if (ptgrab != GrabSuccess) {
  238. ptgrab = XGrabPointer(dpy, lock->root, False,
  239. ButtonPressMask | ButtonReleaseMask |
  240. PointerMotionMask, GrabModeAsync,
  241. GrabModeAsync, None, invisible, CurrentTime);
  242. }
  243. if (kbgrab != GrabSuccess) {
  244. kbgrab = XGrabKeyboard(dpy, lock->root, True,
  245. GrabModeAsync, GrabModeAsync, CurrentTime);
  246. }
  247. /* input is grabbed: we can lock the screen */
  248. if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
  249. XMapRaised(dpy, lock->win);
  250. if (rr->active)
  251. XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
  252. XSelectInput(dpy, lock->root, SubstructureNotifyMask);
  253. return lock;
  254. }
  255. /* retry on AlreadyGrabbed but fail on other errors */
  256. if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
  257. (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
  258. break;
  259. usleep(100000);
  260. }
  261. /* we couldn't grab all input: fail out */
  262. if (ptgrab != GrabSuccess)
  263. fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
  264. screen);
  265. if (kbgrab != GrabSuccess)
  266. fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
  267. screen);
  268. return NULL;
  269. }
  270. static void
  271. usage(void)
  272. {
  273. die("usage: slock [-v] [cmd [arg ...]]\n");
  274. }
  275. int
  276. main(int argc, char **argv) {
  277. struct xrandr rr;
  278. struct lock **locks;
  279. struct passwd *pwd;
  280. struct group *grp;
  281. uid_t duid;
  282. gid_t dgid;
  283. const char *hash;
  284. Display *dpy;
  285. int s, nlocks, nscreens;
  286. CARD16 standby, suspend, off;
  287. ARGBEGIN {
  288. case 'v':
  289. fprintf(stderr, "slock-"VERSION"\n");
  290. return 0;
  291. default:
  292. usage();
  293. } ARGEND
  294. /* validate drop-user and -group */
  295. errno = 0;
  296. if (!(pwd = getpwnam(user)))
  297. die("slock: getpwnam %s: %s\n", user,
  298. errno ? strerror(errno) : "user entry not found");
  299. duid = pwd->pw_uid;
  300. errno = 0;
  301. if (!(grp = getgrnam(group)))
  302. die("slock: getgrnam %s: %s\n", group,
  303. errno ? strerror(errno) : "group entry not found");
  304. dgid = grp->gr_gid;
  305. #ifdef __linux__
  306. dontkillme();
  307. #endif
  308. hash = gethash();
  309. errno = 0;
  310. if (!crypt("", hash))
  311. die("slock: crypt: %s\n", strerror(errno));
  312. if (!(dpy = XOpenDisplay(NULL)))
  313. die("slock: cannot open display\n");
  314. /* drop privileges */
  315. if (setgroups(0, NULL) < 0)
  316. die("slock: setgroups: %s\n", strerror(errno));
  317. if (setgid(dgid) < 0)
  318. die("slock: setgid: %s\n", strerror(errno));
  319. if (setuid(duid) < 0)
  320. die("slock: setuid: %s\n", strerror(errno));
  321. /* check for Xrandr support */
  322. rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
  323. /* get number of screens in display "dpy" and blank them */
  324. nscreens = ScreenCount(dpy);
  325. if (!(locks = calloc(nscreens, sizeof(struct lock *))))
  326. die("slock: out of memory\n");
  327. for (nlocks = 0, s = 0; s < nscreens; s++) {
  328. if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
  329. nlocks++;
  330. else
  331. break;
  332. }
  333. XSync(dpy, 0);
  334. /* did we manage to lock everything? */
  335. if (nlocks != nscreens)
  336. return 1;
  337. /* DPMS magic to disable the monitor */
  338. if (!DPMSCapable(dpy))
  339. die("slock: DPMSCapable failed\n");
  340. if (!DPMSEnable(dpy))
  341. die("slock: DPMSEnable failed\n");
  342. if (!DPMSGetTimeouts(dpy, &standby, &suspend, &off))
  343. die("slock: DPMSGetTimeouts failed\n");
  344. if (!standby || !suspend || !off)
  345. die("slock: at least one DPMS variable is zero\n");
  346. if (!DPMSSetTimeouts(dpy, monitortime, monitortime, monitortime))
  347. die("slock: DPMSSetTimeouts failed\n");
  348. XSync(dpy, 0);
  349. /* run post-lock command */
  350. if (argc > 0) {
  351. switch (fork()) {
  352. case -1:
  353. die("slock: fork failed: %s\n", strerror(errno));
  354. case 0:
  355. if (close(ConnectionNumber(dpy)) < 0)
  356. die("slock: close: %s\n", strerror(errno));
  357. execvp(argv[0], argv);
  358. fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno));
  359. _exit(1);
  360. }
  361. }
  362. /* everything is now blank. Wait for the correct password */
  363. readpw(dpy, &rr, locks, nscreens, hash);
  364. /* reset DPMS values to inital ones */
  365. DPMSSetTimeouts(dpy, standby, suspend, off);
  366. XSync(dpy, 0);
  367. return 0;
  368. }