19 Commits
1.2 ... 1.3

Author SHA1 Message Date
Markus Teich
6a52a85a1a add slock.1 man page 2016-02-11 16:51:12 +01:00
FRIGN
c28ac1ebf1 Update license year
It actually was 2014 and not 2015.
2016-02-11 16:30:52 +01:00
FRIGN
aa6a385341 Don't forget the E-Mail 2016-02-11 16:29:26 +01:00
FRIGN
6adeb1281e Add myself to License
forgot that a while ago
2016-02-11 16:28:41 +01:00
Markus Teich
f6582b68b0 update copyright year in -v output 2016-02-11 16:26:12 +01:00
Markus Teich
bfafc91da7 prepare 1.3 release 2016-02-11 16:23:48 +01:00
Markus Teich
32149e4043 remove .hgtags 2016-02-11 16:22:34 +01:00
Markus Teich
1766ecdfd4 add hint for suid to oom error message 2016-01-29 22:11:18 +01:00
Markus Teich
9dfe0ce531 error messages on grab failure 2016-01-18 16:49:15 +01:00
Markus Teich
55e827af0f code style fix 2016-01-18 16:47:41 +01:00
Markus Teich
e867c38123 add option to run command after screen is locked 2015-12-26 19:27:01 +01:00
David Phillips
b95ee111c7 Don't change to failure colour on success 2015-08-27 06:16:25 +02:00
David Phillips
0edbd2e016 Slightly safer OOM killer disablement in linux 2015-07-06 11:15:07 +02:00
Markus Teich
754195f8d7 rework setting window color 2015-05-08 18:07:05 +02:00
Markus Teich
10d4e479c5 consistently use () with sizeof 2015-05-08 16:44:18 +02:00
Nick Currier
b1289f30b7 Option to not show failure color on clear 2015-05-08 16:44:06 +02:00
Markus Teich
f5ef1b8eb5 resize lockscreen window after Xrandr resize 2015-04-01 23:25:47 +02:00
David Phillips
f2ea92c3dd Blank the screen with color 0, add third color for failed logins
- Adds another color in config.def.h, COLOR_INIT
- Renames the colours from numerical ones to ones with meaningful names;
  COLOR_INPUT for when there is content in the input buffer and COLOR_EMPTY
  for when the input buffer has been cleared (backspaced or a failed attempt).
- Ensures XFreeColors frees the right number of colours. This is now derived
  from the size of `Lock->colors` rather than being an integer literal.
- Makes slock exhibit the behaviour described by Markus

The default colours are the same as the ones slock currently uses, with the
exception of the new color, which I have set to red, as it indicates someone
has either failed an attempt to unlock, or that they have entered input and
erased it all.
2015-04-01 23:13:11 +02:00
Anselm R Garbe
a31b919111 applied Dimitris' style patch from Dec'14, with some minor modifications 2015-01-27 22:16:52 +01:00
7 changed files with 179 additions and 103 deletions

10
.hgtags
View File

@@ -1,10 +0,0 @@
0a95c73c7374fbc2342b6040d9f35ddf597729e1 0.1
da5cb1f0a685258d5315ea109860bacbc2871a57 0.2
f9157b1864388ad8f1920e5fde7c5849e73d8327 0.3
4c2cf4d6a2d0e08cbe280ec50ef76c9aecfc0fbe 0.4
bd24ea7fcca26b161225c464df23ecbfe85280e1 0.5
dd226a81c09adfa86db232419b3000b7e406df68 0.6
c4635bb35a4581261f0187b347d5e596dd390ca3 0.7
c0eb8221ba49c6d10becc93c063c45196a3bb1ba 0.8
1e8a77601cb9c872921bbfc47909d9339027b295 0.9
05b949016e85da011c48783d6896de801d347bed 1.0

View File

@@ -1,7 +1,9 @@
MIT/X Consortium License
© 2015-2016 Markus Teich <markus.teich@stusta.mhn.de>
© 2014 Dimitris Papastamos <sin@2f30.org>
© 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2014-2016 Laslo Hunhold <dev@frign.de>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

View File

@@ -35,7 +35,8 @@ clean:
dist: clean
@echo creating dist tarball
@mkdir -p slock-${VERSION}
@cp -R LICENSE Makefile README config.def.h config.mk ${SRC} slock-${VERSION}
@cp -R LICENSE Makefile README config.def.h config.mk ${SRC} slock.1 \
slock-${VERSION}
@tar -cf slock-${VERSION}.tar slock-${VERSION}
@gzip slock-${VERSION}.tar
@rm -rf slock-${VERSION}
@@ -46,9 +47,15 @@ install: all
@cp -f slock ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/slock
@chmod u+s ${DESTDIR}${PREFIX}/bin/slock
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" <slock.1 >${DESTDIR}${MANPREFIX}/man1/slock.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/slock.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/slock
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/slock.1
.PHONY: all options clean dist install uninstall

