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.

233 lines
7.9 KiB

  1. /*
  2. * vim:ts=8:expandtab
  3. *
  4. * i3lock - an improved version of slock
  5. *
  6. * i3lock © 2009 Michael Stapelberg and contributors
  7. * slock © 2006-2008 Anselm R Garbe
  8. *
  9. * See file LICENSE for license information.
  10. *
  11. * Note that on any error (calloc is out of memory for example)
  12. * we do not do anything so that the user can fix the error by
  13. * himself (kill X to get more free memory or stop some other
  14. * program using SSH/console).
  15. *
  16. */
  17. #define _XOPEN_SOURCE 500
  18. #include <ctype.h>
  19. #include <stdarg.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <sys/types.h>
  25. #include <X11/keysym.h>
  26. #include <X11/Xlib.h>
  27. #include <X11/Xutil.h>
  28. #include <X11/extensions/dpms.h>
  29. #include <stdbool.h>
  30. #include <getopt.h>
  31. #include <security/pam_appl.h>
  32. static char passwd[256];
  33. static void die(const char *errstr, ...) {
  34. va_list ap;
  35. va_start(ap, errstr);
  36. vfprintf(stderr, errstr, ap);
  37. va_end(ap);
  38. exit(EXIT_FAILURE);
  39. }
  40. /*
  41. * Callback function for PAM. We only react on password request callbacks.
  42. *
  43. */
  44. static int conv_callback(int num_msg, const struct pam_message **msg,
  45. struct pam_response **resp, void *appdata_ptr) {
  46. if (num_msg == 0)
  47. return 1;
  48. /* PAM expects an arry of responses, one for each message */
  49. if ((*resp = calloc(num_msg, sizeof(struct pam_message))) == NULL) {
  50. perror("calloc");
  51. return 1;
  52. }
  53. for (int c = 0; c < num_msg; c++) {
  54. if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF &&
  55. msg[c]->msg_style != PAM_PROMPT_ECHO_ON)
  56. continue;
  57. /* return code is currently not used but should be set to zero */
  58. resp[c]->resp_retcode = 0;
  59. if ((resp[c]->resp = strdup(passwd)) == NULL) {
  60. perror("strdup");
  61. return 1;
  62. }
  63. }
  64. return 0;
  65. }
  66. int main(int argc, char *argv[]) {
  67. char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
  68. char buf[32];
  69. char *username;
  70. int num, screen;
  71. unsigned int len;
  72. bool running = true;
  73. /* By default, fork, don’t beep and don’t turn off monitor */
  74. bool dont_fork = false;
  75. bool beep = false;
  76. bool dpms = false;
  77. Cursor invisible;
  78. Display *dpy;
  79. KeySym ksym;
  80. Pixmap pmap;
  81. Window root, w;
  82. XColor black, dummy;
  83. XEvent ev;
  84. XSetWindowAttributes wa;
  85. pam_handle_t *handle;
  86. struct pam_conv conv = {conv_callback, NULL};
  87. char opt;
  88. int optind = 0;
  89. static struct option long_options[] = {
  90. {"version", no_argument, NULL, 'v'},
  91. {"nofork", no_argument, NULL, 'n'},
  92. {"beep", no_argument, NULL, 'b'},
  93. {"dpms", no_argument, NULL, 'd'},
  94. {NULL, no_argument, NULL, 0}
  95. };
  96. while ((opt = getopt_long(argc, argv, "vnbd", long_options, &optind)) != -1) {
  97. switch (opt) {
  98. case 'v':
  99. die("i3lock-"VERSION", © 2009 Michael Stapelberg\n"
  100. "based on slock, which is © 2006-2008 Anselm R Garbe\n");
  101. case 'n':
  102. dont_fork = true;
  103. break;
  104. case 'b':
  105. beep = true;
  106. break;
  107. case 'd':
  108. dpms = true;
  109. break;
  110. default:
  111. die("i3lock: Unknown option. Syntax: i3lock [-v] [-n] [-b] [-d]\n");
  112. }
  113. }
  114. if ((username = getenv("USER")) == NULL)
  115. die("USER environment variable not set, please set it.\n");
  116. int ret = pam_start("i3lock", username, &conv, &handle);
  117. if (ret != PAM_SUCCESS)
  118. die("PAM: %s\n", pam_strerror(handle, ret));
  119. if(!(dpy = XOpenDisplay(0)))
  120. die("i3lock: cannot open display\n");
  121. screen = DefaultScreen(dpy);
  122. root = RootWindow(dpy, screen);
  123. if (!dont_fork) {
  124. if (fork() != 0)
  125. return 0;
  126. }
  127. /* init */
  128. wa.override_redirect = 1;
  129. wa.background_pixel = WhitePixel(dpy, screen);
  130. w = XCreateWindow(dpy, root, 0, 0, DisplayWidth(dpy, screen), DisplayHeight(dpy, screen),
  131. 0, DefaultDepth(dpy, screen), CopyFromParent,
  132. DefaultVisual(dpy, screen), CWOverrideRedirect | CWBackPixel, &wa);
  133. XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "black", &black, &dummy);
  134. pmap = XCreateBitmapFromData(dpy, w, curs, 8, 8);
  135. invisible = XCreatePixmapCursor(dpy, pmap, pmap, &black, &black, 0, 0);
  136. XDefineCursor(dpy, w, invisible);
  137. XMapRaised(dpy, w);
  138. for(len = 1000; len; len--) {
  139. if(XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
  140. GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
  141. break;
  142. usleep(1000);
  143. }
  144. if((running = running && (len > 0))) {
  145. for(len = 1000; len; len--) {
  146. if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
  147. == GrabSuccess)
  148. break;
  149. usleep(1000);
  150. }
  151. running = (len > 0);
  152. }
  153. len = 0;
  154. XSync(dpy, False);
  155. /* main event loop */
  156. while(running && !XNextEvent(dpy, &ev)) {
  157. if (len == 0 && dpms && DPMSCapable(dpy)) {
  158. DPMSEnable(dpy);
  159. DPMSForceLevel(dpy, DPMSModeOff);
  160. }
  161. if(ev.type != KeyPress)
  162. continue;
  163. buf[0] = 0;
  164. num = XLookupString(&ev.xkey, buf, sizeof buf, &ksym, 0);
  165. if(IsKeypadKey(ksym)) {
  166. if(ksym == XK_KP_Enter)
  167. ksym = XK_Return;
  168. else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
  169. ksym = (ksym - XK_KP_0) + XK_0;
  170. }
  171. if(IsFunctionKey(ksym) ||
  172. IsKeypadKey(ksym) ||
  173. IsMiscFunctionKey(ksym) ||
  174. IsPFKey(ksym) ||
  175. IsPrivateKeypadKey(ksym))
  176. continue;
  177. switch(ksym) {
  178. case XK_Return:
  179. passwd[len] = 0;
  180. if ((ret = pam_authenticate(handle, 0)) == PAM_SUCCESS)
  181. running = false;
  182. else {
  183. fprintf(stderr, "PAM: %s\n", pam_strerror(handle, ret));
  184. if (beep)
  185. XBell(dpy, 100);
  186. }
  187. len = 0;
  188. break;
  189. case XK_Escape:
  190. len = 0;
  191. break;
  192. case XK_BackSpace:
  193. if (len > 0)
  194. len--;
  195. break;
  196. default:
  197. if(num && !iscntrl((int) buf[0]) && (len + num < sizeof passwd)) {
  198. memcpy(passwd + len, buf, num);
  199. len += num;
  200. }
  201. break;
  202. }
  203. }
  204. XUngrabPointer(dpy, CurrentTime);
  205. XFreePixmap(dpy, pmap);
  206. XDestroyWindow(dpy, w);
  207. XCloseDisplay(dpy);
  208. return 0;
  209. }