/*************************************************************************** * Copyright (c) 2013 Abdurrahman AVCI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ***************************************************************************/ #include "Greeter.h" #include "Configuration.h" #include "Constants.h" #include "DaemonApp.h" #include "DisplayManager.h" #include "Seat.h" #include "ThemeConfig.h" #include "ThemeMetadata.h" #include "Display.h" #include "XorgDisplayServer.h" #include "XorgUserDisplayServer.h" #include "WaylandDisplayServer.h" #include #include #include namespace SDDM { Greeter::Greeter(Display *parent) : QObject(parent) , m_display(parent) { m_metadata = new ThemeMetadata(QString()); m_themeConfig = new ThemeConfig(QString()); } Greeter::~Greeter() { stop(); delete m_metadata; delete m_themeConfig; } void Greeter::setSocket(const QString &socket) { m_socket = socket; } void Greeter::setTheme(const QString &theme) { m_themePath = theme; if (theme.isEmpty()) { m_metadata->setTo(QString()); m_themeConfig->setTo(QString()); } else { const QString path = QStringLiteral("%1/metadata.desktop").arg(m_themePath); m_metadata->setTo(path); QString configFile = QStringLiteral("%1/%2").arg(m_themePath).arg(m_metadata->configFile()); m_themeConfig->setTo(configFile); } } QString Greeter::displayServerCommand() const { return m_displayServerCmd; } void Greeter::setDisplayServerCommand(const QString &cmd) { m_displayServerCmd = cmd; } QString Greeter::greeterPathForQt(int qtVersion) { const QString suffix = qtVersion == 5 ? QString() : QStringLiteral("-qt%1").arg(qtVersion); return QStringLiteral(BIN_INSTALL_DIR "/sddm-greeter%1").arg(suffix); } bool Greeter::start() { // check flag if (m_started) return false; // If no theme is given, use the default theme of the default greeter version const int themeQtVersion = m_themePath.isEmpty() ? (QT_VERSION >> 16) : m_metadata->qtVersion(); QString greeterPath = greeterPathForQt(themeQtVersion); if (!QFileInfo(greeterPath).isExecutable()) { qWarning() << "The theme at" << m_themePath << "requires missing" << greeterPath << ". Using fallback theme."; setTheme(QString()); greeterPath = greeterPathForQt(QT_VERSION >> 16); } // themes QString xcursorTheme = mainConfig.Theme.CursorTheme.get(); if (m_themeConfig->contains(QLatin1String("cursorTheme"))) xcursorTheme = m_themeConfig->value(QLatin1String("cursorTheme")).toString(); QString xcursorSize = mainConfig.Theme.CursorSize.get(); if (m_themeConfig->contains(QLatin1String("cursorSize"))) xcursorSize = m_themeConfig->value(QLatin1String("cursorSize")).toString(); QString platformTheme; if (m_themeConfig->contains(QLatin1String("platformTheme"))) platformTheme = m_themeConfig->value(QLatin1String("platformTheme")).toString(); QString style; if (m_themeConfig->contains(QLatin1String("style"))) style = m_themeConfig->value(QLatin1String("style")).toString(); // greeter command QStringList args; args << QLatin1String("--socket") << m_socket; if (!m_themePath.isEmpty()) args << QLatin1String("--theme") << m_themePath; if (!platformTheme.isEmpty()) args << QLatin1String("-platformtheme") << platformTheme; if (!style.isEmpty()) args << QLatin1String("-style") << style; Q_ASSERT(m_display); auto *displayServer = m_display->displayServer(); if (daemonApp->testing()) { // create process m_process = new QProcess(this); // delete process on finish connect(m_process, QOverload::of(&QProcess::finished), this, &Greeter::finished); connect(m_process, &QProcess::readyReadStandardOutput, this, &Greeter::onReadyReadStandardOutput); connect(m_process, &QProcess::readyReadStandardError, this, &Greeter::onReadyReadStandardError); // log message qDebug() << "Greeter starting..."; args << QStringLiteral("--test-mode"); if (m_display->displayServerType() == Display::X11DisplayServerType) { // set process environment QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert(QStringLiteral("DISPLAY"), m_display->name()); env.insert(QStringLiteral("XAUTHORITY"), qobject_cast(displayServer)->authPath()); env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); if (!xcursorSize.isEmpty()) env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); m_process->setProcessEnvironment(env); } // Greeter command m_process->start(greeterPath, args); //if we fail to start bail immediately, and don't block in waitForStarted if (m_process->state() == QProcess::NotRunning) { qCritical() << "Greeter failed to launch."; return false; } // wait for greeter to start if (!m_process->waitForStarted()) { // log message qCritical() << "Failed to start greeter."; // return fail return false; } // log message qDebug() << "Greeter started."; // set flag m_started = true; } else { // authentication m_auth = new Auth(this); m_auth->setVerbose(true); connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged); connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted); connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady); connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished); connect(m_auth, &Auth::info, this, &Greeter::authInfo); connect(m_auth, &Auth::error, this, &Greeter::authError); // command QStringList cmd; cmd << greeterPath << args; // greeter environment QProcessEnvironment env; QProcessEnvironment sysenv = QProcessEnvironment::systemEnvironment(); insertEnvironmentList({QStringLiteral("LANG"), QStringLiteral("LANGUAGE"), QStringLiteral("LC_CTYPE"), QStringLiteral("LC_NUMERIC"), QStringLiteral("LC_TIME"), QStringLiteral("LC_COLLATE"), QStringLiteral("LC_MONETARY"), QStringLiteral("LC_MESSAGES"), QStringLiteral("LC_PAPER"), QStringLiteral("LC_NAME"), QStringLiteral("LC_ADDRESS"), QStringLiteral("LC_TELEPHONE"), QStringLiteral("LC_MEASUREMENT"), QStringLiteral("LC_IDENTIFICATION"), QStringLiteral("LD_LIBRARY_PATH"), QStringLiteral("QML2_IMPORT_PATH"), QStringLiteral("QT_PLUGIN_PATH"), QStringLiteral("XDG_DATA_DIRS") }, sysenv, env); env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); if (!xcursorSize.isEmpty()) env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); env.insert(QStringLiteral("XDG_SEAT"), m_display->seat()->name()); env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(m_display->seat()->name())); env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId()))); if (m_display->seat()->name() == QLatin1String("seat0") && m_display->terminalId() > 0) env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_display->terminalId())); env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("greeter")); env.insert(QStringLiteral("XDG_SESSION_TYPE"), m_display->sessionType()); if (m_display->displayServerType() == Display::X11DisplayServerType) { env.insert(QStringLiteral("DISPLAY"), m_display->name()); env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); m_auth->setCookie(qobject_cast(displayServer)->cookie()); } else if (m_display->displayServerType() == Display::WaylandDisplayServerType) { env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("xdg-shell")); } m_auth->insertEnvironment(env); // log message qDebug() << "Greeter starting..."; // start greeter m_auth->setUser(QStringLiteral("sddm")); m_auth->setDisplayServerCommand(m_displayServerCmd); m_auth->setGreeter(true); m_auth->setSession(cmd.join(QLatin1Char(' '))); m_auth->start(); } // return success return true; } void Greeter::insertEnvironmentList(QStringList names, QProcessEnvironment sourceEnv, QProcessEnvironment &targetEnv) { for (QStringList::const_iterator it = names.constBegin(); it != names.constEnd(); ++it) if (sourceEnv.contains(*it)) targetEnv.insert(*it, sourceEnv.value(*it)); } void Greeter::stop() { // check flag if (!m_started) return; // log message qDebug() << "Greeter stopping..."; if (daemonApp->testing()) { // terminate process m_process->terminate(); // wait for finished if (!m_process->waitForFinished(5000)) m_process->kill(); } else { m_auth->stop(); } } void Greeter::finished() { // check flag if (!m_started) return; // reset flag m_started = false; // log message qDebug() << "Greeter stopped."; // clean up if (m_process) { m_process->deleteLater(); m_process = nullptr; } } void Greeter::onRequestChanged() { m_auth->request()->setFinishAutomatically(true); } void Greeter::onSessionStarted(bool success) { // set flag m_started = success; // log message if (success) qDebug() << "Greeter session started successfully"; else qDebug() << "Greeter session failed to start"; } void Greeter::onDisplayServerReady(const QString &displayName) { auto *displayServer = m_display->displayServer(); auto *xorgUser = qobject_cast(displayServer); if (xorgUser) xorgUser->setDisplayName(displayName); auto *wayland = qobject_cast(displayServer); if (wayland) wayland->setDisplayName(displayName); } void Greeter::onHelperFinished(Auth::HelperExitStatus status) { // reset flag m_started = false; // log message qDebug() << "Greeter stopped." << status; // clean up m_auth->deleteLater(); m_auth = nullptr; if (status == Auth::HELPER_DISPLAYSERVER_ERROR) { Q_EMIT displayServerFailed(); } else if (status == Auth::HELPER_TTY_ERROR) { Q_EMIT ttyFailed(); } else if (status == Auth::HELPER_SESSION_ERROR) { Q_EMIT failed(); } } bool Greeter::isRunning() const { return (m_process && m_process->state() == QProcess::Running) || (m_auth && m_auth->isActive()); } void Greeter::onReadyReadStandardError() { if (m_process) { qDebug() << "Greeter errors:" << m_process->readAllStandardError().constData(); } } void Greeter::onReadyReadStandardOutput() { if (m_process) { qDebug() << "Greeter output:" << m_process->readAllStandardOutput().constData(); } } void Greeter::authInfo(const QString &message, Auth::Info info) { Q_UNUSED(info); qDebug() << "Information from greeter session:" << message; } void Greeter::authError(const QString &message, Auth::Error error) { Q_UNUSED(error); qWarning() << "Error from greeter session:" << message; } }