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