Skip to content

Commit

Permalink
Add swaylockd
Browse files Browse the repository at this point in the history
  • Loading branch information
jirutka committed Sep 8, 2021
1 parent 5cb9579 commit aafa6fb
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
6 changes: 6 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ executable('swaylock',
install: true
)

executable('swaylockd',
'swaylockd.c',
install: true
)

install_data(
'pam/swaylock',
install_dir: sysconfdir + '/pam.d/'
Expand All @@ -192,6 +197,7 @@ if scdoc.found()
mandir = get_option('mandir')
man_files = [
'swaylock.1.scd',
'swaylockd.1.scd',
]
foreach filename : man_files
topic = filename.split('.')[-3].split('/')[-1]
Expand Down
60 changes: 60 additions & 0 deletions swaylockd.1.scd
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
swaylockd(1)

# NAME

swaylockd - launch swaylock(1) and re-launch if it dies


# SYNOPSIS

*swaylockd* [options...]

All options are directly passed to *swaylock(1)*.


# DESCRIPTION

*swaylockd* is a dumb launcher to spawn *swaylock(1)* and ensure it's running no matter what - it immediately restarts *swaylock(1)* if terminated by a signal (e.g. the process crashed) and also blocks all signals (except `SIGKILL` and `SIGSTOP`).
It also ensures that only one instance per user is running (using *flock(2)*).

*IMPORTANT*: *swaylockd* is not compatible with *swaylock(1)* option *--daemonize*!


# ENVIRONMENT

*XDG_RUNTIME_DIR*:
This variable must be set and contain the path to the existing directory where the lock file will be created.
Refer to "XDG Base Directory Specification" for more information.


# FILES

*${XDG_RUNTIME_DIR}/swaylockd.lock*
A lock file created when *swaylockd* is executed and removed right before it quits.


# LOGGING

Error messages generated by *swaylockd* are printed to STDERR and logged to syslog with ident string "swaylockd" and facility code 1 (user).


# HISTORY

*swaylockd* has been developed mainly as a workaround for the *swaylock(1)* stability issues (see e.g. https://github.com/swaywm/swaylock/issues/181).

*swaylock* is a critical piece of security software - as a screen locker, any bug in the program that causes it to crash will cause the screen to unlock.
As soon as *swaylock* is no longer running, the screen is no longer locked.
Therefore, great care must be taken to ensure that it never crash.

*swaylockd* started (and is still available) as a standalone project at https://github.com/jirutka/swaylockd.
The code has been merged into sway-effects.


# AUTHORS

Jakub Jirutka


# SEE ALSO

*swaylock(1)*
101 changes: 101 additions & 0 deletions swaylockd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// vim: set ts=4:
// SPDX-FileCopyrightText: 2021 Jakub Jirutka <[email protected]>
// SPDX-License-Identifier: MIT

#define _POSIX_C_SOURCE 200809L

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <spawn.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/wait.h>

#define PROGNAME "swaylockd"

#ifndef SWAYLOCK_PATH
#define SWAYLOCK_PATH "/usr/bin/swaylock"
#endif

#define logerr(format, ...) \
do { \
fprintf(stderr, PROGNAME ": " format "\n", __VA_ARGS__); \
if (syslog_enabled) syslog(LOG_ERR, format, __VA_ARGS__); \
} while (0)


extern char **environ;

static bool syslog_enabled = true;

int main (int argc, char **argv) {
char lock_path[PATH_MAX] = "\0";
int lock_fd = -1;
int rc = 1;

// Open connection to syslog.
openlog(PROGNAME, LOG_PID, LOG_USER);

{
const char *xdg_rd = getenv("XDG_RUNTIME_DIR");
if (xdg_rd == NULL) {
logerr("%s", "XDG_RUNTIME_DIR not set in the environment");
return 100;
}
(void) snprintf(lock_path, sizeof(lock_path), "%s/%s.lock", xdg_rd, PROGNAME);
}

// Obtain exclusive lock.
if ((lock_fd = open(lock_path, O_CREAT | O_RDWR | O_CLOEXEC, 0600)) < 0) {
logerr("failed to write %s: %s", lock_path, strerror(errno));
return 101;
}
if (flock(lock_fd, LOCK_EX | LOCK_NB) < 0) {
logerr("another instance of %s is running", PROGNAME);
return 102;
}

// Block (ignore) all signals we can (i.e. except SIGKILL and SIGSTOP).
{
sigset_t mask;
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
}

argv[0] = SWAYLOCK_PATH;

for (int i = 1;; i++) {
pid_t pid;
if (posix_spawn(&pid, SWAYLOCK_PATH, NULL, 0, argv, environ) != 0) {
logerr("failed to spawn %s: %s", SWAYLOCK_PATH, strerror(errno));
rc = 101;
goto done;
}

int status = -1;
(void) waitpid(pid, &status, 0);

// Exit only if the child terminated normally, not via signal.
if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
goto done;
}
logerr("%s terminated with signal %d, restarting (%d)",
SWAYLOCK_PATH, WIFSIGNALED(status) ? WTERMSIG(status) : -1, i);

// Just to make sure we don't flood syslog in case of some bug...
if (i > 100) syslog_enabled = false;
}

done:
if (unlink(lock_path) < 0) {
logerr("failed to remove lock file %s: %s", lock_path, strerror(errno));
}
return rc;
}

0 comments on commit aafa6fb

Please sign in to comment.