我正在创建一个 bash 脚本,尝试将一个程序安装为用户服务,但无法systemctl --user
在脚本中运行命令。
这就是我所拥有的:
#!/bin/bash
(...)
useradd -M -g user -d "$3" user
(...)
[I create the unit file and move it to ~/.config/systemd/user/]
runuser -l user -c "systemctl --user list-units"
su user << 'EOF'
export XDG_RUNTIME_DIR=/run/user/$(id -u user)
export DBUS_SESSION_BUS_ADDRESS=/run/user/$(id -u user)/bus
systemctl --user daemon-reload
EOF
exit 0
因此,runuser 和此处的文档都会导致错误。
runuser 输出:
无法连接到总线:没有该文件或目录
此处的文档输出:
无法连接到总线:连接被拒绝
我一直在寻找解决方案,但尚未找到针对此特定情况的任何具体方法。
有谁遇到过类似的事情吗?
答案1
回答我自己的问题感觉很奇怪,但我通过执行以下操作设法找到了解决方案:
(...)
mkdir -p "/home/user/.config/systemd/user/multi-user.target.wants/"
chown user:user -R "/home/user/.config/"
loginctl enable-linger user
su user << 'EOF'
export XDG_RUNTIME_DIR=/run/user/$(id -u user)
export DBUS_SESSION_BUS_ADDRESS=/run/user/$(id -u user)/bus
systemctl --user daemon-reload
systemctl --user enable my-service.service
systemctl --user start my-service.service
EOF
(...)
启用 linger 足以进行 daemon-reload,但在那之后,启用会失败,因为文件夹/home/user/.config/systemd/user/multi-user.target.wants/
尚不存在。
奇怪的是,即使启用了 linger,并且我可以看到/lib/systemd/systemd --user
该用户正在运行的进程,但是当我执行su user
并登录到它的 shell 时,我仍然无法运行,systemctl --user
因为它输出:
无法连接到总线:没有此文件或目录只有在该 su 会话内再次设置环境变量后,我才能
systemctl --user
再次运行命令。
答案2
通常,此错误意味着 D-Bus 会话总线套接字不存在,或者它存在但是一个“过时”的套接字,没有进程再监听它。对于 systemctl,这还意味着私有的“直接到 systemd 的通道”套接字也不存在(因为 systemctl 会自动将其用作后备)。
总之,这意味着用户的systemd --user
服务经理根本没跑,因此此时“daemon-reload”是没有意义的——一开始就没有加载任何内容。
一般而言,su
和runuser
并非用于启动用户服务管理器;它们不会通过 pam_systemd 注册完整的“登录会话”。您只能使用它们来访问已运行的用户服务管理器。
(但即使它正在运行,在安装新单元后使用“daemon-reload”也很少有用;它只需要让 systemd 注意到自启动以来已经加载的现有单元的更改。)
您可能应该安装服务单元,/etc/systemd/user
以便所有用户都可以自动使用它,而无需您的 shell 脚本。
答案3
Systemd 248引入了一个-M
选项,使您能够更轻松地连接到其他用户的守护进程。
此外,正如@u1686_grawity 提到的,您可能希望安装此类全局单元文件/etc/systemd/user
。如果您想默认启用这些单元(据我所知,这就是您想要的),您也可以为所有用户这样做。因此在这种情况下,您可以这样做:
sudo cp your-unit.service /etc/systemd/user/
# I don't think `multi-user.target` exists for user daemons
# You can verify this with `systemctl --user list-unit-files -t target`
# You should use `default.target` instead
sudo systemctl --global enable your-unit.service
for USER in user0 user1 user2; do
# these two may not be necessary
sudo systemctl -M "$USER@" --user daemon-reload
sudo systemctl -M "$USER@" --user enable your-unit.service
sudo systemctl -M "$USER@" --user start your-unit.service
done