1 /* See LICENSE file for license details. */
2 #define _XOPEN_SOURCE 500
15 #include <sys/types.h>
16 #include <X11/extensions/Xrandr.h>
17 #include <X11/keysym.h>
19 #include <X11/Xutil.h>
22 #include <login_cap.h>
44 unsigned long colors
[NUMCOLS
];
54 die(const char *errstr
, ...)
59 vfprintf(stderr
, errstr
, ap
);
66 #include <linux/oom.h>
72 const char oomfile
[] = "/proc/self/oom_score_adj";
74 if (!(f
= fopen(oomfile
, "w"))) {
77 die("slock: fopen %s: %s\n", oomfile
, strerror(errno
));
79 fprintf(f
, "%d", OOM_SCORE_ADJ_MIN
);
82 die("slock: unable to disable OOM killer. "
83 "suid or sgid set?\n");
85 die("slock: fclose %s: %s\n", oomfile
,
92 /* only run as root */
100 if (!(pw
= getpwuid(getuid()))) {
102 die("slock: getpwuid: %s\n", strerror(errno
));
104 die("slock: cannot retrieve password entry\n");
106 rval
= pw
->pw_passwd
;
109 if (rval
[0] == 'x' && rval
[1] == '\0') {
111 if (!(sp
= getspnam(getenv("USER"))))
112 die("slock: cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
117 /* drop privileges */
118 if (geteuid() == 0 &&
119 ((getegid() != pw
->pw_gid
&& setgid(pw
->pw_gid
) < 0) || setuid(pw
->pw_uid
) < 0))
120 die("slock: cannot drop privileges\n");
129 readpw(Display
*dpy
, const char *pws
)
132 char buf
[32], passwd
[256], *encrypted
;
133 int num
, screen
, running
, failure
;
134 unsigned int len
, color
;
137 static int oldc
= INIT
;
143 /* As "slock" stands for "Simple X display locker", the DPMS settings
144 * had been removed and you can set it with "xset" or some other
145 * utility. This way the user can easily set a customized DPMS
147 while (running
&& !XNextEvent(dpy
, &ev
)) {
148 if (ev
.type
== KeyPress
) {
149 explicit_bzero(&buf
, sizeof(buf
));
150 num
= XLookupString(&ev
.xkey
, buf
, sizeof(buf
), &ksym
, 0);
151 if (IsKeypadKey(ksym
)) {
152 if (ksym
== XK_KP_Enter
)
154 else if (ksym
>= XK_KP_0
&& ksym
<= XK_KP_9
)
155 ksym
= (ksym
- XK_KP_0
) + XK_0
;
157 if (IsFunctionKey(ksym
) ||
159 IsMiscFunctionKey(ksym
) ||
161 IsPrivateKeypadKey(ksym
))
167 running
= !auth_userokay(getlogin(), NULL
, "auth-slock", passwd
);
170 if (!(encrypted
= crypt(passwd
, pws
)))
171 fprintf(stderr
, "slock: crypt: %s\n", strerror(errno
));
173 running
= !!strcmp(encrypted
, pws
);
179 explicit_bzero(&passwd
, sizeof(passwd
));
183 explicit_bzero(&passwd
, sizeof(passwd
));
191 if (num
&& !iscntrl((int)buf
[0]) && (len
+ num
< sizeof(passwd
))) {
192 memcpy(passwd
+ len
, buf
, num
);
197 color
= len
? INPUT
: (failure
|| failonclear
? FAILED
: INIT
);
198 if (running
&& oldc
!= color
) {
199 for (screen
= 0; screen
< nscreens
; screen
++) {
200 XSetWindowBackground(dpy
, locks
[screen
]->win
, locks
[screen
]->colors
[color
]);
201 XClearWindow(dpy
, locks
[screen
]->win
);
205 } else if (rr
&& ev
.type
== rrevbase
+ RRScreenChangeNotify
) {
206 XRRScreenChangeNotifyEvent
*rre
= (XRRScreenChangeNotifyEvent
*)&ev
;
207 for (screen
= 0; screen
< nscreens
; screen
++) {
208 if (locks
[screen
]->win
== rre
->window
) {
209 XResizeWindow(dpy
, locks
[screen
]->win
, rre
->width
, rre
->height
);
210 XClearWindow(dpy
, locks
[screen
]->win
);
213 } else for (screen
= 0; screen
< nscreens
; screen
++)
214 XRaiseWindow(dpy
, locks
[screen
]->win
);
219 unlockscreen(Display
*dpy
, Lock
*lock
)
221 if(dpy
== NULL
|| lock
== NULL
)
224 XUngrabPointer(dpy
, CurrentTime
);
225 XUngrabKeyboard(dpy
, CurrentTime
);
226 XFreeColors(dpy
, DefaultColormap(dpy
, lock
->screen
), lock
->colors
, NUMCOLS
, 0);
227 XFreePixmap(dpy
, lock
->pmap
);
228 XDestroyWindow(dpy
, lock
->win
);
234 cleanup(Display
*dpy
)
238 for (s
= 0; s
< nscreens
; ++s
)
239 unlockscreen(dpy
, locks
[s
]);
246 lockscreen(Display
*dpy
, int screen
)
248 char curs
[] = {0, 0, 0, 0, 0, 0, 0, 0};
249 int i
, ptgrab
, kbgrab
;
252 XSetWindowAttributes wa
;
255 if (dpy
== NULL
|| screen
< 0 || !(lock
= malloc(sizeof(Lock
))))
258 lock
->screen
= screen
;
259 lock
->root
= RootWindow(dpy
, lock
->screen
);
261 for (i
= 0; i
< NUMCOLS
; i
++) {
262 XAllocNamedColor(dpy
, DefaultColormap(dpy
, lock
->screen
), colorname
[i
], &color
, &dummy
);
263 lock
->colors
[i
] = color
.pixel
;
267 wa
.override_redirect
= 1;
268 wa
.background_pixel
= lock
->colors
[INIT
];
269 lock
->win
= XCreateWindow(dpy
, lock
->root
, 0, 0, DisplayWidth(dpy
, lock
->screen
), DisplayHeight(dpy
, lock
->screen
),
270 0, DefaultDepth(dpy
, lock
->screen
), CopyFromParent
,
271 DefaultVisual(dpy
, lock
->screen
), CWOverrideRedirect
| CWBackPixel
, &wa
);
272 lock
->pmap
= XCreateBitmapFromData(dpy
, lock
->win
, curs
, 8, 8);
273 invisible
= XCreatePixmapCursor(dpy
, lock
->pmap
, lock
->pmap
, &color
, &color
, 0, 0);
274 XDefineCursor(dpy
, lock
->win
, invisible
);
276 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
277 for (i
= 0, ptgrab
= kbgrab
= -1; i
< 6; i
++) {
278 if (ptgrab
!= GrabSuccess
) {
279 ptgrab
= XGrabPointer(dpy
, lock
->root
, False
,
280 ButtonPressMask
| ButtonReleaseMask
|
281 PointerMotionMask
, GrabModeAsync
,
282 GrabModeAsync
, None
, invisible
, CurrentTime
);
284 if (kbgrab
!= GrabSuccess
) {
285 kbgrab
= XGrabKeyboard(dpy
, lock
->root
, True
,
286 GrabModeAsync
, GrabModeAsync
, CurrentTime
);
289 /* input is grabbed: we can lock the screen */
290 if (ptgrab
== GrabSuccess
&& kbgrab
== GrabSuccess
) {
291 XMapRaised(dpy
, lock
->win
);
293 XRRSelectInput(dpy
, lock
->win
, RRScreenChangeNotifyMask
);
295 XSelectInput(dpy
, lock
->root
, SubstructureNotifyMask
);
299 /* retry on AlreadyGrabbed but fail on other errors */
300 if ((ptgrab
!= AlreadyGrabbed
&& ptgrab
!= GrabSuccess
) ||
301 (kbgrab
!= AlreadyGrabbed
&& kbgrab
!= GrabSuccess
))
307 /* we couldn't grab all input: fail out */
308 if (ptgrab
!= GrabSuccess
)
309 fprintf(stderr
, "slock: unable to grab mouse pointer for screen %d\n", screen
);
310 if (kbgrab
!= GrabSuccess
)
311 fprintf(stderr
, "slock: unable to grab keyboard for screen %d\n", screen
);
318 die("usage: slock [-v] [cmd [arg ...]]\n");
322 main(int argc
, char **argv
) {
323 #ifndef HAVE_BSD_AUTH
331 fprintf(stderr
, "slock-"VERSION
"\n");
341 /* Check if the current user has a password entry */
343 if (!getpwuid(getuid())) {
345 die("slock: no password entry for current user\n");
347 die("slock: getpwuid: %s\n", strerror(errno
));
350 #ifndef HAVE_BSD_AUTH
353 die("slock: failed to get user password hash.\n");
356 if (!(dpy
= XOpenDisplay(NULL
)))
357 die("slock: cannot open display\n");
359 /* check for Xrandr support */
360 rr
= XRRQueryExtension(dpy
, &rrevbase
, &rrerrbase
);
362 /* get number of screens in display "dpy" and blank them */
363 nscreens
= ScreenCount(dpy
);
364 if (!(locks
= calloc(nscreens
, sizeof(Lock
*)))) {
366 die("slock: out of memory\n");
368 for (nlocks
= 0, s
= 0; s
< nscreens
; s
++) {
369 if ((locks
[s
] = lockscreen(dpy
, s
)) != NULL
)
376 /* did we manage to lock everything? */
377 if (nlocks
!= nscreens
) {
382 /* run post-lock command */
387 die("slock: fork failed: %s\n", strerror(errno
));
389 if (close(ConnectionNumber(dpy
)) < 0)
390 die("slock: close: %s\n", strerror(errno
));
391 execvp(argv
[0], argv
);
392 fprintf(stderr
, "slock: execvp %s: %s\n", argv
[0],
398 /* everything is now blank. Wait for the correct password */
405 /* password ok, unlock everything and quit */