View File

@@ -1,2 +1,6 @@
#define COLOR1 "black"
#define COLOR2 "#005577"
static const char *colorname[NUMCOLS] = {
"black", /* after initialization */
"#005577", /* during input */
"#CC3333", /* failed/cleared the input */
};
static const Bool failonclear = True;

View File

@@ -1,17 +1,18 @@
# slock version
VERSION = 1.2
VERSION = 1.3
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext
LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr
# flags
CPPFLAGS = -DVERSION=\"${VERSION}\" -DHAVE_SHADOW_H

29
slock.1 Normal file
View File

@@ -0,0 +1,29 @@
.TH SLOCK 1 slock\-VERSION
.SH NAME
slock \- simple X display locker
.SH SYNOPSIS
.B slock
.RB [ \-v
|
.IR post_lock_command ]
.SH DESCRIPTION
.B slock
is a screen locker for X. If provided, the
.IR post_lock_command
will be executed when the screen is locked.
.SH OPTIONS
.TP
.B \-v
prints version information to stdout, then exits.
.SH EXAMPLES
$ slock /usr/sbin/s2ram
.SH CUSTOMIZATION
.B slock
can be customized by creating a custom config.h and (re)compiling the source
code. This keeps it fast, secure and simple.
.SH AUTHORS
See the LICENSE file for the authors.
.SH LICENSE
See the LICENSE file for the terms of redistribution.
.SH BUGS
Please report them.

219
slock.c
View File

