作为systemd的用户,使用systemctl与系统管理器进行交互是相当普遍的。但是,如果您正在编写代码,并且希望通过编程方式操作unit和service,该怎么办?一种方法是将systemctl放入subprocess中,但是这种方法有开销,和其他事情需要考虑。另一种方法是通过dbus与systemd通信。

什么是dbus?

在Linux中,dbus是一种使进程相互通信的方法。它是进程间通信的一种实现。有关什么是dbus以及更多信息,请查看freedesktop文档。

暂时忘了Python,让我们来探讨一下dbus。 gdbus是一个实用程序,我们可以用来看dbus。 dbus可以执行的一项操作是发现对象API。例如,如果我想看看我们可以通过dbus使用systemd做什么,我可以执行以下操作…

$ gdbus introspect \
--system \
--dest org.freedesktop.systemd1 \
--object-path /org/freedesktop/systemd1
输出看起来像这样(为解释而折叠)…
node /org/freedesktop/systemd1 {
interface org.freedesktop.DBus.Peer {
...
};
interface org.freedesktop.DBus.Introspectable {
...
};
interface org.freedesktop.DBus.Properties {
...
};
interface org.freedesktop.systemd1.Manager {
...
};
node job {
};
node unit {
};
};

我们将专注于Manager界面以与systemd服务管理器进行交互。该界面包含以下部分(为简洁起见,省略了部分内容)…

interface org.freedesktop.systemd1.Manager {
methods:
GetUnit(in s arg_0,
out o arg_1);
GetUnitByPID(in u arg_0,
out o arg_1);
…
signals:
UnitNew(s arg_0,
o arg_1);
UnitRemoved(s arg_0,
o arg_1);
…
properties:
@org.freedesktop.DBus.Property.EmitsChangedSignal(“const”)
readonly s Version = ‘237’;
@org.freedesktop.DBus.Property.EmitsChangedSignal(“const”)
readonly s Features = ‘+PAM +AUDIT +SELINUX …’;
…
};

对于dbus界面中的三个部分:方法是我们与dbus对象进行交互的方式,信号是我们如何获取某些事件的通知,而属性只是存储的数据。我们将专注于方法,因为这将是您将使用的最常见功能。

通过dbus与systemd通信

上一节很重要,因为它使我们(dbus用户)能够弄清楚我们可以做什么以及如何通过dbus与特定进程进行交互。用gdbus调用方法并不复杂。

假设我们要确定sshd.service单元是否已启用。我们将执行以下操作(从上述输出中发现)…

$ gdbus call \
--system \
--dest org.freedesktop.systemd1 \
--object-path /org/freedesktop/systemd1 \
--method org.freedesktop.systemd1.Manager.GetUnitFileState \
sshd.service

(在我的计算机上)输出是预期的 (‘enabled’,)。仔细检查上述输出中的所有不同方法,以确切了解可以通过dbus执行的操作。

使用Python发出dbus请求

上面是运行systemctl status sshd.service的一种花哨的方法。但这是为了允许我们通过Python代码使用dbus。

import dbus
unit_name = 'sshd.service'
bus = dbus.SystemBus()
systemd = bus.get_object(
org.freedesktop.systemd1',
'/org/freedesktop/systemd1'
)
manager = dbus.Interface(
systemd,
'org.freedesktop.systemd1.Manager'
)
unit_state = manager.GetUnitFileState(unit_name)

我不会逐行讲,但通过实例化总线并在systemd的Manager接口上获取句柄,我们可以进行相同的GetUnitFileState方法调用。

注意:我非常重视与dbus交互的重要部分。我们可以讨论两种类型的总线:系统总线和用户总线。系统是用于大多数系统服务的机器的一条总线(这就是为什么我使用系统总线获取有关sshd.service的信息的原因)。用户总线对于每个登录都是唯一的,并且用于用户应用程序的通信。使用gdbus,您可以为系统总线指定--system或为用户总线指定--session。在Python API中,您将为系统总线初始化SystemBus或为用户总线初始化SessionBus。