在無頭系統上啟動共享會話 D-Bus 的 systemd 服務
我需要幫助在無頭 Linux 系統上啟動通過會話(而非系統)D-Bus 進行通信的服務。關鍵是沒有人會登錄到無頭系統上。
到目前為止,我已經能夠在三個不同的終端中代表未登錄的使用者(“其他使用者”)啟動 D-Bus 守護程序並測試 D-Bus 通信:
在第一個終端中,我為“其他使用者”啟動了一個 D-Bus 守護程序:
$ sudo -u otheruser dbus-daemon --session --print-address 1 unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48
在第二個終端中,我使用上面的 DBUS_SESSION_BUS_ADDRESS 響應啟動 D-Bus 伺服器應用程序:
$ sudo -u otheruser DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48" /usr/bin/my-dbus-service
然後,在第三個終端中,我可以測試連接:
$ sudo -u otheruser DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48" gdbus introspect --session --dest com.mycompany.myappname --object-path /com/mycompany/interface
但是,我想通過 systemd 啟動 D-Bus 伺服器應用程序以及一些客戶端 D-Bus 服務。如何通過 systemd 啟動 D-Bus 會話,以便將其 DBUS_SESSION_BUS_ADDRESS 環境變數傳播到“其他使用者”的 D-Bus 伺服器和客戶端服務?
一種可能的解決方案可能是將 dbus-daemon 的輸出通過管道傳輸到“somefile”中,然後在啟動 D-Bus 伺服器和客戶端之前設置 DBUS_SESSION_BUS_ADDRESS=$(cat somefile)。這對我來說似乎有點太尷尬了。特別是因為我知道系統D-Bus 連接的 systemd 服務文件中的“Busname”指令有一些魔力。如何正確啟動“其他使用者”的 systemd 服務,以便這些 systemd 服務可以與會話 D-Bus 介面通信?
你需要幾件事來完成這項工作。
- 使使用者服務無需使用者登錄即可在引導時執行(systemd linger)。
- 一個 systemd 套接字文件,用於指定 systemd 分配的 D-Bus 套接字。
- 用於啟動 D-Bus 會話匯流排的 systemd 服務,然後為其他 systemd 服務設置 DBUS_SESSION_BUS_ADDRESS 環境變數。
- 確保您的 systemd
my-dbus-client.service
文件屬於Type=dbus
或依賴於該dbus.socket
單元,以確保它們分配 dbus 會話匯流排套接字並啟動 dbus 會話服務(如果尚未啟動)。首先,要使給定使用者的 Systemd 服務在啟動時無需登錄即可啟動,您需要啟用 systemd 使用者延遲 - 在配置為使用者啟用它時,只需以 root 身份執行一次:
# loginctl enable-linger otheruser
接下來,如果您在基於 Debian 的系統上,對於接下來的兩個步驟,您可以簡單地安裝包 dbus-user-session 包:
# apt-get install dbus-user-session
如果您正在使用其他發行版,想要手動執行此操作,或者只是想了解它是如何工作的,請繼續。否則跳過
dbus.service
and的創建dbus.socket
。使用以下內容創建文件
/usr/lib/systemd/user/dbus.socket
(注意,在某些發行版上,使用者目錄可能位於/lib
而不是/usr/lib
):[Unit] Description=D-Bus User Message Bus Socket [Socket] ListenStream=%t/bus ExecStartPost=-/bin/systemctl --user set-environment DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus [Install] WantedBy=sockets.target Also=dbus.service
傳播
DBUS_SESSION_BUS_ADDRESS
到所有服務,這是您主要關心的問題,由ExecPostStart
下面的行解決 - 所有後續服務都將具有該設置。
%t
被替換為XDG_RUNTIME_DIR
- systemd 創建的臨時目錄,該目錄/run
特定於使用者會話,您可以填充文件。如果你想在其他地方創建這個套接字,你沒有理由不能。只要確保它是暫時的,或者它在重新啟動/會話拆除時被清理。我在嘗試使 dbus unix 套接字成為抽象套接字時確實遇到了一些問題——systemd 由於某種原因似乎不喜歡這種形式
unix:abstract=
或@
前綴。現在創建
/usr/lib/systemd/user/dbus.service
具有以下內容的文件:[Unit] Description=D-Bus User Message Bus Requires=dbus.socket [Service] ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig [Install] Also=dbus.socket
systemd 在幕後使用了一點魔法,將已經創建的 unix 套接字傳遞給 dbus-daemon。Systemd 使用來自的資訊
dbus.socket
來創建套接字,並且它的文件描述符在環境變數中設置LISTEN_FDS
,然後傳遞到dbus-daemon
. 上面列出的特殊選項使 dbus-daemon 使用傳入的文件描述符而不是創建一個新的。這允許 dbus 客戶端與 dbus-daemon 並行啟動,而不必擔心套接字不存在。最後,創建您自己的 systemd 使用者服務,確保將類型設置為
Type=dbus
,設置BusName=
為將由該服務註冊的 dbus 服務名稱之一的名稱,或者確保Requires=dbus.socket
在 Unit 部分中指定。這是一個例子:[Unit] Description=Config Server Startup [Service] Type=dbus BusName=com.example.app.configuree ExecStart=/opt/example/app/configuration_server Restart=on-failure [Install] WantedBy=default.target
您可以將它們放置在以下幾個位置之一:
$HOME/.config/systemd/user
/usr/lib/systemd/user
啟用您的服務
systemctl --user enable <service name>
並重新啟動,一切都應該工作。為了
systemctl --user ..
工作,您需要有一個完整的 systemd 登錄環境,以便 /run/user/{uid} 存在。由 su - .. –login 或 sudo 創建的輕量級環境不設置此項。您需要 ssh 登錄,登錄到控制台,或者,如果您執行正確設置的 systemd 發行版,您可以抓取並使用machinectl shell
在目前 shell 中創建完整的 systemd 環境。參考:
man loginctl
逗留man pam_systemd
對於 XDG_RUNTIME_DIR 資訊man systemd.service
對於 Type=dbus、BusName= 和對 dbus.socket 的隱式依賴man sd_listen_fds
有關 LISTEN_FDS 環境變數的資訊- https://wiki.archlinux.org/index.php/Systemd/User - 關於 systemd 使用者會話的一般資訊