本文来自依云's Blog,转载请注明。
前几日群里又有人提到使用 systemd 管理用户的守护进程。早些时候我就知道有设计这么个功能,然调用systemctl --user时却总是跑去执行/bin/false,然后告诉我执行失败 QAQ
既然提到,就再试试呗。没想到这次systemctl --user列出一大堆 unit!还没弄明白它是哪里来的,不过至少说明systemctl --user已经可以用了!于是尝试性地把几个不依赖 X 的服务改成 systemd 服务了。至于依赖 X 的就不要 systemd 管了,我自己写在 Awesome 配置里,毕竟它们属于这个 X 会话,独立性没那么强,也免得纠结 DISPLAY 环境变量的问题。
我的 systemd 版本是 208。大概以前的版本确实有些问题,现在已经弄好了。
systemd 的用户配置位于~/.config/systemd/user、/lib/systemd/user和/etc/systemd/user下。我当然是用「家」里的那个了。
我改写的这些服务之前大部分是在 tmux 里跑的,除了占用 tmux 窗口外还占用个 zsh。现在全部改写成 systemd 服务了,于是 tmux 那边干净了不少 =w=
比如这个:
[Unit] Description=Privoxy Web Proxy With Advanced Filtering Capabilities ConditionPathIsDirectory=%h/.privoxy [Service] Type=simple PIDFile=/run/user/%U/privoxy.pid SyslogFacility=local0 ExecStart=/usr/bin/privoxy --pidfile /run/user/%U/privoxy.pid --no-daemon %h/.privoxy/config [Install] WantedBy=default.target
可以看到,systemd 里写起要执行什么命令还是挺简陋的。毕竟它不调用 shell 的,于是环境变量都用不了,家目录的位置也要写成%h,%U则是用户 ID。令我不解的是,执行的命令和#!里的一样,必须使用绝对路径,而不能只写个名字,依赖 $PATH 环境变量去寻找。于是就有这样丑陋的ExecStart了——
ExecStart=/bin/zsh -c "exec sslocal -s HOST -p PORT -l LOCALPORT -k PASSWORD -m METHOD -c <(echo {})"
至于那个SyslogFacility,是为了在 syslog-ng 的日志中不显示出来而定义的,这样就可以根据 facility 来过滤掉这些用户级服务的日志了。
还有个SyslogIdentifier也挺好用的。systemd 默认使用ExecStart里第一个字的文件名部分来作为日志的标识,于是在系统日志(syslog 和 journald)中就看到一堆 sh 以及一些 zsh 和 socat,没区分度了……于是写明 SyslogFacility 来赋予其一个更合理的名字。
systemd 用户级服务使用systemctl --user来管理,其它的和系统级的差不多,也是 enable / start / disable / stop / status 这些。当然 daemon-reload 命令会频繁地使用啦 :-)
查看 journald 日志却有些不同,也是加--user,但是查看某个服务的日志输出却不是-u SERVICE了。有个--user-unit NAME参数,但是它会只显示 systemd 启动的进程的日志,而没有 systemd 自己处理该服务时的日志了……通过阅读日志的 JSON 格式输出(-o json-pretty),终于弄了个比较理想的方案。以下是我在 zshrc 里的相关配置(zsh only 哦):
alias sysuser="systemctl --user"
function juser () {
# sadly, this won't have nice completion
typeset -a args
integer nextIsService=0
for i; do
if [[ $i == -u ]]; then
nextIsService=1
args=($args _SYSTEMD_CGROUP=/user.slice/user-$UID.slice/user@$UID.service/)
else
if [[ $nextIsService -eq 1 ]]; then
nextIsService=0
args[$#args]="${args[$#args]}$i.service"
else
args=($args $i)
fi
fi
done
journalctl -n --user ${^args}
}
加-n是因为默认全部输出,一下子跳到后边的日志时就太卡了,但是只输出最后一部分日志就挺快的。默认是十行,也可以接数字来指定数量。指定多次时最后一次生效,所以我问题把它写进去也不怎么影响手动指定的情况。
于是管理用户级服务时我就用sysuser ...,而要看某个服务的日志时就用juser -u xxx。
因为我使用 eCryptfs 加密了主目录,所以还遇到一个问题:用户级的 systemd 进程是通过 PAM 启动的,而这启动的时候我的主目录还没解密呢。于是 systemd 没有读取到我的配置,没有自动启动任何服务……于是只好在~/.profile里加两行,在挂载之后告诉 systemd 一声:
if [ -r "$HOME/.ecryptfs/auto-mount" ]; then
grep -qs "$HOME ecryptfs" /proc/mounts
if [ $? -ne 0 ]; then
mv $HOME/.Xauthority /tmp 2>/dev/null
mount -i "$HOME"
cd "$HOME"
mv /tmp/.Xauthority $HOME 2>/dev/null
systemctl --user daemon-reload
systemctl --user default
fi
fi

Feb 03, 2014 08:44:00 AM
早期 arch 刚转到 systemd 的时候,systemctl networkmanager 起不来后,我就放弃 arch 了,现在是什么情况了?
Feb 04, 2014 08:45:07 PM
我不用 NetworkManager。我还没遇到起不来的服务。
Feb 09, 2014 11:15:26 AM
当时 systemctl networkmanager 提示 networkmanager 不存在,去看一下,真的是空的。模仿写了一个结果不能用,遂放弃。
Feb 10, 2014 04:55:31 PM
查了一下,那个叫 NetworkManager 啊……
Feb 12, 2014 05:02:42 PM
我都去目录看了整个是空的。也不知道是我当时安装哪里有问题了,总之结果就是那样,所以放弃了。
Feb 12, 2014 08:31:09 PM
目录? /lib/systemd/system 么?怎么可能是空的……
Feb 17, 2014 01:19:58 PM
很久的事情了。我也忘了具体是什么目录,结局就是那样。
Jun 11, 2023 08:44:43 PM
>systemd 的用户配置位于~/.confg/systemd/user
s/.confg/.config/g
Jun 12, 2023 11:28:25 AM
已修正,喵~