feat: add plasma-face-unlock
This commit is contained in:
commit
f671acc93b
105 files changed
+13862
No files matched your search
@@ -0,0 +1,111 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// plasma-face-unlockd: the part that runs as root.
|
||||
//
|
||||
// It owns the camera and the face data, and it is the only thing that does.
|
||||
// Everything else (the lock screen agent, sudo, the setup window, the menu)
|
||||
// asks it over one socket. systemd starts it on the first connection and it
|
||||
// exits again after a minute with nothing to do, so most of the time it is not
|
||||
// running at all.
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include "buildconfig.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <csignal>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
// sd_listen_fds(), without linking libsystemd for one function.
|
||||
int systemdSocket()
|
||||
{
|
||||
const QByteArray pid = qgetenv("LISTEN_PID");
|
||||
const QByteArray fds = qgetenv("LISTEN_FDS");
|
||||
if (pid.isEmpty() || fds.isEmpty() || pid.toLongLong() != getpid() || fds.toInt() < 1) {
|
||||
return -1;
|
||||
}
|
||||
qunsetenv("LISTEN_PID");
|
||||
qunsetenv("LISTEN_FDS");
|
||||
qunsetenv("LISTEN_FDNAMES");
|
||||
return 3;
|
||||
}
|
||||
|
||||
// SIGTERM from systemd is the normal way this ends. The camera thread is
|
||||
// cancelled and joined on the way out rather than being cut off mid-frame.
|
||||
void quitOnSignals(QCoreApplication &app)
|
||||
{
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigprocmask(SIG_BLOCK, &mask, nullptr);
|
||||
const int fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
auto *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, &app);
|
||||
QObject::connect(notifier, &QSocketNotifier::activated, &app, [fd] {
|
||||
signalfd_siginfo info;
|
||||
while (read(fd, &info, sizeof(info)) > 0) {
|
||||
}
|
||||
QCoreApplication::quit();
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Face data and state are for root's eyes only, whatever creates them.
|
||||
umask(077);
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName(QStringLiteral("plasma-face-unlockd"));
|
||||
app.setApplicationVersion(QStringLiteral(PFU_VERSION));
|
||||
qSetMessagePattern(QStringLiteral("%{if-warning}warning: %{endif}%{if-critical}error: %{endif}%{message}"));
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QStringLiteral("Face unlock daemon for KDE Plasma"));
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
const QCommandLineOption socketOpt(QStringLiteral("socket"), QStringLiteral("Listen here instead of the system socket."), QStringLiteral("path"),
|
||||
QStringLiteral(PFU_SOCKET));
|
||||
const QCommandLineOption stateOpt(QStringLiteral("state-dir"), QStringLiteral("Where face data is kept."), QStringLiteral("dir"),
|
||||
QStringLiteral(PFU_STATEDIR));
|
||||
const QCommandLineOption configOpt(QStringLiteral("config"), QStringLiteral("The settings file."), QStringLiteral("file"), QStringLiteral(PFU_CONFIG));
|
||||
const QCommandLineOption modelOpt(QStringLiteral("models"), QStringLiteral("Where the networks are."), QStringLiteral("dir"),
|
||||
QStringLiteral(PFU_MODELDIR));
|
||||
const QCommandLineOption idleOpt(QStringLiteral("idle-exit"), QStringLiteral("Exit after this many idle seconds (0: never)."), QStringLiteral("seconds"));
|
||||
parser.addOptions({socketOpt, stateOpt, configOpt, modelOpt, idleOpt});
|
||||
parser.process(app);
|
||||
|
||||
ServerOptions options;
|
||||
options.socketPath = parser.value(socketOpt);
|
||||
options.stateDir = parser.value(stateOpt);
|
||||
options.configPath = parser.value(configOpt);
|
||||
options.modelDir = parser.value(modelOpt);
|
||||
options.systemdFd = systemdSocket();
|
||||
// Started by the socket: go away again when there is nothing to do.
|
||||
// Started by hand (development): stay.
|
||||
options.idleSeconds = parser.isSet(idleOpt) ? parser.value(idleOpt).toInt() : (options.systemdFd >= 0 ? 60 : 0);
|
||||
options.askPolkit = geteuid() == 0;
|
||||
if (!options.askPolkit) {
|
||||
qInfo("not running as root: face changes are not checked with polkit (development only)");
|
||||
}
|
||||
|
||||
quitOnSignals(app);
|
||||
|
||||
Server server(options);
|
||||
QString error;
|
||||
if (!server.start(&error)) {
|
||||
qCritical("cannot listen: %s", qPrintable(error));
|
||||
return 1;
|
||||
}
|
||||
return app.exec();
|
||||
}
|
||||
Reference in new issue
Block a user