![编写依赖于 Xorg 的服务](https://linux22.com/image/1410182/%E7%BC%96%E5%86%99%E4%BE%9D%E8%B5%96%E4%BA%8E%20Xorg%20%E7%9A%84%E6%9C%8D%E5%8A%A1.png)
我正在尝试为 编写一个用户级服务redshift
,它需要等到 Xorg 启动并运行。我当前的服务文件如下所示:
[Unit]
Description=Redshift
After=graphical.target
[Service]
Environment=DISPLAY=:0
ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr
Restart=always
[Install]
WantedBy=default.target
但是,它似乎试图在 Xorg 启动之前启动,之后我必须手动启动该服务。我想我使用了错误的After=
目标。有什么提示吗?
答案1
我一直在研究这个问题,grawity 的答案似乎已经过时了。现在,您可以使用 systemd 设置用户服务,这些服务作为用户会话的一部分运行。他们可以设置 DISPLAY 和 XAUTHORITY(目前在 Arch、Debian Stretch+ 和 Ubuntu 中)。
这比以前使用桌面自动启动文件的建议更有意义,因为您可以像系统级应用程序(重新启动等)一样进行进程管理。
目前最好的文档是 Arch wiki;Systemd/用户
TLDR 版本;
- 在中创建所需的 *.service 文件
~/.config/systemd/user/
- 运行
systemctl --user enable [service]
(不包括.service后缀) - 可选择立即
systemctl --user start [service]
运行 - 用于
systemctl --user status [service]
检查其运行情况
一些其他有用的命令。
systemctl --user list-unit-files
- 查看所有用户单位systemctl --user daemon-reload
- 如果你编辑 .service 文件
- 之后...
我升级并将大多数会话守护进程转换为 systemd .service 文件。因此我可以添加一些额外的注释。
没有默认钩子可以在登录时运行服务,因此您必须自己触发它。我从我的~/.xsession
文件中执行此操作。
systemctl --user import-environment PATH DBUS_SESSION_BUS_ADDRESS
systemctl --no-block --user start xsession.target
第一行将一些环境变量导入到 systemd 用户会话中,第二行启动目标。我的xsession.target
文件;
[Unit]
Description=Xsession running
BindsTo=graphical-session.target
以我的xbindkeys.service
为例。
[Unit]
Description=xbindkeys
PartOf=graphical-session.target
[Service]
ExecStart=/usr/bin/xbindkeys -n -f ${HOME}/projects/dotfiles/.xbindkeysrc
Restart=always
[Install]
WantedBy=xsession.target
答案2
通常的提示是“不要”。 redshift
不是系统范围的服务——它有一个单独的实例每次会议,并且它需要知道如何连接到该特定会话的 Xorg。
(Xorg 也不是系统服务——只有显示管理器是的,它还为每个会话启动一个单独的 Xorg。//graphical.target
会告诉您显示管理器何时准备就绪,但它并未说明 DM 何时实际启动第一个或所有显示器。)
仅在启动时启动它DISPLAY=:0
是不够的,因为无法保证在任何给定时间都只有一个显示器,也无法保证它总是存在:0
(例如,如果 Xorg 崩溃并留下一个过时的锁文件,则下一个会运行,:1
因为它会认为:0
仍然被占用);您还需要设置文件的路径,XAUTHORITY
因为 X11 需要身份验证;并确保redshift
在您注销并再次登录时重新启动。
那么如何开始呢?桌面环境几乎总是有几种启动自己的方法会议服务。查看旧帖子其中已经描述了两个常见的因素:~/.xprofile
剧本和~/.config/autostart/*.desktop
地点。
如果你使用启动,您可以使用它~/.xinitrc
来启动这些程序。独立窗口管理器通常有自己的启动/初始化脚本;例如~/.config/openbox/autostart
Openbox。
所有这些方法的共同点是程序都是从之内会议——避免上面列出的所有问题。
答案3
这是我刚刚创建的解决方法,目前还不可用graphical-session.target
(在我的 Kubuntu 16.04 系统上):
- 创建一个伪 systemd 用户单元它使 graphic-session.target 上升和下降。
使用下列内容创建~/.config/systemd/user/xsession.target
:
[单元] 描述 = Xsession 已启动并正在运行 BindsTo=graphical-session.target
告诉 systemd 有关这个新单元的信息:
$> systemctl --user daemon-reload
- 创造自动启动和关闭脚本它
xsession.target
通过 Ubuntu 16.04 桌面当前可用的机制进行控制。
使用下列内容创建~/.config/autostart-scripts/xsession.target-login.sh
:
/bin/bash #!/bin/bash 如果 !systemctl --user is-active xsession.target &> /dev/null 然后 /bin/systemctl --user import-environment 显示 XAUTHORITY /bin/systemctl --user 启动 xsession.target 菲
使用下列内容创建~/.config/plasma-workspace/shutdown/xsession.target-logout.sh
:
/bin/bash #!/bin/bash 如果 systemctl --user is-active xsession.target &> /dev/null 然后 /bin/systemctl --user 停止 xsession.target 菲
使脚本可执行:
$> chmod + x ~/.config/autostart-scripts/xsession.target-login.sh $> chmod + x ~/.config/plasma-workspace/shutdown/xsession.target-logout.sh
笔记:这两个文件被放置在 KDE 会选择它们进行自动启动和关闭的位置。对于其他桌面环境(例如 Gnome),这些文件可能被放置在其他地方 - 但我不知道这些环境。
笔记:此解决方法不支持多桌面会话。它只能graphical-session.target
在一台机器上只运行一个活动的 X11 会话时才能正确处理(但对于我们大多数 Linux 用户来说,情况就是这样)。
- 创造您自己的 systemd 用户单元哪些依赖于
graphical-session.target
并让它们在您的桌面上登录时干净地运行。
例如@mkaito 的单位应该是这样的:
[单元] 描述=Redshift PartOf=图形会话.target [服务] ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr 重启=总是
daemon-reload
(编辑单位后不要忘记做一个!)
- 重启机器,登录并验证设备是否按预期启动
$> systemctl --用户状态图形会话.target ● graphic-session.target - 当前图形用户会话 已加载:已加载(/usr/lib/systemd/user/graphical-session.target;静态;供应商预设:已启用) 活跃:自 Don 2017-01-05 15:08:42 CET;47 分钟前开始活跃 文档:man:systemd.special(7) $> systemctl --用户状态你的单位...
在未来的某一天(会是 Ubuntu 17.04 吗?),我的解决方法将变得过时,因为系统graphical-session.target
本身将正确处理。到那时,只需删除自动启动和关闭脚本,xsession.target
您的自定义用户单元可能保持不变并正常工作。
答案4
很多答案都暗示需要一些解决方法或脚本。至少在最近的 GNOME 会话中,我不再需要这样的解决方法。
如果我添加WantedBy=graphical-session.target
,那么必要的 X11 环境变量(即DISPLAY
和XAUTHORITY
)可供服务使用,并且我可以在我的用户服务文件中执行 X11 程序:
[Unit]
...
PartOf=graphical-session.target
[Service]
...
[Install]
WantedBy=graphical-session.target
据我所知,graphical-session.target
是一个被动目标。使用它意味着正在启动的实际会话通过绑定到此目标BindsTo=graphical-session.target
。对于gnome-session.target
这似乎是这种情况,但对于较旧或不太流行的桌面环境可能并非如此。
我发现检查用户日志很有帮助journalctl --user -b
,它打印出按什么顺序达到了哪些目标:
5 月 08 日 21:28:10 earth systemd[2481]: 已达到目标 GNOME 打印机通知目标。5
月 08 日 21:28:10 earth systemd[2481]: 已达到目标 GNOME 会话。5
月 08 日 21:28:10 earth systemd[2481]: 已达到目标 GNOME X11 会话 (会话:gnome-flashback-metacity)。5
月 08 日 21:28:10 earth systemd[2481]: 已达到目标当前图形用户会话。
日志输出目标的描述,我们可以使用以下命令来识别实际的目标名称:
systemctl --user list-units --type target