Runsisi's Blog
不念过去 不畏将来
tcmu-runner 与 dbus

tcmu-runner

$ dpkg -L tcmu-runner
/etc/dbus-1/system.d/tcmu-runner.conf
/lib/systemd/system/tcmu-runner.service
/usr/share/dbus-1/system-services/org.kernel.TCMUService1.service
# tree /etc/dbus-1/
/etc/dbus-1/
├── session.conf
├── session.d
├── system.conf
└── system.d
    ├── com.redhat.tuned.conf
    ├── dnsmasq.conf
    ├── nm-dispatcher.conf
    ├── nm-ifcfg-rh.conf
    ├── org.fedoraproject.Setroubleshootd.conf
    ├── org.fedoraproject.SetroubleshootFixit.conf
    ├── org.freedesktop.hostname1.conf
    ├── org.freedesktop.import1.conf
    ├── org.freedesktop.locale1.conf
    ├── org.freedesktop.login1.conf
    ├── org.freedesktop.machine1.conf
    ├── org.freedesktop.NetworkManager.conf
    ├── org.freedesktop.PolicyKit1.conf
    ├── org.freedesktop.systemd1.conf
    ├── org.freedesktop.timedate1.conf
    ├── org.ganesha.nfsd.conf
    ├── org.gnome.GConf.Defaults.conf
    ├── org.selinux.conf
    ├── tcmu-runner.conf
    └── wpa_supplicant.conf
# busctl list
NAME                              PID PROCESS         USER             CONNECTION    UNIT                      SESSION    DESCRIPTION
:1.0                              848 systemd-logind  root             :1.0          systemd-logind.service    -          -
:1.2                              850 avahi-daemon    avahi            :1.2          avahi-daemon.service      -          -
:1.2108705                    1240137 polkitd         polkitd          :1.2108705    polkit.service            -          -
:1.2108706                    1240164 tuned           root             :1.2108706    tuned.service             -          -
:1.2115569                    2229634 busctl          root             :1.2115569    session-22308.scope       22308      -
:1.292083                           1 systemd         root             :1.292083     -                         -          -
com.redhat.tuned              1240164 tuned           root             :1.2108706    tuned.service             -          -
org.freedesktop.Avahi             850 avahi-daemon    avahi            :1.2          avahi-daemon.service      -          -
org.freedesktop.DBus                - -               -                -             -                         -          -
org.freedesktop.PolicyKit1    1240137 polkitd         polkitd          :1.2108705    polkit.service            -          -
org.freedesktop.hostname1           - -               -                (activatable) -                         -
org.freedesktop.import1             - -               -                (activatable) -                         -
org.freedesktop.locale1             - -               -                (activatable) -                         -
org.freedesktop.login1            848 systemd-logind  root             :1.0          systemd-logind.service    -          -
org.freedesktop.machine1            - -               -                (activatable) -                         -
org.freedesktop.systemd1            1 systemd         root             :1.292083     -                         -          -
org.freedesktop.timedate1           - -               -                (activatable) -                         -
org.kernel.TCMUService1             - -               -                (activatable) -                         -
$ cat /etc/dbus-1/system.d/tcmu-runner.conf
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
  <allow own="org.kernel.TCMUService1"/>
</policy>
<policy context="default">
  <allow send_destination="org.kernel.TCMUService1"/>
  <allow own_prefix="org.kernel.TCMUService1.HandlerManager1"/>
  <allow send_interface="org.kernel.TCMUService1"/>
</policy>
</busconfig>

修改 dbus 配置文件之后需要重新加载 dbus 配置:

$ sudo systemctl reload dbus.service

但是在重启服务之前需要确保 dbus 的配置文件 dbus 用户用户拥有读权限:

$ ps -ef | grep dbus-daemon
dbus     2234654       1  0 17:17 ?        00:00:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
$ sudo chmod o+r /etc/dbus-1/system.d/tcmu-runner.conf

否则 reload 配置时会出现如下的错误:

Dec  4 17:21:00 host-192-168-10-234 dbus[2234654]: Encountered error 'Failed to open "/etc/dbus-1/system.d/tcmu-runner.conf": Permission denied' while parsing '/etc/dbus-1/system.d/tcmu-runner.conf'
# For services that acquire a name on the DBus system bus, use Type=dbus and set BusName= accordingly. The service should not fork (daemonize). systemd will consider the service to be initialized once the name has been acquired on the system bus.

$ cat /lib/systemd/system/tcmu-runner.service
[Unit]
Description=LIO Userspace-passthrough daemon
Documentation=man:tcmu-runner(8)
Wants=rsyslog.service
After=network.target rsyslog.service

