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.

545 lines
13 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. int fd = open("/dev/null", O_WRONLY);
  368. dup2(fd, 1);
  369. dup2(fd, 2);
  370. close(fd);
  371. execvp(prelock[i][0], (char **)prelock[i]);
  372. }
  373. }
  374. }
  375. void runpostlock(){
  376. for(int i = 0; i < LENGTH(prelock); i++){
  377. if(fork() == 0){
  378. int fd = open("/dev/null", O_WRONLY);
  379. dup2(fd, 1);
  380. dup2(fd, 2);
  381. close(fd);
  382. execvp(postlock[i][0], (char **)postlock[i]);
  383. }
  384. }
  385. }
  386. int lock(Display *dpy, uid_t duid, gid_t dgid, char* hash){
  387. struct xrandr rr;
  388. struct lock **locks;
  389. int i, s, nlocks, nscreens;
  390. /* drop privileges */
  391. if (setgroups(0, NULL) < 0)
  392. die("slock: setgroups: %s\n", strerror(errno));
  393. if (setgid(dgid) < 0)
  394. die("slock: setgid: %s\n", strerror(errno));
  395. if (setuid(duid) < 0)
  396. die("slock: setuid: %s\n", strerror(errno));
  397. /* check for Xrandr support */
  398. rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
  399. /* get number of screens in display "dpy" and blank them */
  400. nscreens = ScreenCount(dpy);
  401. if (!(locks = calloc(nscreens, sizeof(struct lock *))))
  402. die("slock: out of memory\n");
  403. for (nlocks = 0, s = 0; s < nscreens; s++) {
  404. if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) {
  405. writemessage(dpy, locks[s]->win, s);
  406. nlocks++;
  407. } else {
  408. break;
  409. }
  410. }
  411. XSync(dpy, 0);
  412. /* did we manage to lock everything? */
  413. if (nlocks != nscreens)
  414. return 1;
  415. readpw(dpy, &rr, locks, nscreens, hash);
  416. return 0;
  417. }
  418. int
  419. main(int argc, char **argv) {
  420. Display *dpy;
  421. struct passwd *pwd;
  422. struct group *grp;
  423. int count_fonts;
  424. char **font_names;
  425. int i;
  426. uid_t duid;
  427. gid_t dgid;
  428. const char *hash;
  429. ARGBEGIN {
  430. case 'v':
  431. fprintf(stderr, "slock-"VERSION"\n");
  432. return 0;
  433. case 'm':
  434. message = EARGF(usage());
  435. break;
  436. case 'f':
  437. if (!(dpy = XOpenDisplay(NULL)))
  438. die("slock: cannot open display\n");
  439. font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts);
  440. for (i=0; i<count_fonts; i++) {
  441. fprintf(stderr, "%s\n", *(font_names+i));
  442. }
  443. return 0;
  444. default:
  445. usage();
  446. } ARGEND
  447. /* validate drop-user and -group */
  448. errno = 0;
  449. if (!(pwd = getpwnam(user)))
  450. die("slock: getpwnam %s: %s\n", user,
  451. errno ? strerror(errno) : "user entry not found");
  452. duid = pwd->pw_uid;
  453. errno = 0;
  454. if (!(grp = getgrnam(group)))
  455. die("slock: getgrnam %s: %s\n", group,
  456. errno ? strerror(errno) : "group entry not found");
  457. dgid = grp->gr_gid;
  458. #ifdef __linux__
  459. dontkillme();
  460. #endif
  461. hash = gethash();
  462. errno = 0;
  463. if (!crypt("", hash))
  464. die("slock: crypt: %s\n", strerror(errno));
  465. if (!(dpy = XOpenDisplay(NULL)))
  466. die("slock: cannot open display\n");
  467. runprelock();
  468. int lock_pid = fork();
  469. if(lock_pid == 0){
  470. lock(dpy, duid, dgid, hash);
  471. }else{
  472. waitpid(lock_pid);
  473. }
  474. runpostlock();
  475. return 0;
  476. }