Another copy of my dotfiles. Because I don't completely trust GitHub.
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.

535 lines
12 KiB

  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/Xinerama.h>
  18. #include <X11/keysym.h>
  19. #include <X11/Xlib.h>
  20. #include <signal.h>
  21. #include <X11/Xutil.h>
  22. #include <X11/XKBlib.h>
  23. #include "arg.h"
  24. #include "util.h"
  25. #define LENGTH(X) (sizeof X / sizeof X[0])
  26. char *argv0;
  27. /* global count to prevent repeated error messages */
  28. int count_error = 0;
  29. enum {
  30. INIT,
  31. INPUT,
  32. FAILED,
  33. CAPS,
  34. NUMCOLS
  35. };
  36. struct lock {
  37. int screen;
  38. Window root, win;
  39. Pixmap pmap;
  40. unsigned long colors[NUMCOLS];
  41. };
  42. struct xrandr {
  43. int active;
  44. int evbase;
  45. int errbase;
  46. };
  47. #include "config.h"
  48. static void
  49. die(const char *errstr, ...)
  50. {
  51. va_list ap;
  52. va_start(ap, errstr);
  53. vfprintf(stderr, errstr, ap);
  54. va_end(ap);
  55. exit(1);
  56. }
  57. #ifdef __linux__
  58. #include <fcntl.h>
  59. #include <linux/oom.h>
  60. static void
  61. dontkillme(void)
  62. {
  63. FILE *f;
  64. const char oomfile[] = "/proc/self/oom_score_adj";
  65. if (!(f = fopen(oomfile, "w"))) {
  66. if (errno == ENOENT)
  67. return;
  68. die("slock: fopen %s: %s\n", oomfile, strerror(errno));
  69. }
  70. fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
  71. if (fclose(f)) {
  72. if (errno == EACCES)
  73. die("slock: unable to disable OOM killer. "
  74. "Make sure to suid or sgid slock.\n");
  75. else
  76. die("slock: fclose %s: %s\n", oomfile, strerror(errno));
  77. }
  78. }
  79. #endif
  80. static void
  81. writemessage(Display *dpy, Window win, int screen)
  82. {
  83. int len, line_len, width, height, s_width, s_height, i, j, k, tab_replace, tab_size;
  84. XGCValues gr_values;
  85. XFontStruct *fontinfo;
  86. XColor color, dummy;
  87. XineramaScreenInfo *xsi;
  88. GC gc;
  89. fontinfo = XLoadQueryFont(dpy, font_name);
  90. if (fontinfo == NULL) {
  91. if (count_error == 0) {
  92. fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name);
  93. fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n");
  94. count_error++;
  95. }
  96. return;
  97. }
  98. tab_size = 8 * XTextWidth(fontinfo, " ", 1);
  99. XAllocNamedColor(dpy, DefaultColormap(dpy, screen),
  100. text_color, &color, &dummy);
  101. gr_values.font = fontinfo->fid;
  102. gr_values.foreground = color.pixel;
  103. gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values);
  104. /* To prevent "Uninitialized" warnings. */
  105. xsi = NULL;
  106. /*
  107. * Start formatting and drawing text
  108. */
  109. len = strlen(message);
  110. /* Max max line length (cut at '\n') */
  111. line_len = 0;
  112. k = 0;
  113. for (i = j = 0; i < len; i++) {
  114. if (message[i] == '\n') {
  115. if (i - j > line_len)
  116. line_len = i - j;
  117. k++;
  118. i++;
  119. j = i;
  120. }
  121. }
  122. /* If there is only one line */
  123. if (line_len == 0)
  124. line_len = len;
  125. if (XineramaIsActive(dpy)) {
  126. xsi = XineramaQueryScreens(dpy, &i);
  127. s_width = xsi[0].width;
  128. s_height = xsi[0].height;
  129. } else {
  130. s_width = DisplayWidth(dpy, screen);
  131. s_height = DisplayHeight(dpy, screen);
  132. }
  133. height = s_height*3/7 - (k*20)/3;
  134. width = (s_width - XTextWidth(fontinfo, message, line_len))/2;
  135. /* Look for '\n' and print the text between them. */
  136. for (i = j = k = 0; i <= len; i++) {
  137. /* i == len is the special case for the last line */
  138. if (i == len || message[i] == '\n') {
  139. tab_replace = 0;
  140. while (message[j] == '\t' && j < i) {
  141. tab_replace++;
  142. j++;
  143. }
  144. XDrawString(dpy, win, gc, width + tab_size*tab_replace, height + 20*k, message + j, i - j);
  145. while (i < len && message[i] == '\n') {
  146. i++;
  147. j = i;
  148. k++;
  149. }
  150. }
  151. }
  152. /* xsi should not be NULL anyway if Xinerama is active, but to be safe */
  153. if (XineramaIsActive(dpy) && xsi != NULL)
  154. XFree(xsi);
  155. }
  156. static const char *
  157. gethash(void)
  158. {
  159. const char *hash;
  160. struct passwd *pw;
  161. /* Check if the current user has a password entry */
  162. errno = 0;
  163. if (!(pw = getpwuid(getuid()))) {
  164. if (errno)
  165. die("slock: getpwuid: %s\n", strerror(errno));
  166. else
  167. die("slock: cannot retrieve password entry\n");
  168. }
  169. hash = pw->pw_passwd;
  170. #if HAVE_SHADOW_H
  171. if (!strcmp(hash, "x")) {
  172. struct spwd *sp;
  173. if (!(sp = getspnam(pw->pw_name)))
  174. die("slock: getspnam: cannot retrieve shadow entry. "
  175. "Make sure to suid or sgid slock.\n");
  176. hash = sp->sp_pwdp;
  177. }
  178. #else
  179. if (!strcmp(hash, "*")) {
  180. #ifdef __OpenBSD__
  181. if (!(pw = getpwuid_shadow(getuid())))
  182. die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
  183. "Make sure to suid or sgid slock.\n");
  184. hash = pw->pw_passwd;
  185. #else
  186. die("slock: getpwuid: cannot retrieve shadow entry. "
  187. "Make sure to suid or sgid slock.\n");
  188. #endif /* __OpenBSD__ */
  189. }
  190. #endif /* HAVE_SHADOW_H */
  191. return hash;
  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. len = 0;
  204. caps = 0;
  205. running = 1;
  206. failure = 0;
  207. oldc = INIT;
  208. if (!XkbGetIndicatorState(dpy, XkbUseCoreKbd, &indicators))
  209. caps = indicators & 1;
  210. while (running && !XNextEvent(dpy, &ev)) {
  211. if (ev.type == KeyPress) {
  212. explicit_bzero(&buf, sizeof(buf));
  213. num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
  214. if (IsKeypadKey(ksym)) {
  215. if (ksym == XK_KP_Enter)
  216. ksym = XK_Return;
  217. else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
  218. ksym = (ksym - XK_KP_0) + XK_0;
  219. }
  220. if (IsFunctionKey(ksym) ||
  221. IsKeypadKey(ksym) ||
  222. IsMiscFunctionKey(ksym) ||
  223. IsPFKey(ksym) ||
  224. IsPrivateKeypadKey(ksym))
  225. continue;
  226. switch (ksym) {
  227. case XK_Return:
  228. passwd[len] = '\0';
  229. errno = 0;
  230. if (!(inputhash = crypt(passwd, hash)))
  231. fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
  232. else
  233. running = !!strcmp(inputhash, hash);
  234. if (running) {
  235. XBell(dpy, 100);
  236. failure = 1;
  237. }
  238. explicit_bzero(&passwd, sizeof(passwd));
  239. len = 0;
  240. break;
  241. case XK_Escape:
  242. explicit_bzero(&passwd, sizeof(passwd));
  243. len = 0;
  244. break;
  245. case XK_BackSpace:
  246. if (len)
  247. passwd[--len] = '\0';
  248. break;
  249. case XK_Caps_Lock:
  250. caps = !caps;
  251. break;
  252. default:
  253. if (num && !iscntrl((int)buf[0]) &&
  254. (len + num < sizeof(passwd))) {
  255. memcpy(passwd + len, buf, num);
  256. len += num;
  257. }
  258. break;
  259. }
  260. color = len ? (caps ? CAPS : INPUT) : (failure || failonclear ? FAILED : INIT);
  261. if (running && oldc != color) {
  262. for (screen = 0; screen < nscreens; screen++) {
  263. XSetWindowBackground(dpy,
  264. locks[screen]->win,
  265. locks[screen]->colors[color]);
  266. XClearWindow(dpy, locks[screen]->win);
  267. writemessage(dpy, locks[screen]->win, screen);
  268. }
  269. oldc = color;
  270. }
  271. } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
  272. rre = (XRRScreenChangeNotifyEvent*)&ev;
  273. for (screen = 0; screen < nscreens; screen++) {
  274. if (locks[screen]->win == rre->window) {
  275. if (rre->rotation == RR_Rotate_90 ||
  276. rre->rotation == RR_Rotate_270)
  277. XResizeWindow(dpy, locks[screen]->win,
  278. rre->height, rre->width);
  279. else
  280. XResizeWindow(dpy, locks[screen]->win,
  281. rre->width, rre->height);
  282. XClearWindow(dpy, locks[screen]->win);
  283. break;
  284. }
  285. }
  286. } else {
  287. for (screen = 0; screen < nscreens; screen++)
  288. XRaiseWindow(dpy, locks[screen]->win);
  289. }
  290. }
  291. }
  292. static struct lock *
  293. lockscreen(Display *dpy, struct xrandr *rr, int screen)
  294. {
  295. char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
  296. int i, ptgrab, kbgrab;
  297. struct lock *lock;
  298. XColor color, dummy;
  299. XSetWindowAttributes wa;
  300. Cursor invisible;
  301. if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
  302. return NULL;
  303. lock->screen = screen;
  304. lock->root = RootWindow(dpy, lock->screen);
  305. for (i = 0; i < NUMCOLS; i++) {
  306. XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
  307. colorname[i], &color, &dummy);
  308. lock->colors[i] = color.pixel;
  309. }
  310. /* init */
  311. wa.override_redirect = 1;
  312. wa.background_pixel = lock->colors[INIT];
  313. lock->win = XCreateWindow(dpy, lock->root, 0, 0,
  314. DisplayWidth(dpy, lock->screen),
  315. DisplayHeight(dpy, lock->screen),
  316. 0, DefaultDepth(dpy, lock->screen),
  317. CopyFromParent,
  318. DefaultVisual(dpy, lock->screen),
  319. CWOverrideRedirect | CWBackPixel, &wa);
  320. lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
  321. invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
  322. &color, &color, 0, 0);
  323. XDefineCursor(dpy, lock->win, invisible);
  324. /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
  325. for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
  326. if (ptgrab != GrabSuccess) {
  327. ptgrab = XGrabPointer(dpy, lock->root, False,
  328. ButtonPressMask | ButtonReleaseMask |
  329. PointerMotionMask, GrabModeAsync,
  330. GrabModeAsync, None, invisible, CurrentTime);
  331. }
  332. if (kbgrab != GrabSuccess) {
  333. kbgrab = XGrabKeyboard(dpy, lock->root, True,
  334. GrabModeAsync, GrabModeAsync, CurrentTime);
  335. }
  336. /* input is grabbed: we can lock the screen */
  337. if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
  338. XMapRaised(dpy, lock->win);
  339. if (rr->active)
  340. XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
  341. XSelectInput(dpy, lock->root, SubstructureNotifyMask);
  342. return lock;
  343. }
  344. /* retry on AlreadyGrabbed but fail on other errors */
  345. if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
  346. (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
  347. break;
  348. usleep(100000);
  349. }
  350. /* we couldn't grab all input: fail out */
  351. if (ptgrab != GrabSuccess)
  352. fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
  353. screen);
  354. if (kbgrab != GrabSuccess)
  355. fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
  356. screen);
  357. return NULL;
  358. }
  359. static void
  360. usage(void)
  361. {
  362. die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n");
  363. }
  364. void runprelock(){
  365. for(int i = 0; i < LENGTH(prelock); i++){
  366. if(fork() == 0)
  367. execvp(prelock[i][0], (char **)prelock[i]);
  368. }
  369. }
  370. void runpostlock(){
  371. for(int i = 0; i < LENGTH(prelock); i++){
  372. if(fork() == 0)
  373. execvp(postlock[i][0], (char **)postlock[i]);
  374. }
  375. }
  376. int lock(Display *dpy, uid_t duid, gid_t dgid, char* hash){
  377. struct xrandr rr;
  378. struct lock **locks;
  379. int i, s, nlocks, nscreens;
  380. /* drop privileges */
  381. if (setgroups(0, NULL) < 0)
  382. die("slock: setgroups: %s\n", strerror(errno));
  383. if (setgid(dgid) < 0)
  384. die("slock: setgid: %s\n", strerror(errno));
  385. if (setuid(duid) < 0)
  386. die("slock: setuid: %s\n", strerror(errno));
  387. /* check for Xrandr support */
  388. rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
  389. /* get number of screens in display "dpy" and blank them */
  390. nscreens = ScreenCount(dpy);
  391. if (!(locks = calloc(nscreens, sizeof(struct lock *))))
  392. die("slock: out of memory\n");
  393. for (nlocks = 0, s = 0; s < nscreens; s++) {
  394. if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) {
  395. writemessage(dpy, locks[s]->win, s);
  396. nlocks++;
  397. } else {
  398. break;
  399. }
  400. }
  401. XSync(dpy, 0);
  402. /* did we manage to lock everything? */
  403. if (nlocks != nscreens)
  404. return 1;
  405. readpw(dpy, &rr, locks, nscreens, hash);
  406. return 0;
  407. }
  408. int
  409. main(int argc, char **argv) {
  410. Display *dpy;
  411. struct passwd *pwd;
  412. struct group *grp;
  413. int count_fonts;
  414. char **font_names;
  415. int i;
  416. uid_t duid;
  417. gid_t dgid;
  418. const char *hash;
  419. ARGBEGIN {
  420. case 'v':
  421. fprintf(stderr, "slock-"VERSION"\n");
  422. return 0;
  423. case 'm':
  424. message = EARGF(usage());
  425. break;
  426. case 'f':
  427. if (!(dpy = XOpenDisplay(NULL)))
  428. die("slock: cannot open display\n");
  429. font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts);
  430. for (i=0; i<count_fonts; i++) {
  431. fprintf(stderr, "%s\n", *(font_names+i));
  432. }
  433. return 0;
  434. default:
  435. usage();
  436. } ARGEND
  437. /* validate drop-user and -group */
  438. errno = 0;
  439. if (!(pwd = getpwnam(user)))
  440. die("slock: getpwnam %s: %s\n", user,
  441. errno ? strerror(errno) : "user entry not found");
  442. duid = pwd->pw_uid;
  443. errno = 0;
  444. if (!(grp = getgrnam(group)))
  445. die("slock: getgrnam %s: %s\n", group,
  446. errno ? strerror(errno) : "group entry not found");
  447. dgid = grp->gr_gid;
  448. #ifdef __linux__
  449. dontkillme();
  450. #endif
  451. hash = gethash();
  452. errno = 0;
  453. if (!crypt("", hash))
  454. die("slock: crypt: %s\n", strerror(errno));
  455. if (!(dpy = XOpenDisplay(NULL)))
  456. die("slock: cannot open display\n");
  457. runprelock();
  458. int lock_pid = fork();
  459. if(lock_pid == 0){
  460. lock(dpy, duid, dgid, hash);
  461. }else{
  462. waitpid(lock_pid);
  463. }
  464. runpostlock();
  465. return 0;
  466. }