/* SPDX-FileCopyrightText: 2011 Martin Gräßlin SPDX-FileCopyrightText: 2023 Harald Sitter SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include "greeterapp.h" #include #include #if HAVE_SYS_PRCTL_H #include #endif #if HAVE_SYS_PROCCTL_H #include #include #endif #include #include static void signalHandler(int signum) { qCDebug(KSCREENLOCKER_GREET) << "Greeter received signal" << signum; ScreenLocker::UnlockApp *instance = qobject_cast(QCoreApplication::instance()); if (!instance) { return; } switch (signum) { case SIGTERM: // exit gracefully to not leave behind screensaver processes (bug#224200) // return exit code 1 to indicate that a valid password was not entered, // to prevent circumventing the password input by sending a SIGTERM qCDebug(KSCREENLOCKER_GREET) << "Greeter received SIGTERM. Will exit with error."; instance->exit(1); break; case SIGUSR1: qCDebug(KSCREENLOCKER_GREET) << "Greeter received SIGUSR1. Will lock immediately."; instance->lockImmediately(); break; } } int main(int argc, char *argv[]) { sigset_t blockedSignals; sigemptyset(&blockedSignals); sigaddset(&blockedSignals, SIGTERM); sigaddset(&blockedSignals, SIGUSR1); pthread_sigmask(SIG_BLOCK, &blockedSignals, NULL); LayerShellQt::Shell::useLayerShell(); // disable ptrace on the greeter #if HAVE_PR_SET_DUMPABLE prctl(PR_SET_DUMPABLE, 0); #endif #if HAVE_PROC_TRACE_CTL int mode = PROC_TRACE_CTL_DISABLE; procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode); #endif qCDebug(KSCREENLOCKER_GREET) << "Greeter is starting up."; KLocalizedString::setApplicationDomain(QByteArrayLiteral("kscreenlocker_greet")); // explicitly disable input methods on x11 as it makes it impossible to unlock, see BUG 306932 // but explicitly set on screen keyboard such as maliit is allowed // on wayland, let the compositor take care of the input method if (!qEnvironmentVariableIsSet("WAYLAND_DISPLAY") && !qEnvironmentVariableIsSet("WAYLAND_SOCKET") && qgetenv("QT_IM_MODULE") != QByteArrayLiteral("maliit")) { qputenv("QT_IM_MODULE", QByteArrayLiteral("qtvirtualkeyboard")); } // Suppresses modal warnings about unwritable configuration files which may render the system inaccessible qputenv("KDE_HOME_READONLY", "1"); // Kwin will re-lock if it restarts, reconnecting would leave us with two greeters but only one functional qunsetenv("QT_WAYLAND_RECONNECT"); // Disable QML caching to prevent cache corruption in full or near-full disk scenarios. // https://bugs.kde.org/show_bug.cgi?id=471952 // https://bugreports.qt.io/browse/QTBUG-117130 qputenv("QML_DISABLE_DISK_CACHE", "1"); auto format = QSurfaceFormat::defaultFormat(); format.setOption(QSurfaceFormat::ResetNotification); QSurfaceFormat::setDefaultFormat(format); ScreenLocker::UnlockApp app(argc, argv); KSignalHandler::self()->watchSignal(SIGTERM); KSignalHandler::self()->watchSignal(SIGUSR1); // only connect signal handler once we can actual handle the signal properly QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, &signalHandler); pthread_sigmask(SIG_UNBLOCK, &blockedSignals, NULL); app.setQuitOnLastWindowClosed(false); app.setQuitLockEnabled(false); QCoreApplication::setApplicationName(QStringLiteral("kscreenlocker_greet")); QCoreApplication::setApplicationVersion(QStringLiteral("0.1")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); // disable session management for the greeter auto disableSessionManagement = [](QSessionManager &sm) { sm.setRestartHint(QSessionManager::RestartNever); }; QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); QCommandLineParser parser; parser.setApplicationDescription(i18n("Greeter for the KDE Plasma Workspaces Screen locker")); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption testingOption(QStringLiteral("testing"), i18n("Starts the greeter in testing mode")); QCommandLineOption shellOption(QStringLiteral("shell"), i18n("Starts the greeter with the selected shell theme (only in Testing mode)"), QStringLiteral("shell"), QStringLiteral("")); QCommandLineOption immediateLockOption(QStringLiteral("immediateLock"), i18n("Lock immediately, ignoring any grace time etc.")); QCommandLineOption graceTimeOption(QStringLiteral("graceTime"), i18n("Delay till the lock user interface gets shown in milliseconds."), QStringLiteral("milliseconds"), QStringLiteral("0")); QCommandLineOption nolockOption(QStringLiteral("nolock"), i18n("Don't show any lock user interface.")); QCommandLineOption switchUserOption(QStringLiteral("switchuser"), i18n("Default to the switch user UI.")); QCommandLineOption waylandFdOption(QStringLiteral("ksldfd"), i18n("File descriptor for connecting to ksld."), QStringLiteral("fd")); parser.addOption(testingOption); parser.addOption(shellOption); parser.addOption(immediateLockOption); parser.addOption(graceTimeOption); parser.addOption(nolockOption); parser.addOption(switchUserOption); parser.addOption(waylandFdOption); parser.process(app); if (parser.isSet(testingOption)) { qCDebug(KSCREENLOCKER_GREET) << "Greeter is running in testing mode"; app.setTesting(true); app.setImmediateLock(true); // parse shell option const QString shell = parser.value(shellOption); if (!shell.isEmpty()) { app.setShell(shell); } // allow ptrace if testing is enabled #if HAVE_PR_SET_DUMPABLE prctl(PR_SET_DUMPABLE, 1); #endif #if HAVE_PROC_TRACE_CTL int mode = PROC_TRACE_CTL_ENABLE; procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode); #endif } else { app.setImmediateLock(parser.isSet(immediateLockOption)); } app.setNoLock(parser.isSet(nolockOption)); bool ok = false; int graceTime = parser.value(graceTimeOption).toInt(&ok); if (ok) { app.setGraceTime(graceTime); } if (parser.isSet(switchUserOption)) { app.setDefaultToSwitchUser(true); } if (parser.isSet(waylandFdOption)) { ok = false; const int fd = parser.value(waylandFdOption).toInt(&ok); if (ok) { app.setKsldSocket(fd); } } app.initialViewSetup(); // This allow ksmserver to know when the application has actually finished setting itself up. // Crucial for blocking until it is ready, ensuring locking happens before sleep, e.g. std::cout << "Locked at " << QDateTime::currentDateTime().toSecsSinceEpoch() << std::endl; return app.exec(); }