[Service]
LimitNOFILE=1000000
Type=dbus
BusName=org.kernel.TCMUService1
KillMode=process
ExecStart=/usr/bin/tcmu-runner

[Install]
WantedBy=multi-user.target
$ cat /usr/share/dbus-1/system-services/org.kernel.TCMUService1.service
# When placed in /usr/share/dbus-1/system-services/, this should cause tcmu-runner
# to be loaded and run by dbus-daemon when a client accesses the service name.

[D-BUS Service]
Name=org.kernel.TCMUService1
Exec=/usr/bin/tcmu-runner
User=root
SystemdService=tcmu-runner.service

这里指定的 User 在配置使用 dbus-daemon-launch-helper 激活 SystemdService systemd 服务时无关紧要:

$ cat /usr/share/dbus-1/system.conf
<!-- This is a setuid helper that is used to launch system services -->
<servicehelper>/usr/lib/dbus-1.0/dbus-daemon-launch-helper</servicehelper>

调用 tcmu-runner dbus 服务

$ d-feet

$ sudo systemctl stop tcmu-runner
$ systemctl status tcmu-runner
● tcmu-runner.service - LIO Userspace-passthrough daemon
     Loaded: loaded (/lib/systemd/system/tcmu-runner.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Sun 2020-11-22 15:03:49 CST; 1s ago
       Docs: man:tcmu-runner(8)
    Process: 1620445 ExecStart=/usr/bin/tcmu-runner (code=exited, status=0/SUCCESS)
   Main PID: 1620445 (code=exited, status=0/SUCCESS)
$ busctl call org.kernel.TCMUService1 /org/kernel/TCMUService1 org.freedesktop.DBus.Introspectable Introspect
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"
                      \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">
<!-- GDBus 2.64.3 -->
<node>
  <interface name=\"org.freedesktop.DBus.Properties\">
    <method name=\"Get\">
      <arg type=\"s\" name=\"interface_name\" direction=\"in\"/>
      <arg type=\"s\" name=\"property_name\" direction=\"in\"/>
      <arg type=\"v\" name=\"value\" direction=\"out\"/>
    </method>
...
$ systemctl status tcmu-runner
● tcmu-runner.service - LIO Userspace-passthrough daemon
     Loaded: loaded (/lib/systemd/system/tcmu-runner.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2020-11-22 15:03:54 CST; 1s ago
       Docs: man:tcmu-runner(8)
   Main PID: 1620574 (tcmu-runner)
      Tasks: 6 (limit: 9355)
     Memory: 7.5M
     CGroup: /system.slice/tcmu-runner.service
             └─1620574 /usr/bin/tcmu-runner

dbus-daemon 配置

// $ man dbus-daemon
--systemd-activation
           Enable systemd-style service activation. Only useful in conjunction with the systemd system and session manager on Linux.
$ ps -ef | grep dbus-daemon
message+     751       1  0 11:09 ?        00:00:01 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
runsisi     2047    2035  0 11:09 ?        00:00:01 /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
runsisi     2435       1  0 11:09 ?        00:00:01 /usr/bin/dbus-daemon --syslog --fork --print-pid 5 --print-address 7 --config-file /usr/share/fcitx/dbus/daemon.conf
runsisi     2455    2449  0 11:09 ?        00:00:00 /usr/bin/dbus-daemon --config-file=/usr/share/defaults/at-spi2/accessibility.conf --nofork --print-address 3

代码实现

tcmu-runner

$ git log -1
commit c6fda29bddf49405625c6ebd3baa6cfdccc7160a (HEAD -> master, upstream/master)
Merge: 9d7d245 81bf087
Author: Xiubo Li <xiubli@redhat.com>
Date:   Tue Nov 17 09:52:41 2020

    Merge pull request #643 from ddiss/inq_cleanup

    scsi: minor simplification of inquiry if/else logic
<!-- tcmu-handler.xml -->

<node>
  <interface name="org.kernel.TCMUService1">
    <property name="ConfigDesc" type="s" access="read"/>
    <!--
	CheckConfig:

The creation of a TCMU device is decoupled from the discovery and
use by its backing handler, and there's no good way for errors in
the handler-specific configstring to be visible to the user when
creating the device.
This method lets tools that are creating TCMU devices check
that the configstring is valid before the device has been created.
    -->
    <method name="CheckConfig">
      <arg type="s" name="configstr" direction="in"/>
      <arg type="b" name="is_valid" direction="out"/>
      <arg type="s" name="message" direction="out"/>
    </method>
  </interface>
  <interface name="org.kernel.TCMUService1.HandlerManager1">
    <method name="RegisterHandler">
      <arg type="s" name="subtype" direction="in"/>
      <arg type="s" name="cfgstr_desc" direction="in"/>
      <arg type="b" name="succeeded" direction="out"/>
      <arg type="s" name="message" direction="out"/>
    </method>
  </interface>
</node>
# CMakeLists.txt
add_library(tcmu
  SHARED
  strlcpy.c
  configfs.c
  api.c
  libtcmu.c
  libtcmu-register.c
  tcmuhandler-generated.c
  libtcmu_log.c
  libtcmu_config.c
  libtcmu_time.c
  )

add_custom_command(
  OUTPUT ${CMAKE_SOURCE_DIR}/tcmuhandler-generated.c ${CMAKE_SOURCE_DIR}/tcmuhandler-generated.h
  COMMAND gdbus-codegen ${CMAKE_SOURCE_DIR}/tcmu-handler.xml --generate-c-code ${CMAKE_SOURCE_DIR}/tcmuhandler-generated --c-generate-object-manager --interface-prefix org.kernel
  MAIN_DEPENDENCY tcmu-handler.xml
  )
// main.c

main
  /* Set up DBus name, see callback */
  g_bus_own_name(G_BUS_TYPE_SYSTEM,
  			"org.kernel.TCMUService1",
  			G_BUS_NAME_OWNER_FLAGS_NONE,
  			dbus_bus_acquired,
  			dbus_name_acquired, // name acquired
  			dbus_name_lost, // name lost
  			NULL, // user data
  			NULL  // user date free func
  	);

dbus

$ git log -1
commit 75a9cd0827229f0cd3b61d79a7dd0d8c2e7c070b (HEAD -> master, origin/master, origin/HEAD)
Merge: b187a5f7 95ad63d7
Author: Simon McVittie <smcv@collabora.com>
Date:   Mon Nov 9 15:00:39 2020 +0000

    Merge branch 'bug/NEWS-machine-id-paths' into 'master'

    NEWS: Add missing directory 'lib' to three paths

    See merge request dbus/dbus!182
// bus/main.c

main
  if (strcmp (arg, "--systemd-activation") == 0) {
    flags |= BUS_CONTEXT_FLAG_SYSTEMD_ACTIVATION;
  }

// bus/bus.c

process_config_first_time_only
  if (flags & BUS_CONTEXT_FLAG_SYSTEMD_ACTIVATION)
    context->systemd_activation = TRUE;

process_config_every_time
  servicehelper = bus_config_parser_get_servicehelper (parser);
  context->servicehelper = s;

dbus_bool_t
bus_context_get_systemd_activation (BusContext *context)
{
  return context->systemd_activation;
}

const char*
bus_context_get_servicehelper (BusContext *context)
{
  return context->servicehelper;
}

// bus/activation.c

bus_activation_activate_service
  if (bus_context_get_systemd_activation (activation->context)) {
    if (strcmp (service_name, "org.freedesktop.systemd1") == 0)
          /* systemd itself is missing apparently. That can happen
             only during early startup. Let's just wait until systemd
             connects to us and do nothing. */
      return TRUE;

    if (entry->systemd_service) {
      message = dbus_message_new_signal (DBUS_PATH_DBUS,
                                         "org.freedesktop.systemd1.Activator",
                                         "ActivationRequest");

      /* Check whether systemd is already connected */
      registry = bus_connection_get_registry (connection);
      _dbus_string_init_const (&service_string, "org.freedesktop.systemd1");
      service = bus_registry_lookup (registry, &service_string);

      if (service != NULL) {
        /* Wonderful, systemd is connected, let's just send the msg */
        retval = bus_dispatch_matches (activation_transaction, NULL,
                                       systemd, message, error);
      } else {
        /* systemd is not around, let's "activate" it. */
        retval = bus_activation_activate_service (activation, NULL, activation_transaction, TRUE,
                                                  message, "org.freedesktop.systemd1", error);
      }
      return TRUE;
    }
    /* OK, we have no configured systemd service, hence let's
       proceed with traditional activation. */
  }

  /* does the bus use a helper? */
  servicehelper = bus_context_get_servicehelper (activation->context);
  _dbus_spawn_async_with_babysitter

参考资料

D-Bus

https://en.wikipedia.org/wiki/D-Bus

dbus-daemon — Message bus daemon

https://dbus.freedesktop.org/doc/dbus-daemon.1.html

The new sd-bus API of systemd

http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html

Avoid Desktop Bus (D-Bus) bus activation

https://jdebp.eu/Softwares/nosh/avoid-dbus-bus-activation.html

systemd.service — Service unit configuration

https://www.freedesktop.org/software/systemd/man/systemd.service.html


最后修改于 2020-12-20