@@ -1,4 +1,3 @@
/* See LICENSE file for license details. */
#define _XOPEN_SOURCE 500
#if HAVE_SHADOW_H
@@ -14,6 +13,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -23,73 +23,97 @@
#include <bsd_auth.h>
#endif
enum {
INIT,
INPUT,
FAILED,
NUMCOLS
};
#include "config.h"
typedef struct {
int screen;
Window root, win;
Pixmap pmap;
unsigned long colors[2];
unsigned long colors[NUMCOLS];
} Lock;
static Lock **locks;
static int nscreens;
static Bool running = True;
static Bool failure = False;
static Bool rr;
static int rrevbase;
static int rrerrbase;
static void
die(const char *errstr, ...) {
die(const char *errstr, ...)
{
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
exit(1);
}
#ifdef __linux__
#include <fcntl.h>
#include <linux/oom.h>
static void
dontkillme(void) {
dontkillme(void)
{
int fd;
int length;
char value[64];
fd = open("/proc/self/oom_score_adj", O_WRONLY);
if (fd < 0 && errno == ENOENT)
return;
if (fd < 0 || write(fd, "-1000\n", 6) != 6 || close(fd) != 0)
die("cannot disable the out-of-memory killer for this process\n");
/* convert OOM_SCORE_ADJ_MIN to string for writing */
length = snprintf(value, sizeof(value), "%d\n", OOM_SCORE_ADJ_MIN);
/* bail on truncation */
if (length >= sizeof(value))
die("buffer too small\n");
if (fd < 0 || write(fd, value, length) != length || close(fd) != 0)
die("cannot disable the out-of-memory killer for this process (make sure to suid or sgid slock)\n");
}
#endif
#ifndef HAVE_BSD_AUTH
/* only run as root */
static const char *
getpw(void) { /* only run as root */
getpw(void)
{
const char *rval;
struct passwd *pw;
errno = 0;
pw = getpwuid(getuid());
if (!pw) {
if (!(pw = getpwuid(getuid()))) {
if (errno)
die("slock: getpwuid: %s\n", strerror(errno));
else
die("slock: cannot retrieve password entry (make sure to suid or sgid slock)\n");
die("slock: cannot retrieve password entry\n");
}
rval = pw->pw_passwd;
rval = pw->pw_passwd;
#if HAVE_SHADOW_H
if (rval[0] == 'x' && rval[1] == '\0') {
struct spwd *sp;
sp = getspnam(getenv("USER"));
if(!sp)
if (!(sp = getspnam(getenv("USER"))))
die("slock: cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
rval = sp->sp_pwdp;
}
#endif
/* drop privileges */
if (geteuid() == 0
&& ((getegid() != pw->pw_gid && setgid(pw->pw_gid) < 0) || setuid(pw->pw_uid) < 0))
if (geteuid() == 0 &&
((getegid() != pw->pw_gid && setgid(pw->pw_gid) < 0) || setuid(pw->pw_uid) < 0))
die("slock: cannot drop privileges\n");
return rval;
}
@@ -104,32 +128,35 @@ readpw(Display *dpy, const char *pws)
{
char buf[32], passwd[256];
int num, screen;
unsigned int len, llen;
unsigned int len, color;
KeySym ksym;
XEvent ev;
static int oldc = INIT;
len = llen = 0;
len = 0;
running = True;
/* As "slock" stands for "Simple X display locker", the DPMS settings
* had been removed and you can set it with "xset" or some other
* utility. This way the user can easily set a customized DPMS
* timeout. */
while(running && !XNextEvent(dpy, &ev)) {
if(ev.type == KeyPress) {
while (running && !XNextEvent(dpy, &ev)) {
if (ev.type == KeyPress) {
buf[0] = 0;
num = XLookupString(&ev.xkey, buf, sizeof buf, &ksym, 0);
if(IsKeypadKey(ksym)) {
if(ksym == XK_KP_Enter)
num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
if (IsKeypadKey(ksym)) {
if (ksym == XK_KP_Enter)
ksym = XK_Return;
else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
ksym = (ksym - XK_KP_0) + XK_0;
}
if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|| IsPrivateKeypadKey(ksym))
if (IsFunctionKey(ksym) ||
IsKeypadKey(ksym) ||
IsMiscFunctionKey(ksym) ||
IsPFKey(ksym) ||
IsPrivateKeypadKey(ksym))
continue;
switch(ksym) {
switch (ksym) {
case XK_Return:
passwd[len] = 0;
#ifdef HAVE_BSD_AUTH
@@ -137,49 +164,55 @@ readpw(Display *dpy, const char *pws)
#else
running = !!strcmp(crypt(passwd, pws), pws);
#endif
if(running)
if (running) {
XBell(dpy, 100);
failure = True;
}
len = 0;
break;
case XK_Escape:
len = 0;
break;
case XK_BackSpace:
if(len)
if (len)
--len;
break;
default:
if(num && !iscntrl((int) buf[0]) && (len + num < sizeof passwd)) {
if (num && !iscntrl((int)buf[0]) && (len + num < sizeof(passwd))) {
memcpy(passwd + len, buf, num);
len += num;
}
break;
}
if(llen == 0 && len != 0) {
for(screen = 0; screen < nscreens; screen++) {
XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[1]);
color = len ? INPUT : (failure || failonclear ? FAILED : INIT);
if (running && oldc != color) {
for (screen = 0; screen < nscreens; screen++) {
XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[color]);
XClearWindow(dpy, locks[screen]->win);
}
} else if(llen != 0 && len == 0) {
for(screen = 0; screen < nscreens; screen++) {
XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[0]);
oldc = color;
}
} else if (rr && ev.type == rrevbase + RRScreenChangeNotify) {
XRRScreenChangeNotifyEvent *rre = (XRRScreenChangeNotifyEvent*)&ev;
for (screen = 0; screen < nscreens; screen++) {
if (locks[screen]->win == rre->window) {
XResizeWindow(dpy, locks[screen]->win, rre->width, rre->height);
XClearWindow(dpy, locks[screen]->win);
}
}
llen = len;
}
else for(screen = 0; screen < nscreens; screen++)
} else for (screen = 0; screen < nscreens; screen++)
XRaiseWindow(dpy, locks[screen]->win);
}
}
static void
unlockscreen(Display *dpy, Lock *lock) {
unlockscreen(Display *dpy, Lock *lock)
{
if(dpy == NULL || lock == NULL)
return;
XUngrabPointer(dpy, CurrentTime);
XFreeColors(dpy, DefaultColormap(dpy, lock->screen), lock->colors, 2, 0);
XFreeColors(dpy, DefaultColormap(dpy, lock->screen), lock->colors, NUMCOLS, 0);
XFreePixmap(dpy, lock->pmap);
XDestroyWindow(dpy, lock->win);
@@ -187,69 +220,71 @@ unlockscreen(Display *dpy, Lock *lock) {
}
static Lock *
lockscreen(Display *dpy, int screen) {
lockscreen(Display *dpy, int screen)
{
char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned int len;
int i;
Lock *lock;
XColor color, dummy;
XSetWindowAttributes wa;
Cursor invisible;
if(dpy == NULL || screen < 0)
return NULL;
lock = malloc(sizeof(Lock));
if(lock == NULL)
if (!running || dpy == NULL || screen < 0 || !(lock = malloc(sizeof(Lock))))
return NULL;
lock->screen = screen;
lock->root = RootWindow(dpy, lock->screen);
for (i = 0; i < NUMCOLS; i++) {
XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), colorname[i], &color, &dummy);
lock->colors[i] = color.pixel;
}
/* init */
wa.override_redirect = 1;
wa.background_pixel = BlackPixel(dpy, lock->screen);
wa.background_pixel = lock->colors[INIT];
lock->win = XCreateWindow(dpy, lock->root, 0, 0, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen),
0, DefaultDepth(dpy, lock->screen), CopyFromParent,
DefaultVisual(dpy, lock->screen), CWOverrideRedirect | CWBackPixel, &wa);
XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), COLOR2, &color, &dummy);
lock->colors[1] = color.pixel;
XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), COLOR1, &color, &dummy);
lock->colors[0] = color.pixel;
0, DefaultDepth(dpy, lock->screen), CopyFromParent,
DefaultVisual(dpy, lock->screen), CWOverrideRedirect | CWBackPixel, &wa);
lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, &color, &color, 0, 0);
XDefineCursor(dpy, lock->win, invisible);
XMapRaised(dpy, lock->win);
for(len = 1000; len; len--) {
if(XGrabPointer(dpy, lock->root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
if (rr)
XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
/* Try to grab mouse pointer *and* keyboard, else fail the lock */
for (len = 1000; len; len--) {
if (XGrabPointer(dpy, lock->root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
break;
usleep(1000);
}
if(running && (len > 0)) {
for(len = 1000; len; len--) {
if(XGrabKeyboard(dpy, lock->root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
== GrabSuccess)
break;
if (!len) {
fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", screen);
} else {
for (len = 1000; len; len--) {
if (XGrabKeyboard(dpy, lock->root, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) {
/* everything fine, we grabbed both inputs */
XSelectInput(dpy, lock->root, SubstructureNotifyMask);
return lock;
}
usleep(1000);
}
fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", screen);
}
running &= (len > 0);
if(!running) {
unlockscreen(dpy, lock);
lock = NULL;
}
else
XSelectInput(dpy, lock->root, SubstructureNotifyMask);
return lock;
/* grabbing one of the inputs failed */
running = 0;
unlockscreen(dpy, lock);
return NULL;
}
static void
usage(void) {
fprintf(stderr, "usage: slock [-v]\n");
exit(EXIT_FAILURE);
usage(void)
{
fprintf(stderr, "usage: slock [-v|POST_LOCK_CMD]\n");
exit(1);
}
int
@@ -260,43 +295,51 @@ main(int argc, char **argv) {
Display *dpy;
int screen;
if((argc == 2) && !strcmp("-v", argv[1]))
die("slock-%s, © 2006-2014 slock engineers\n", VERSION);
else if(argc != 1)
if ((argc == 2) && !strcmp("-v", argv[1]))
die("slock-%s, © 2006-2016 slock engineers\n", VERSION);
if ((argc == 2) && !strcmp("-h", argv[1]))
usage();
#ifdef __linux__
dontkillme();
#endif
if(!getpwuid(getuid()))
if (!getpwuid(getuid()))
die("slock: no passwd entry for you\n");
#ifndef HAVE_BSD_AUTH
pws = getpw();
#endif
if(!(dpy = XOpenDisplay(0)))
if (!(dpy = XOpenDisplay(0)))
die("slock: cannot open display\n");
rr = XRRQueryExtension(dpy, &rrevbase, &rrerrbase);
/* Get the number of screens in display "dpy" and blank them all. */
nscreens = ScreenCount(dpy);
locks = malloc(sizeof(Lock *) * nscreens);
if(locks == NULL)
if (!(locks = malloc(sizeof(Lock*) * nscreens)))
die("slock: malloc: %s\n", strerror(errno));
int nlocks = 0;
for(screen = 0; screen < nscreens; screen++) {
if ( (locks[screen] = lockscreen(dpy, screen)) != NULL)
for (screen = 0; screen < nscreens; screen++) {
if ((locks[screen] = lockscreen(dpy, screen)) != NULL)
nlocks++;
}
XSync(dpy, False);
/* Did we actually manage to lock something? */
if (nlocks == 0) { // nothing to protect
if (nlocks == 0) { /* nothing to protect */
free(locks);
XCloseDisplay(dpy);
return 1;
}
if (argc >= 2 && fork() == 0) {
if (dpy)
close(ConnectionNumber(dpy));
execvp(argv[1], argv+1);
die("slock: execvp %s failed: %s\n", argv[1], strerror(errno));
}
/* Everything is now blank. Now wait for the correct password. */
#ifdef HAVE_BSD_AUTH
readpw(dpy);
@@ -305,7 +348,7 @@ main(int argc, char **argv) {
#endif
/* Password ok, unlock everything and quit. */
for(screen = 0; screen < nscreens; screen++)
for (screen = 0; screen < nscreens; screen++)
unlockscreen(dpy, locks[screen]);
free(locks);