diff --git a/misc/systemd/dde-session-pre.target.wants/treeland-xwayland.service.in b/misc/systemd/dde-session-pre.target.wants/treeland-xwayland.service.in index 97ffa8725..c84e4e413 100644 --- a/misc/systemd/dde-session-pre.target.wants/treeland-xwayland.service.in +++ b/misc/systemd/dde-session-pre.target.wants/treeland-xwayland.service.in @@ -19,5 +19,6 @@ Sockets=treeland-xwayland.socket UnsetEnvironment=DISPLAY ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR@/treeland-sd --type xwayland ExecStop=-/usr/bin/systemctl --user unset-environment DISPLAY -Slice=session.slice +Restart=on-failure RestartSec=3s +Slice=session.slice diff --git a/misc/systemd/treeland.service.in b/misc/systemd/treeland.service.in index bfeca37a6..8d1fb57b2 100644 --- a/misc/systemd/treeland.service.in +++ b/misc/systemd/treeland.service.in @@ -20,6 +20,10 @@ Environment=ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer Environment=ASAN_OPTIONS=log_path=/tmp/treeland-asan:detect_leaks=0:abort_on_error=1:symbolize=1 Environment=DDM_DISPLAY_MANAGER=1 Environment=TREELAND_INPUT_ENHANCE=1 +# Kill orphaned Xwayland processes from previous treeland instance. +# When treeland crashes, its Xwayland child processes may become orphans +# because the cleanup code in Session::~Session() is not executed. +ExecStartPre=-/usr/bin/pkill -9 -u dde -f '^Xwayland[[:space:]]+:[0-9]+' ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/treeland.sh --lockscreen Restart=on-failure RestartSec=1s diff --git a/src/systemd-socket.cpp b/src/systemd-socket.cpp index 78a1ab6fa..fb178a7c6 100644 --- a/src/systemd-socket.cpp +++ b/src/systemd-socket.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -29,19 +30,122 @@ Q_LOGGING_CATEGORY(lcSdSocket, "treeland.systemd.socket") typedef QMap StringMap; Q_DECLARE_METATYPE(StringMap) -class SignalReceiver : public QObject +class SocketActivator : public QObject { Q_OBJECT public: - SignalReceiver(std::function activateFdFunc, QObject *parent = nullptr) - : QObject(parent), activateFd(activateFdFunc) { + SocketActivator(QDBusUnixFileDescriptor unixFileDescriptor, + QString type, + QDBusConnection connection, + QObject *parent = nullptr) + : QObject(parent) + , m_unixFileDescriptor(unixFileDescriptor) + , m_type(std::move(type)) + , m_connection(connection) { } + + void start() { + m_connection.connect("org.deepin.Compositor1", + "/org/deepin/Compositor1", + "org.deepin.Compositor1", + "SessionChanged", + this, + SLOT(activate())); + activate(); + } + + ~SocketActivator() { + qDeleteAll(m_tmpFiles); + } + public Q_SLOTS: - void onSessionChanged() { - activateFd(); + void activate() { + QDBusInterface updateFd("org.deepin.Compositor1", + "/org/deepin/Compositor1", + "org.deepin.Compositor1", + m_connection); + + if (updateFd.isValid()) { + if (m_type == "wayland") { + updateFd.call("ActivateWayland", QVariant::fromValue(m_unixFileDescriptor)); + + QDBusInterface dbus("org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + QDBusConnection::sessionBus()); + StringMap env; + env["WAYLAND_DISPLAY"] = "treeland.socket"; + + const auto extraEnvs = qgetenv("TREELAND_SESSION_ENVIRONMENTS"); + if (!extraEnvs.isEmpty()) { + const auto envs = extraEnvs.split('\n'); + for (const auto &i : envs) { + const auto pair = i.split('='); + if (pair.size() == 2) { + env[QString::fromLocal8Bit(pair[0])] = QString::fromLocal8Bit(pair[1]); + } + } + } + + dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env)); + + sd_notify(0, "READY=1"); + } else if (m_type == "xwayland") { + QDBusMessage reply = updateFd.call("XWaylandName"); + if (reply.type() == QDBusMessage::ReplyMessage) { + QVariantList values = reply.arguments(); + if (values.size() < 2) { + qCWarning(lcSdSocket) << "Invalid XWaylandName reply"; + return; + } + QString xwaylandName = values.at(0).toString(); + QByteArray auth = values.at(1).toByteArray(); + + QByteArray runtimeDir = qgetenv("XDG_RUNTIME_DIR"); + if (runtimeDir.isEmpty()) + runtimeDir = QDir::tempPath().toLocal8Bit(); + QTemporaryFile *authFile = new QTemporaryFile(); + authFile->setFileTemplate(QStringLiteral("%1/.xauth_XXXXXX").arg(runtimeDir)); + if (!authFile->open()) { + qCWarning(lcSdSocket) << "Failed to create temporary xauth file"; + return; + } + QString authFileName = authFile->fileName(); + m_tmpFiles.append(authFile); + authFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner); + authFile->write(auth); + authFile->flush(); + authFile->close(); + + QDBusInterface dbus("org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + QDBusConnection::sessionBus()); + StringMap env; + env["DISPLAY"] = xwaylandName; + env["XAUTHORITY"] = authFileName; + dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env)); + + sd_notify(0, "READY=1"); + } else if (reply.type() == QDBusMessage::ErrorMessage) { + qCWarning(lcSdSocket) << "XWaylandName failed:" << reply.errorMessage(); + } + } + } + if (m_type == "xwayland" && !updateFd.isValid()) { + if (++m_retryCount < 20) + QTimer::singleShot(500, this, &SocketActivator::activate); + else + qCWarning(lcSdSocket) << "XWayland activation timed out after 10s"; + } } + private: - std::function activateFd; + QDBusUnixFileDescriptor m_unixFileDescriptor; + QString m_type; + QDBusConnection m_connection; + QList m_tmpFiles; + int m_retryCount = 0; }; int main(int argc, char *argv[]) @@ -70,100 +174,14 @@ int main(int argc, char *argv[]) exit(1); } - QList tmpFiles; QDBusUnixFileDescriptor unixFileDescriptor(SD_LISTEN_FDS_START); - auto active = [unixFileDescriptor, type, &tmpFiles](QDBusConnection connection) { - auto activateFd = [unixFileDescriptor, type, &connection, &tmpFiles] { - QDBusInterface updateFd("org.deepin.Compositor1", - "/org/deepin/Compositor1", - "org.deepin.Compositor1", - connection); + auto *s1 = new SocketActivator(unixFileDescriptor, type, QDBusConnection::sessionBus(), &app); + auto *s2 = new SocketActivator(unixFileDescriptor, type, QDBusConnection::systemBus(), &app); + s1->start(); + s2->start(); - if (updateFd.isValid()) { - if (type == "wayland") { - updateFd.call("ActivateWayland", QVariant::fromValue(unixFileDescriptor)); - - QDBusInterface dbus("org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - QDBusConnection::sessionBus()); - StringMap env; - env["WAYLAND_DISPLAY"] = "treeland.socket"; - - const auto extraEnvs = qgetenv("TREELAND_SESSION_ENVIRONMENTS"); - if (!extraEnvs.isEmpty()) { - const auto envs = extraEnvs.split('\n'); - for (const auto &i : envs) { - const auto pair = i.split('='); - if (pair.size() == 2) { - env[QString::fromLocal8Bit(pair[0])] = QString::fromLocal8Bit(pair[1]); - } - } - } - - auto reply = dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env)); - - sd_notify(0, "READY=1"); - } else if (type == "xwayland") { - QDBusMessage reply = updateFd.call("XWaylandName"); - if (reply.type() == QDBusMessage::ReplyMessage) { - QVariantList values = reply.arguments(); - if (values.size() < 2) { - qCWarning(lcSdSocket) << "Invalid XWaylandName reply"; - return; - } - QString xwaylandName = values.at(0).toString(); - QByteArray auth = values.at(1).toByteArray(); - - QByteArray runtimeDir = qgetenv("XDG_RUNTIME_DIR"); - if (runtimeDir.isEmpty()) - runtimeDir = QDir::tempPath().toLocal8Bit(); - QTemporaryFile *authFile = new QTemporaryFile(); - authFile->setFileTemplate(QStringLiteral("%1/.xauth_XXXXXX").arg(runtimeDir)); - if (!authFile->open()) { - qCWarning(lcSdSocket) << "Failed to create temporary xauth file"; - return; - } - QString authFileName = authFile->fileName(); - tmpFiles.append(authFile); - authFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner); - authFile->write(auth); - authFile->flush(); - authFile->close(); - - QDBusInterface dbus("org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - QDBusConnection::sessionBus()); - StringMap env; - env["DISPLAY"] = xwaylandName; - env["XAUTHORITY"] = authFileName; - auto reply = - dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env)); - - sd_notify(0, "READY=1"); - } - } - } - }; - - connection.connect("org.deepin.Compositor1", - "/org/deepin/Compositor1", - "org.deepin.Compositor1", - "SessionChanged", - new SignalReceiver(activateFd), - SLOT(onSessionChanged())); - activateFd(); - }; - - active(QDBusConnection::sessionBus()); - active(QDBusConnection::systemBus()); - - int ret = app.exec(); - for (auto i : std::as_const(tmpFiles)) - delete i; - return ret; + return app.exec(); } #include "systemd-socket.moc"