unl0kr: add a systemd password agent
This commit is contained in:
@@ -10,6 +10,7 @@ If a change only affects particular applications, they are listed in parentheses
|
||||
|
||||
## Unreleased
|
||||
|
||||
- feat: Add a systemd password agent (!33, thanks @vstoiakin)
|
||||
- feat: Load config from /usr/share aswell (!26 & !28, thanks @fossdd & @pabloyoyoista)
|
||||
- feat(buffyboard): Add man pages (!23, thanks @Jarrah)
|
||||
- misc: Unify build system (!23 & !29, thanks @Jarrah & @vladimir.stoyakin)
|
||||
|
@@ -45,7 +45,14 @@ $ sudo ./_build/unl0kr/unl0kr # For Unl0kr
|
||||
$ sudo ./_build/buffyboard/buffyboard # For Buffyboard
|
||||
```
|
||||
|
||||
With meson <0\.55 use `ninja` instead of `meson compile`\.
|
||||
On distributions based on systemd, `unl0kr` can be used as a [password agent](https://systemd.io/PASSWORD_AGENTS/), to give systemd the ability to ask passwords on touchscreen-only devices:
|
||||
|
||||
```
|
||||
# systemctl stop systemd-ask-password-console.path
|
||||
# systemctl stop systemd-ask-password-wall.path
|
||||
# systemctl start unl0kr-agent.path
|
||||
# systemd-ask-password --no-tty # Unl0kr is started
|
||||
```
|
||||
|
||||
## Making a release
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
project('buffybox', 'c',
|
||||
version: '3.2.0',
|
||||
default_options: 'warning_level=3',
|
||||
default_options: ['warning_level=3', 'b_ndebug=if-release'],
|
||||
meson_version: '>= 0.59.0'
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ add_project_arguments('-DPROJECT_VERSION="@0@"'.format(meson.project_version()),
|
||||
depinih = dependency('inih')
|
||||
deplibinput = dependency('libinput')
|
||||
deplibudev = dependency('libudev')
|
||||
depxkbcommon = dependency('xkbcommon') # For unl0kr only
|
||||
|
||||
if get_option('man')
|
||||
depscdoc = dependency('scdoc', native: true)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
option('with-drm', type: 'feature', value: 'auto', description: 'Enable DRM backend')
|
||||
option('man', type: 'boolean', value: true, description: 'Install manual pages')
|
||||
option('systemd-buffyboard-service', type: 'feature', value: 'auto', description: 'Install systemd service file for buffyboard')
|
||||
option('systemd-password-agent', type: 'feature', value: 'auto', description: 'Build a systemd password agent for touchscreens')
|
||||
|
@@ -1,8 +1,6 @@
|
||||
# Copyright 2021 Clayton Craft
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
depxkbcommon = dependency('xkbcommon')
|
||||
|
||||
unl0kr_sources = files(
|
||||
'backends.c',
|
||||
'command_line.c',
|
||||
@@ -32,3 +30,25 @@ executable('unl0kr',
|
||||
|
||||
install_data('unl0kr.conf', install_dir: get_option('sysconfdir'))
|
||||
|
||||
depsystemd = dependency('systemd', required: get_option('systemd-password-agent'))
|
||||
if depsystemd.found()
|
||||
executable('unl0kr-agent',
|
||||
sources: files('unl0kr-agent.c'),
|
||||
dependencies: depinih,
|
||||
c_args: '-DUNL0KR_BINARY="@0@"'.format(get_option('prefix') / get_option('bindir') / 'unl0kr'),
|
||||
install: true,
|
||||
install_dir: get_option('libexecdir')
|
||||
)
|
||||
|
||||
system_unit_dir = depsystemd.get_variable(pkgconfig: 'systemd_system_unit_dir')
|
||||
|
||||
install_data('unl0kr-agent.path', install_dir: system_unit_dir)
|
||||
|
||||
configure_file(
|
||||
configuration: {'LIBEXECDIR': get_option('prefix') / get_option('libexecdir')},
|
||||
input: 'unl0kr-agent.service.in',
|
||||
output: 'unl0kr-agent.service',
|
||||
install: true,
|
||||
install_dir: system_unit_dir
|
||||
)
|
||||
endif
|
||||
|
665
unl0kr/unl0kr-agent.c
Normal file
665
unl0kr/unl0kr-agent.c
Normal file
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define INI_STOP_ON_FIRST_ERROR 0 /* Ignore unknown keys */
|
||||
|
||||
#include <ini.h>
|
||||
|
||||
struct Request
|
||||
{
|
||||
uint64_t not_after;
|
||||
char* file;
|
||||
char* socket;
|
||||
char* message;
|
||||
char* icon;
|
||||
pid_t pid;
|
||||
bool accept_cached;
|
||||
bool echo;
|
||||
bool silent;
|
||||
};
|
||||
|
||||
void Request_init(struct Request* req)
|
||||
{
|
||||
req->not_after = 0;
|
||||
req->file = NULL;
|
||||
req->socket = NULL;
|
||||
req->message = NULL;
|
||||
req->icon = NULL;
|
||||
req->pid = 0;
|
||||
req->accept_cached = false;
|
||||
req->echo = false;
|
||||
req->silent = false;
|
||||
}
|
||||
|
||||
void Request_free(struct Request* req)
|
||||
{
|
||||
if (req->file)
|
||||
free(req->file);
|
||||
if (req->socket)
|
||||
free(req->socket);
|
||||
if (req->message)
|
||||
free(req->message);
|
||||
if (req->icon)
|
||||
free(req->icon);
|
||||
}
|
||||
|
||||
void Request_reset(struct Request* req)
|
||||
{
|
||||
Request_free(req);
|
||||
Request_init(req);
|
||||
}
|
||||
|
||||
struct Request request;
|
||||
int fd_epoll, fd_inotify;
|
||||
pid_t pid_unl0kr;
|
||||
bool unl0kr_exited;
|
||||
|
||||
timer_t id_timer;
|
||||
enum {
|
||||
NO_ACTION,
|
||||
TERMINATE_UNL0KR,
|
||||
KILL_UNL0KR
|
||||
} timer_action;
|
||||
bool timer_expired;
|
||||
|
||||
void erase_and_free(char* p)
|
||||
{
|
||||
const size_t length = strlen(p);
|
||||
for (size_t i = 0; i < length; i++)
|
||||
p[i] = 0;
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
int send_password(const char *password)
|
||||
{
|
||||
int fd_socket = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
if (fd_socket < 0) {
|
||||
int ret = errno;
|
||||
perror("socket() is failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_un address;
|
||||
address.sun_family = AF_UNIX;
|
||||
strncpy(address.sun_path, request.socket, sizeof(address.sun_path) - 1);
|
||||
address.sun_path[sizeof(address.sun_path) - 1] = 0;
|
||||
|
||||
ssize_t n = sendto(fd_socket, password, strlen(password), MSG_NOSIGNAL, (const struct sockaddr*) &address, sizeof(address));
|
||||
if (n < 0) {
|
||||
int ret = errno;
|
||||
perror("sendto() is failed");
|
||||
close(fd_socket);
|
||||
return ret;
|
||||
}
|
||||
|
||||
close(fd_socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool to_bool(const char* value)
|
||||
{
|
||||
if (strcmp(value, "true") == 0) {
|
||||
return true;
|
||||
} else if (strcmp(value, "1") == 0) {
|
||||
return true;
|
||||
} else if (strcmp(value, "yes") == 0) {
|
||||
return true;
|
||||
} else if (strcmp(value, "false") == 0) {
|
||||
return false;
|
||||
} else if (strcmp(value, "0") == 0) {
|
||||
return false;
|
||||
} else if (strcmp(value, "no") == 0) {
|
||||
return false;
|
||||
} else if (strcmp(value, "") == 0) {
|
||||
return false;
|
||||
} else {
|
||||
fprintf(stderr, "The value '%s' is not a boolean\n", value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int ini_parser(void* user, const char* section, const char* name, const char* value)
|
||||
{
|
||||
struct Request* d = (struct Request*) user;
|
||||
|
||||
if (strcmp(section, "Ask") != 0) {
|
||||
fprintf(stderr, "The ini file contains unknown section: %s\n", section);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "NotAfter") == 0) {
|
||||
d->not_after = atol(value);
|
||||
} else if (strcmp(name, "Socket") == 0) {
|
||||
d->socket = strdup(value);
|
||||
} else if (strcmp(name, "Message") == 0) {
|
||||
d->message = strdup(value);
|
||||
} else if (strcmp(name, "Icon") == 0) {
|
||||
d->icon = strdup(value);
|
||||
} else if (strcmp(name, "PID") == 0) {
|
||||
d->pid = atoi(value);
|
||||
} else if (strcmp(name, "AcceptCached") == 0) {
|
||||
d->accept_cached = to_bool(value);
|
||||
} else if (strcmp(name, "Echo") == 0) {
|
||||
d->echo = to_bool(value);
|
||||
} else if (strcmp(name, "Silent") == 0) {
|
||||
d->silent = to_bool(value);
|
||||
} else {
|
||||
fprintf(stderr, "The ini file contains unknown key: %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int find_request(char** ret_file)
|
||||
{
|
||||
const char* ask_folder = "/run/systemd/ask-password";
|
||||
const size_t ask_folder_length = strlen(ask_folder);
|
||||
|
||||
DIR* dir = opendir(ask_folder);
|
||||
if (!dir) {
|
||||
int ret = errno;
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "Can't open '%s': %s\n", ask_folder, strerror(errno));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (entry->d_type != DT_REG && entry->d_type != DT_LNK)
|
||||
continue;
|
||||
|
||||
if (strncmp(entry->d_name, "ask.", 4) != 0)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
if (!entry) {
|
||||
closedir(dir);
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
char* file = malloc(ask_folder_length + 1 + strlen(entry->d_name) + 1);
|
||||
if (!file) {
|
||||
closedir(dir);
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
strcpy(file, ask_folder);
|
||||
strcpy(file + ask_folder_length, "/");
|
||||
strcpy(file + ask_folder_length + 1, entry->d_name);
|
||||
|
||||
closedir(dir);
|
||||
|
||||
*ret_file = file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_inotify_events()
|
||||
{
|
||||
/* We expect only IN_DELETE_SELF and IN_IGNORED */
|
||||
size_t buffer_size = sizeof(struct inotify_event) * 2;
|
||||
uint8_t buffer[buffer_size];
|
||||
|
||||
ssize_t block_size = read(fd_inotify, buffer, buffer_size);
|
||||
if (block_size < 0) {
|
||||
int ret = errno;
|
||||
perror("read() is failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert((size_t) block_size == buffer_size);
|
||||
|
||||
struct inotify_event* ievent1 = (struct inotify_event*) buffer;
|
||||
struct inotify_event* ievent2 = ievent1 + 1;
|
||||
|
||||
assert(ievent1->mask & IN_DELETE_SELF);
|
||||
assert(ievent2->mask & IN_IGNORED);
|
||||
|
||||
assert(read(fd_inotify, buffer, buffer_size) == -1 && errno == EAGAIN); // no more events
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sigalarm(int signo, siginfo_t *info, void *context)
|
||||
{
|
||||
assert(signo == SIGALRM);
|
||||
(void)(context);
|
||||
|
||||
if (info->si_code == SI_TIMER) {
|
||||
assert(info->si_value.sival_ptr == &id_timer);
|
||||
timer_expired = true;
|
||||
}
|
||||
|
||||
switch (timer_action) {
|
||||
case TERMINATE_UNL0KR:
|
||||
if (kill(pid_unl0kr, SIGTERM) == 0) {
|
||||
struct itimerspec spec;
|
||||
spec.it_interval.tv_sec = 0;
|
||||
spec.it_interval.tv_nsec = 0;
|
||||
spec.it_value.tv_sec = 5;
|
||||
spec.it_value.tv_nsec = 0;
|
||||
timer_settime(id_timer, 0, &spec, NULL);
|
||||
timer_action = KILL_UNL0KR;
|
||||
}
|
||||
break;
|
||||
case KILL_UNL0KR:
|
||||
kill(pid_unl0kr, SIGKILL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sigchild(int signo, siginfo_t *info, void *context)
|
||||
{
|
||||
assert(signo == SIGCHLD);
|
||||
(void)(context);
|
||||
|
||||
if (pid_unl0kr == 0)
|
||||
return;
|
||||
|
||||
assert(info->si_pid == pid_unl0kr);
|
||||
|
||||
if (info->si_code == CLD_EXITED ||
|
||||
info->si_code == CLD_KILLED ||
|
||||
info->si_code == CLD_DUMPED ) {
|
||||
unl0kr_exited = true;
|
||||
}
|
||||
}
|
||||
|
||||
int event_loop(pid_t pid)
|
||||
{
|
||||
int ret = 0;
|
||||
int r;
|
||||
|
||||
if (request.not_after != 0) {
|
||||
struct itimerspec spec;
|
||||
spec.it_interval.tv_sec = 0;
|
||||
spec.it_interval.tv_nsec = 0;
|
||||
spec.it_value.tv_sec = request.not_after / 1000000;
|
||||
spec.it_value.tv_nsec = (request.not_after % 1000000) * 1000;
|
||||
r = timer_settime(id_timer, TIMER_ABSTIME, &spec, NULL);
|
||||
if (r == -1)
|
||||
perror("timer_settime() is failed");
|
||||
}
|
||||
|
||||
struct epoll_event event;
|
||||
pid_unl0kr = pid;
|
||||
timer_action = TERMINATE_UNL0KR;
|
||||
timer_expired = false;
|
||||
unl0kr_exited = false;
|
||||
|
||||
sigset_t sigmask;
|
||||
sigemptyset(&sigmask);
|
||||
|
||||
for (;;) {
|
||||
r = epoll_pwait(fd_epoll, &event, 1, -1, &sigmask);
|
||||
if (r == -1) {
|
||||
if (errno != EINTR) {
|
||||
ret = errno;
|
||||
perror("epoll_pwait() is failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (unl0kr_exited)
|
||||
break;
|
||||
|
||||
if (timer_expired && ret != ECANCELED)
|
||||
ret = ETIME;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
r = process_inotify_events();
|
||||
if (r != 0) {
|
||||
ret = errno;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ECANCELED;
|
||||
|
||||
if (timer_expired)
|
||||
continue;
|
||||
|
||||
if (request.not_after != 0) {
|
||||
struct itimerspec spec;
|
||||
spec.it_interval.tv_sec = 0;
|
||||
spec.it_interval.tv_nsec = 0;
|
||||
spec.it_value.tv_sec = 0;
|
||||
spec.it_value.tv_nsec = 0;
|
||||
r = timer_settime(id_timer, 0, &spec, NULL);
|
||||
if (r == -1)
|
||||
perror("Disarming the timer is failed");
|
||||
}
|
||||
|
||||
r = raise(SIGALRM);
|
||||
if (r != 0) {
|
||||
ret = errno;
|
||||
perror("raise() is failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timer_action = NO_ACTION;
|
||||
pid_unl0kr = 0;
|
||||
|
||||
/* Stop the timer unconditionally because it can be armed by sigalarm() */
|
||||
struct itimerspec spec;
|
||||
spec.it_interval.tv_sec = 0;
|
||||
spec.it_interval.tv_nsec = 0;
|
||||
spec.it_value.tv_sec = 0;
|
||||
spec.it_value.tv_nsec = 0;
|
||||
r = timer_settime(id_timer, 0, &spec, NULL);
|
||||
if (r == -1)
|
||||
perror("Disarming the timer is failed");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int exec_unl0kr(char** ret_password)
|
||||
{
|
||||
int ret = 0;
|
||||
int r;
|
||||
|
||||
int fd_pipe[2];
|
||||
if (pipe(fd_pipe) != 0) {
|
||||
ret = errno;
|
||||
perror("Can't create a pipe");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sigset_t used_signals;
|
||||
sigemptyset(&used_signals);
|
||||
sigaddset(&used_signals, SIGCHLD);
|
||||
sigaddset(&used_signals, SIGALRM);
|
||||
|
||||
r = sigprocmask(SIG_BLOCK, &used_signals, NULL);
|
||||
if (r == -1) {
|
||||
ret = errno;
|
||||
perror("sigprocmask(SIG_BLOCK) is failed");
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
ret = errno;
|
||||
perror("fork() is failed");
|
||||
goto exit2;
|
||||
}
|
||||
if (pid == 0) {
|
||||
/* Child */
|
||||
close(fd_pipe[0]);
|
||||
|
||||
if (dup2(fd_pipe[1], STDOUT_FILENO) == -1) {
|
||||
perror("dup2() is failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
execl(UNL0KR_BINARY, "unl0kr", (char*) 0);
|
||||
|
||||
perror("exec() is failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Parent */
|
||||
r = event_loop(pid);
|
||||
|
||||
int status;
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
ret = errno;
|
||||
perror("waitpid() is failed");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
if (r != 0) {
|
||||
ret = r;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
if (!WIFEXITED(status)) {
|
||||
ret = ECHILD;
|
||||
fprintf(stderr, "unl0kr terminated abnormally\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
int password_size;
|
||||
if (ioctl(fd_pipe[0], FIONREAD, &password_size) == -1) {
|
||||
ret = errno;
|
||||
perror("ioctl() is failed");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
char* password = malloc(1 + password_size + 1);
|
||||
if (!password) {
|
||||
ret = ENOMEM;
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
password[0] = '+';
|
||||
if (password_size != 0) {
|
||||
password_size = read(fd_pipe[0], password + 1, password_size);
|
||||
if (password_size == -1) {
|
||||
ret = errno;
|
||||
perror("read() is failed");
|
||||
free(password);
|
||||
goto exit2;
|
||||
}
|
||||
}
|
||||
password[1 + password_size] = 0;
|
||||
|
||||
*ret_password = password;
|
||||
exit2:
|
||||
r = sigprocmask(SIG_UNBLOCK, &used_signals, NULL);
|
||||
if (r == -1)
|
||||
perror("sigprocmask(SIG_UNBLOCK) is failed");
|
||||
exit1:
|
||||
close(fd_pipe[0]);
|
||||
close(fd_pipe[1]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wait_for_file_removed()
|
||||
{
|
||||
struct epoll_event event;
|
||||
|
||||
int r = epoll_wait(fd_epoll, &event, 1, 20000);
|
||||
if (r == -1) {
|
||||
int ret = errno;
|
||||
perror("epoll_wait() is failed");
|
||||
return ret;
|
||||
} else if (r == 0) {
|
||||
fprintf(stderr, "The file '%s' was not removed as expected, exiting.\n", request.file);
|
||||
return ETIME;
|
||||
}
|
||||
|
||||
return process_inotify_events();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int exit_code = EXIT_SUCCESS;
|
||||
int r;
|
||||
|
||||
Request_init(&request);
|
||||
|
||||
fd_epoll = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (fd_epoll == -1) {
|
||||
perror("epoll_create1() is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
fd_inotify = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
|
||||
if (fd_inotify == -1) {
|
||||
perror("inotify_init1() is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
struct epoll_event epevent_inotify;
|
||||
epevent_inotify.events = EPOLLIN|EPOLLET;
|
||||
epevent_inotify.data.fd = fd_inotify;
|
||||
|
||||
r = epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd_inotify, &epevent_inotify);
|
||||
if (r == -1) {
|
||||
perror("epoll_ctl() is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
struct sigevent sigevent_timer;
|
||||
sigevent_timer.sigev_notify = SIGEV_SIGNAL;
|
||||
sigevent_timer.sigev_signo = SIGALRM;
|
||||
sigevent_timer.sigev_value.sival_ptr = &id_timer;
|
||||
|
||||
r = timer_create(CLOCK_MONOTONIC, &sigevent_timer, &id_timer);
|
||||
if (r == -1) {
|
||||
perror("timer_create() is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
struct sigaction sigaction_alarm;
|
||||
sigaction_alarm.sa_sigaction = sigalarm;
|
||||
sigaction_alarm.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sigaction_alarm.sa_mask);
|
||||
sigaddset(&sigaction_alarm.sa_mask, SIGCHLD);
|
||||
|
||||
r = sigaction(SIGALRM, &sigaction_alarm, NULL);
|
||||
if (r == -1) {
|
||||
perror("sigaction() for SIGALRM is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto exit4;
|
||||
}
|
||||
|
||||
struct sigaction sigaction_child;
|
||||
sigaction_child.sa_sigaction = sigchild;
|
||||
sigaction_child.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
|
||||
sigemptyset(&sigaction_child.sa_mask);
|
||||
sigaddset(&sigaction_child.sa_mask, SIGALRM);
|
||||
|
||||
r = sigaction(SIGCHLD, &sigaction_child, NULL);
|
||||
if (r == -1) {
|
||||
perror("sigaction() for SIGCHLD is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
goto exit4;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
char* file;
|
||||
r = find_request(&file);
|
||||
if (r != 0) {
|
||||
if (r != ENOENT)
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
int wd_inotify = inotify_add_watch(fd_inotify, file, IN_DELETE_SELF | IN_DONT_FOLLOW);
|
||||
if (wd_inotify == -1) {
|
||||
fprintf(stderr, "inotify_add_watch() is failed for '%s': %s\n", file, strerror(errno));
|
||||
free(file);
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
Request_reset(&request);
|
||||
request.file = file;
|
||||
|
||||
r = ini_parse(file, ini_parser, &request);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "The file '%s' can't be parsed: %d\n", request.file, r);
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (request.pid != 0) {
|
||||
r = kill(request.pid, 0);
|
||||
if (r == -1 && errno == ESRCH) {
|
||||
fprintf(stderr, "The file '%s' contains invalid PID, removing.\n", request.file);
|
||||
remove(request.file);
|
||||
goto loop1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!request.socket) {
|
||||
fprintf(stderr, "The file '%s' doesn't contain a socket, waiting for removal.\n", request.file);
|
||||
goto loop1;
|
||||
}
|
||||
|
||||
if (request.not_after != 0) {
|
||||
struct timespec ts;
|
||||
r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (r == -1) {
|
||||
perror("clock_gettime() is failed");
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
uint64_t now = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
|
||||
if (request.not_after <= now) {
|
||||
fprintf(stderr, "The request '%s' expired, waiting for removal.\n", request.file);
|
||||
goto loop1;
|
||||
}
|
||||
}
|
||||
|
||||
char* password;
|
||||
r = exec_unl0kr(&password);
|
||||
if (r != 0) {
|
||||
if (r == ECANCELED)
|
||||
continue;
|
||||
else if (r == ETIME) {
|
||||
send_password("-");
|
||||
goto loop1;
|
||||
} else {
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = send_password(password);
|
||||
erase_and_free(password);
|
||||
if (r != 0) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
loop1:
|
||||
r = wait_for_file_removed();
|
||||
if (r != 0) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit4:
|
||||
timer_delete(id_timer);
|
||||
exit3:
|
||||
close(fd_inotify);
|
||||
exit2:
|
||||
close(fd_epoll);
|
||||
exit1:
|
||||
Request_free(&request);
|
||||
return exit_code;
|
||||
}
|
18
unl0kr/unl0kr-agent.path
Normal file
18
unl0kr/unl0kr-agent.path
Normal file
@@ -0,0 +1,18 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
[Unit]
|
||||
Description=Dispatch Password Requests to unl0kr Directory Watch
|
||||
|
||||
ConditionPathExists=!/run/plymouth/pid
|
||||
|
||||
DefaultDependencies=no
|
||||
After=plymouth-start.service
|
||||
Before=paths.target cryptsetup.target
|
||||
Conflicts=emergency.service
|
||||
Before=emergency.service
|
||||
Conflicts=shutdown.target
|
||||
Before=shutdown.target
|
||||
|
||||
[Path]
|
||||
DirectoryNotEmpty=/run/systemd/ask-password
|
||||
MakeDirectory=yes
|
16
unl0kr/unl0kr-agent.service.in
Normal file
16
unl0kr/unl0kr-agent.service.in
Normal file
@@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
[Unit]
|
||||
Description=Dispatch Password Requests to unl0kr
|
||||
|
||||
ConditionPathExists=!/run/plymouth/pid
|
||||
|
||||
DefaultDependencies=no
|
||||
After=plymouth-start.service
|
||||
Conflicts=emergency.service
|
||||
Before=emergency.service
|
||||
Conflicts=shutdown.target initrd-switch-root.target
|
||||
Before=shutdown.target initrd-switch-root.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@LIBEXECDIR@/unl0kr-agent
|
Reference in New Issue
Block a user