tcmu-runner

1
2
3
4
$ 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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) - -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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 配置:

1
$ sudo systemctl reload dbus.service

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

1
2
3
$ 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 配置时会出现如下的错误:

1
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'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 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
1
2
3
4
5
6
7
8
9
$ 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 服务时无关紧要:

1
2
3
$ 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 服务

1
$ d-feet

1
2
3
4
5
6
7
8
$ 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)
1
2
3
4
5
6
7
8
9
10
11
12
$ 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>
...
1
2
3
4
5
6
7
8
9
10
$ 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 配置

1
2
3
// $ man dbus-daemon
--systemd-activation
Enable systemd-style service activation. Only useful in conjunction with the systemd system and session manager on Linux.
1
2
3
4
5
$ 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

1
2
3
4
5
6
7
8
9
$ 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 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
)
1
2
3
4
5
6
7
8
9
10
11
12
13
// 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

1
2
3
4
5
6
7
8
9
10
11
$ 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 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