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.

189 lines
6.5 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 <stdbool.h>
  29. #include <security/pam_appl.h>
  30. static char passwd[256];
  31. static void die(const char *errstr, ...) {
  32. va_list ap;
  33. va_start(ap, errstr);
  34. vfprintf(stderr, errstr, ap);
  35. va_end(ap);
  36. exit(EXIT_FAILURE);
  37. }
  38. /*
  39. * Callback function for PAM. We only react on password request callbacks.
  40. *
  41. */
  42. static int conv_callback(int num_msg, const struct pam_message **msg,
  43. struct pam_response **resp, void *appdata_ptr) {
  44. if (num_msg == 0)
  45. return 1;
  46. /* PAM expects an arry of responses, one for each message */
  47. if ((*resp = calloc(num_msg, sizeof(struct pam_message))) == NULL) {
  48. perror("calloc");
  49. return 1;
  50. }
  51. for (int c = 0; c < num_msg; c++) {
  52. if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF &&
  53. msg[c]->msg_style != PAM_PROMPT_ECHO_ON)
  54. continue;
  55. /* return code is currently not used but should be set to zero */
  56. resp[c]->resp_retcode = 0;
  57. if ((resp[c]->resp = strdup(passwd)) == NULL) {
  58. perror("strdup");
  59. return 1;
  60. }
  61. }
  62. return 0;
  63. }
  64. int main(int argc, char *argv[]) {
  65. char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
  66. char buf[32];
  67. int num, screen;
  68. unsigned int len;
  69. bool running = true;
  70. Cursor invisible;
  71. Display *dpy;
  72. KeySym ksym;
  73. Pixmap pmap;
  74. Window root, w;
  75. XColor black, dummy;
  76. XEvent ev;
  77. XSetWindowAttributes wa;
  78. /* TODO: use getopt */
  79. if((argc == 2) && !strcmp("-v", argv[1]))
  80. die("i3lock-"VERSION", © 2009 Michael Stapelberg\n"
  81. "based on slock, which is © 2006-2008 Anselm R Garbe\n");
  82. else if(argc != 1)
  83. die("usage: i3lock [-v]\n");
  84. pam_handle_t *handle;
  85. struct pam_conv conv;
  86. conv.conv = conv_callback;
  87. int ret = pam_start("i3lock", getenv("USER"), &conv, &handle);
  88. printf("pam_start = %d\n", ret);
  89. if (ret != PAM_SUCCESS)
  90. die("error = %s\n", pam_strerror(handle, ret));
  91. if(!(dpy = XOpenDisplay(0)))
  92. die("slock: cannot open display\n");
  93. screen = DefaultScreen(dpy);
  94. root = RootWindow(dpy, screen);
  95. if (fork() != 0)
  96. return 0;
  97. /* init */
  98. wa.override_redirect = 1;
  99. wa.background_pixel = WhitePixel(dpy, screen);
  100. w = XCreateWindow(dpy, root, 0, 0, DisplayWidth(dpy, screen), DisplayHeight(dpy, screen),
  101. 0, DefaultDepth(dpy, screen), CopyFromParent,
  102. DefaultVisual(dpy, screen), CWOverrideRedirect | CWBackPixel, &wa);
  103. XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "black", &black, &dummy);
  104. pmap = XCreateBitmapFromData(dpy, w, curs, 8, 8);
  105. invisible = XCreatePixmapCursor(dpy, pmap, pmap, &black, &black, 0, 0);
  106. XDefineCursor(dpy, w, invisible);
  107. XMapRaised(dpy, w);
  108. for(len = 1000; len; len--) {
  109. if(XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
  110. GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
  111. break;
  112. usleep(1000);
  113. }
  114. if((running = running && (len > 0))) {
  115. for(len = 1000; len; len--) {
  116. if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
  117. == GrabSuccess)
  118. break;
  119. usleep(1000);
  120. }
  121. running = (len > 0);
  122. }
  123. len = 0;
  124. XSync(dpy, False);
  125. /* main event loop */
  126. while(running && !XNextEvent(dpy, &ev)) {
  127. if(ev.type == KeyPress) {
  128. buf[0] = 0;
  129. num = XLookupString(&ev.xkey, buf, sizeof buf, &ksym, 0);
  130. if(IsKeypadKey(ksym)) {
  131. if(ksym == XK_KP_Enter)
  132. ksym = XK_Return;
  133. else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
  134. ksym = (ksym - XK_KP_0) + XK_0;
  135. }
  136. if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
  137. || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
  138. || IsPrivateKeypadKey(ksym))
  139. continue;
  140. switch(ksym) {
  141. case XK_Return:
  142. passwd[len] = 0;
  143. if ((ret = pam_authenticate(handle, 0)) == PAM_SUCCESS)
  144. running = false;
  145. else fprintf(stderr, "PAM: %s\n", pam_strerror(handle, ret));
  146. len = 0;
  147. break;
  148. case XK_Escape:
  149. len = 0;
  150. break;
  151. case XK_BackSpace:
  152. if (len > 0)
  153. len--;
  154. break;
  155. default:
  156. if(num && !iscntrl((int) buf[0]) && (len + num < sizeof passwd)) {
  157. memcpy(passwd + len, buf, num);
  158. len += num;
  159. }
  160. break;
  161. }
  162. }
  163. }
  164. XUngrabPointer(dpy, CurrentTime);
  165. XFreePixmap(dpy, pmap);
  166. XDestroyWindow(dpy, w);
  167. XCloseDisplay(dpy);
  168. return 0;
  169. }