• 首页
  • 加入
  • RSS
  • Starting sessions with systemd part 2

    Wednesday, December 29, 2021
    deepin 社区官方博客 #deepin-community:deepin.org


    接着上一篇文章,这篇文章主要讲解一下具体的实现。

    https://github.com/linuxdeepin/dde-session 已经包含了所有的文件和提交。

    上面文章说过,为了让 dde 使用 systemd –user 来关系服务,我提供了一组服务:

    dde-session-initialized.target
    dde-session-manager.service
    dde-session-manager.target
    dde-session-pre.target
    dde-session-restart-dbus.service
    dde-session-shutdown.service
    dde-session-shutdown.target
    dde-session.target
    dde-session-x11-services-ready.target
    dde-session-x11-services.target
    dde-session-x11.target
    org.deepin.Session.service
    

    lightdm 在认证通过以后,会启动 dde-session 进程,dde-session 会通过 systemd 的 dbus 启动 org.deepin.Session.service

    org.deepin.Session.service 中会执行 dde-session-ctl --systemd-service,启动 dde-session-x11.target.

    dde-session-x11.target 关联了所有初始化的服务,systemd 会帮助自动运行这些服务,并且最终会运行到 dde-session-manager.service 上。

    dde-session-manager.service

    [Service]
    Type=simple
    ExecStart=/usr/bin/startdde
    ExecStopPost=/usr/lib/libexec/dde-session-ctl --logout
    

    org.deepin.Session.service 中会启动一个特殊的服务,这个服务会监听 dde-session 的退出,因为总要有一个服务去监听 session 的状态,要确保 session 在服务在,session 退服务退,服务退 session 退,共存亡,这也是所有操作中最麻烦的一步。

    org.deepin.Session.service

    [Service]
    Type=dbus
    BusName=org.deepin.Session
    ExecStart=/usr/bin/dde-session --systemd-service
    ExecStop=/usr/lib/libexec/dde-session-ctl --shutdown
    

    在 dde-session-ctl 中是这么实现 shutdown 的:

    if (isShutdown) {
        // kill startdde-session or call login1
        QDBusInterface systemd("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager");
        qInfo() << systemd.call("StartUnit", "dde-session-shutdown.target", "replace-irreversibly");
        return 0;
    }
    

    在 dde-session-ctl 中是这么实现 logout 的:

    if (parser.isSet(logout)) {
        org::deepin::Session session("org.deepin.Session", "/org/deepin/Session", QDBusConnection::sessionBus());
        session.Logout();
    
        return 0;
    }
    

    dde-session main.cpp

    // 添加启动参数,表示这是通过 systemd 启动的。
    QCommandLineOption systemd(QStringList{"d", "systemd-service", "wait for systemd services"});
    parser.addOption(systemd);
    parser.process(app);
    
    if (parser.isSet(systemd)) {
        return -1;
    }
    
    QDBusServiceWatcher *watcher = new QDBusServiceWatcher("org.deepin.Session", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration);
        watcher->connect(watcher, &QDBusServiceWatcher::serviceUnregistered, [=] {
            QDBusInterface systemdDBus("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager");
            qInfo() << systemdDBus.call("StartUnit", "dde-session-shutdown.service", "replace");
            qApp->quit();
        });
        qInfo() << systemdDBus.call("StartUnit", "dde-session-x11.target", "replace");
    }
    

    dde-session-shutdown.service 只负责执行 dde-session-ctl --shutdown,用于启动 dde-session-shutdown.target

    dde-session-shutdown.service

    [Service]
    Type=oneshot
    ExecStart=/usr/lib/libexec/dde-session-ctl --logout
    

    通过 systemd 的接口启动了 dde-session-shutdown.target,从而启动了清理流程。

    dde-session-shutdown.target 没有执行任何内容,仅仅是关联了一组服务,但是是冲突这些服务,因为这是启动的时候

    dde-session-shutdown.target 中还关联了最重要的一个服务: dde-session-restart-dbus.service,这个服务是用来关闭 dbus.service 服务的,这样就可以保证 session 结束的时候,不会有 dbus 服务逃逸出去。

    dde-session-restart-dbus.service

    [Service]
    Type=notify
    ExecStart=/usr/lib/libexec/dde-session-ctl --restart-dbus
    

    在 dde-session-ctl 中是这么实现 restart-dbus 的:

    if (isRestartDBus) {
        QDBusInterface systemd("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager");
        qInfo() << systemd.call("StopUnit", "dbus.service", "replace-irreversibly");
        return 0;
    }
    

    复盘一下整个流程,在登录界面输入密码登录以后,lightdm 会启动 dde-session 作为 session 的入口,dde-session 会注册一个 org.deepin.Session 的 dbus 服务,并且会启动关联的 systemd 服务,启动一个 dde-session --systemd-service,在这个进程里,会启动 dde-session-x11.target,从而最终启动了 dde-session-manager.service,将原本的会话入口 startdde 启动了。

    dde-session --systemd-service 会监听 org.deepin.Session 服务的存活,当服务不存在时,就会开始执行退出流程,从而启动了 dde-session-ctl --shutdown,在 dde-session-ctl 的帮助下,启动了 dde-session-shutdown.target,将所有启动的 dde 核心服务都冲突掉,从而完成了服务关闭,在最后阶段再将 dbus.service 服务停止,完成最终的清理。

    还有一种退出模式,手动启动了 dde-session-shutdown.service 服务,也会触发完成的退出流程,dde-session-shutdown.service 会利用 dde-session-ctl --logout 将会话注销,从而触发上面的执行流程,完成会话与服务的关闭。

    目前还有一些问题没有解决,没有创建独立的 systemd.slice 和 systemd.scope,这可以帮助 dde 细致的划分和管理自己的子 services,退出 dbus.service 的手段非常黑,而且 dbus 服务的程序由于图形服务也已经结束了,导致成片的 X11 Error。这些都是要解决的。

    下一步准备写几个 service,用来代替 startdde 的组件启动,比如 kwin、dde-desktop、dde-dock 和 xdg-autostart 等。

    就目前的情况来看,任重而道远。

    原文链接:https://blog.justforlxz.com/2021/12/29/Starting-sessions-with-systemd-part-2/