1 /* See LICENSE file for license details. */
2 #define _XOPEN_SOURCE 500
16 #include <sys/types.h>
17 #include <X11/extensions/Xrandr.h>
18 #include <X11/keysym.h>
20 #include <X11/Xutil.h>
38 unsigned long colors
[NUMCOLS
];
50 die(const char *errstr
, ...)
55 vfprintf(stderr
, errstr
, ap
);
62 #include <linux/oom.h>
68 const char oomfile
[] = "/proc/self/oom_score_adj";
70 if (!(f
= fopen(oomfile
, "w"))) {
73 die("slock: fopen %s: %s\n", oomfile
, strerror(errno
));
75 fprintf(f
, "%d", OOM_SCORE_ADJ_MIN
);
78 die("slock: unable to disable OOM killer. "
79 "Make sure to suid or sgid slock.\n");
81 die("slock: fclose %s: %s\n", oomfile
, strerror(errno
));
92 /* Check if the current user has a password entry */
94 if (!(pw
= getpwuid(getuid()))) {
96 die("slock: getpwuid: %s\n", strerror(errno
));
98 die("slock: cannot retrieve password entry\n");
100 hash
= pw
->pw_passwd
;
103 if (!strcmp(hash
, "x")) {
105 if (!(sp
= getspnam(pw
->pw_name
)))
106 die("slock: getspnam: cannot retrieve shadow entry. "
107 "Make sure to suid or sgid slock.\n");
111 if (!strcmp(hash
, "*")) {
113 if (!(pw
= getpwuid_shadow(getuid())))
114 die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
115 "Make sure to suid or sgid slock.\n");
116 hash
= pw
->pw_passwd
;
118 die("slock: getpwuid: cannot retrieve shadow entry. "
119 "Make sure to suid or sgid slock.\n");
120 #endif /* __OpenBSD__ */
122 #endif /* HAVE_SHADOW_H */
128 readpw(Display
*dpy
, struct xrandr
*rr
, struct lock
**locks
, int nscreens
,
131 XRRScreenChangeNotifyEvent
*rre
;
132 char buf
[32], passwd
[256], *inputhash
;
133 int num
, screen
, running
, failure
, oldc
;
134 unsigned int len
, color
;
143 while (running
&& !XNextEvent(dpy
, &ev
)) {
144 if (ev
.type
== KeyPress
) {
145 explicit_bzero(&buf
, sizeof(buf
));
146 num
= XLookupString(&ev
.xkey
, buf
, sizeof(buf
), &ksym
, 0);
147 if (IsKeypadKey(ksym
)) {
148 if (ksym
== XK_KP_Enter
)
150 else if (ksym
>= XK_KP_0
&& ksym
<= XK_KP_9
)
151 ksym
= (ksym
- XK_KP_0
) + XK_0
;
153 if (IsFunctionKey(ksym
) ||
155 IsMiscFunctionKey(ksym
) ||
157 IsPrivateKeypadKey(ksym
))
163 if (!(inputhash
= crypt(passwd
, hash
)))
164 fprintf(stderr
, "slock: crypt: %s\n", strerror(errno
));
166 running
= !!strcmp(inputhash
, hash
);
171 explicit_bzero(&passwd
, sizeof(passwd
));
175 explicit_bzero(&passwd
, sizeof(passwd
));
180 passwd
[len
--] = '\0';
183 if (num
&& !iscntrl((int)buf
[0]) &&
184 (len
+ num
< sizeof(passwd
))) {
185 memcpy(passwd
+ len
, buf
, num
);
190 color
= len
? INPUT
: ((failure
|| failonclear
) ? FAILED
: INIT
);
191 if (running
&& oldc
!= color
) {
192 for (screen
= 0; screen
< nscreens
; screen
++) {
193 XSetWindowBackground(dpy
,
195 locks
[screen
]->colors
[color
]);
196 XClearWindow(dpy
, locks
[screen
]->win
);
200 } else if (rr
->active
&& ev
.type
== rr
->evbase
+ RRScreenChangeNotify
) {
201 rre
= (XRRScreenChangeNotifyEvent
*)&ev
;
202 for (screen
= 0; screen
< nscreens
; screen
++) {
203 if (locks
[screen
]->win
== rre
->window
) {
204 if (rre
->rotation
== RR_Rotate_90
||
205 rre
->rotation
== RR_Rotate_270
)
206 XResizeWindow(dpy
, locks
[screen
]->win
,
207 rre
->height
, rre
->width
);
209 XResizeWindow(dpy
, locks
[screen
]->win
,
210 rre
->width
, rre
->height
);
211 XClearWindow(dpy
, locks
[screen
]->win
);
215 } else for (screen
= 0; screen
< nscreens
; screen
++)
216 XRaiseWindow(dpy
, locks
[screen
]->win
);
221 lockscreen(Display
*dpy
, struct xrandr
*rr
, int screen
)
223 char curs
[] = {0, 0, 0, 0, 0, 0, 0, 0};
224 int i
, ptgrab
, kbgrab
;
227 XSetWindowAttributes wa
;
230 if (dpy
== NULL
|| screen
< 0 || !(lock
= malloc(sizeof(struct lock
))))
233 lock
->screen
= screen
;
234 lock
->root
= RootWindow(dpy
, lock
->screen
);
236 for (i
= 0; i
< NUMCOLS
; i
++) {
237 XAllocNamedColor(dpy
, DefaultColormap(dpy
, lock
->screen
),
238 colorname
[i
], &color
, &dummy
);
239 lock
->colors
[i
] = color
.pixel
;
243 wa
.override_redirect
= 1;
244 wa
.background_pixel
= lock
->colors
[INIT
];
245 lock
->win
= XCreateWindow(dpy
, lock
->root
, 0, 0,
246 DisplayWidth(dpy
, lock
->screen
),
247 DisplayHeight(dpy
, lock
->screen
),
248 0, DefaultDepth(dpy
, lock
->screen
),
250 DefaultVisual(dpy
, lock
->screen
),
251 CWOverrideRedirect
| CWBackPixel
, &wa
);
252 lock
->pmap
= XCreateBitmapFromData(dpy
, lock
->win
, curs
, 8, 8);
253 invisible
= XCreatePixmapCursor(dpy
, lock
->pmap
, lock
->pmap
,
254 &color
, &color
, 0, 0);
255 XDefineCursor(dpy
, lock
->win
, invisible
);
257 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
258 for (i
= 0, ptgrab
= kbgrab
= -1; i
< 6; i
++) {
259 if (ptgrab
!= GrabSuccess
) {
260 ptgrab
= XGrabPointer(dpy
, lock
->root
, False
,
261 ButtonPressMask
| ButtonReleaseMask
|
262 PointerMotionMask
, GrabModeAsync
,
263 GrabModeAsync
, None
, invisible
, CurrentTime
);
265 if (kbgrab
!= GrabSuccess
) {
266 kbgrab
= XGrabKeyboard(dpy
, lock
->root
, True
,
267 GrabModeAsync
, GrabModeAsync
, CurrentTime
);
270 /* input is grabbed: we can lock the screen */
271 if (ptgrab
== GrabSuccess
&& kbgrab
== GrabSuccess
) {
272 XMapRaised(dpy
, lock
->win
);
274 XRRSelectInput(dpy
, lock
->win
, RRScreenChangeNotifyMask
);
276 XSelectInput(dpy
, lock
->root
, SubstructureNotifyMask
);
280 /* retry on AlreadyGrabbed but fail on other errors */
281 if ((ptgrab
!= AlreadyGrabbed
&& ptgrab
!= GrabSuccess
) ||
282 (kbgrab
!= AlreadyGrabbed
&& kbgrab
!= GrabSuccess
))
288 /* we couldn't grab all input: fail out */
289 if (ptgrab
!= GrabSuccess
)
290 fprintf(stderr
, "slock: unable to grab mouse pointer for screen %d\n",
292 if (kbgrab
!= GrabSuccess
)
293 fprintf(stderr
, "slock: unable to grab keyboard for screen %d\n",
301 die("usage: slock [-v] [cmd [arg ...]]\n");
305 main(int argc
, char **argv
) {
314 int s
, nlocks
, nscreens
;
318 fprintf(stderr
, "slock-"VERSION
"\n");
324 /* validate drop-user and -group */
326 if (!(pwd
= getpwnam(user
)))
327 die("slock: getpwnam %s: %s\n", user
,
328 errno
? strerror(errno
) : "user entry not found");
331 if (!(grp
= getgrnam(group
)))
332 die("slock: getgrnam %s: %s\n", group
,
333 errno
? strerror(errno
) : "group entry not found");
342 if (!crypt("", hash
))
343 die("slock: crypt: %s\n", strerror(errno
));
345 if (!(dpy
= XOpenDisplay(NULL
)))
346 die("slock: cannot open display\n");
348 /* drop privileges */
349 if (setgroups(0, NULL
) < 0)
350 die("slock: setgroups: %s\n", strerror(errno
));
351 if (setgid(dgid
) < 0)
352 die("slock: setgid: %s\n", strerror(errno
));
353 if (setuid(duid
) < 0)
354 die("slock: setuid: %s\n", strerror(errno
));
356 /* check for Xrandr support */
357 rr
.active
= XRRQueryExtension(dpy
, &rr
.evbase
, &rr
.errbase
);
359 /* get number of screens in display "dpy" and blank them */
360 nscreens
= ScreenCount(dpy
);
361 if (!(locks
= calloc(nscreens
, sizeof(struct lock
*))))
362 die("slock: out of memory\n");
363 for (nlocks
= 0, s
= 0; s
< nscreens
; s
++) {
364 if ((locks
[s
] = lockscreen(dpy
, &rr
, s
)) != NULL
)
371 /* did we manage to lock everything? */
372 if (nlocks
!= nscreens
)
375 /* run post-lock command */
379 die("slock: fork failed: %s\n", strerror(errno
));
381 if (close(ConnectionNumber(dpy
)) < 0)
382 die("slock: close: %s\n", strerror(errno
));
383 execvp(argv
[0], argv
);
384 fprintf(stderr
, "slock: execvp %s: %s\n", argv
[0], strerror(errno
));
389 /* everything is now blank. Wait for the correct password */
390 readpw(dpy
, &rr
, locks
, nscreens
, hash
);