systemd 的配置文件的编写(一[Unit]和[Install]、通用)

上一篇文章简单的说了下怎么写 .service 文件,从这篇开始,详细的介绍下 systemd 的配置文件如何编写。

一、基本情况

systemd 其实除了常用的 .service 文件,还有以下几种配置文件,分别实现不同的功能:.socket.device.mount.automount.swap.target.path.timer.snapshot,基本上看扩展名就知道什么意思了。所有这此文件,都和 .service 一样,使用 .desktop 文件的语法(即 windows 下 ini 文件的语法),其具体内容可以参考这里

所有的这些文件都有共同的两个 group ,或者说 section,即 [Unit] 和 [Install](注意,这些文件的内容都是区分大小写的),不同文件可能其它不同的段,比如[Service]/[Mount]等。下面就具体介绍下这些文件。

这些文件在启动时按一定的顺序载入,一般情况下是这样的:

表1. 以系统模式运行时的载入顺序 (--system).

路径 描述
/run/systemd/generator.early Generated units (early)
/etc/systemd/system 本地配置
/run/systemd/systemd Volatile units
/run/systemd/generator Generated units (middle)
/usr/local/lib/systemd/system local 包中的文件
/usr/lib/systemd/system 安装包中的文件
/run/systemd/generator.late Generated units (late)

 

表2. 用户模式下的载入顺序 (--user).

路径 描述
/tmp/systemd-generator.early.XXXXXX Generated units (early)
/etc/systemd/user Local configuration
/run/systemd/user Volatile units
/tmp/systemd-generator.XXXXXX Generated units (middle)
/usr/local/lib/systemd/user Units for local packages
/usr/lib/systemd/user Units for installed packages
/tmp/systemd-generator.late.XXXXXX Generated units (late)

按顺序从高到低,先载入的会覆盖后载入的。从而,一般来讲,如果自己定义的 .service ,要放在/etc/systemd/system里,如果是打包,要放在/usr/lib/systemd/system里,在Magic上,这个路径在rpm打包的时候可以用%{_unitdir}来代替。

在用户模式运行时 (–user),可以用变量 $SYSTEMD_UNIT_PATH 指定路径。

也可能使用 systemctl 的 link 指令来指定不在上面这些目录中的配置(很少见)。

这些文件里可能含有不能支持的选项,systemd在这种情况下,会给出警告然后继续运行。如果一个选项以X-开头,systemd会完全忽略它。不过应用程序可以使用这些文件包含的附加信息。

关于文件中的数据类型。布尔类型可以写成多种格式,真值可以写成 1,yes,true,on,假值可以写成0,no,false,off。

时间类型也支持多种格式,一个单独的数字,指多少秒;也可以带有时间后缀,如果多个后缀在一起,会相加。例如:“50”指的是50秒,“2min 200ms”指的是2分钟200毫秒,即120200毫秒。文件中可以支持这样的时间后缀:s, min, h, d, w, ms, us。具体的内容在讨论.time文件的那段还会再说。

空行和以#和;开始的行会被忽略,所以这可以用来写注释。以反斜杠(\)连接在一起的行,处理时会将反斜杠替换成空格,所以可以用这个来把太长的行分成多行。

和一个 foo.service 文件在一起存在的可能有一个 foo.service.wants/ 目录,所有这个目录里的链接的这些文件都隐含的增加在 Wants= 键的后面,这样可以在不修改原文件的情况下,为其增加依赖关系。关于 Wants= 的详细信息可以看下面。建立这些链接的办法是在 systemctl enable 命令时通过读取文件中的 [Install] 段来建立。类似的还有 .requires/ 目录,对应的就是Requires= 。

和一个 foo.service 文件一起存在的还可能有一个 foo.service.d/ 目录,所有在这个目录中带有.conf后缀的文件会在 foo.service 本身解析后进行解析,这对不编辑 foo.service 文件而给它增加一些配置是很有用的。注意确保解析的指令有相应的节头。

如果一行以.include 开头,后面跟上文件名,这样会把指定的文件解析,注意确保解析的指令有相应的节头。和其它语言的包含文件差不多。

注意,虽然 systemd 提供了这样一个灵活的依赖系统,但是建议不是必要的情况,不使用此功能,而是通过基于总线(bus)或套接字(socket)的激活让 systemd 来自己处理这些依赖关系,这样可以实现更简单、灵活的系统。(吐个槽,这个其实没那么容易

有些文件的名称反映了现有系统的路径,比如 dev-sda.device 是指 /dev/sda 设备,这种情况下,需要使用一些替换,比如 / 被换成 -,所有不可打印字符和-换成\x20,root目录/换成-,而其它的前后的/都被删除。

有时,systemd 可以在运行时从一个模板文件产生一个实例,这样可以让 systemd 从一个单一文件产生多个单元文件,systemd首先会在文件系统中查找文件,如果没有找到,而文件名中包含@字符,那它会去找模板文件,其名称是保留@前面和后缀。例如:如果找getty@tty3.serivce没有找到,那么systemd会找getty@.service,并将它从配置文件中实例化。配置文件中用于实例化的字符串,可以用特殊字符 i% 来指定,详细描述请参看后面。

如果文件是空的,或链接到/dev/null,它将不被加载,这样可以用来禁用服务,让它即使手工也无法启动。

这些文件的格式基本是稳定的(从systemd 26,即 fedora15以后),具体的可以参看这里,就是说在将来的版本中,这些配置仍然完全可用。

二、选项 [Unit] 段

[Unit]段是不依赖特定文件类型的一些一般信息,其可用键如下:
Description=   :一些描述,显示给用户界面看的,可以是任何字符串,一般是关于服务的说明。
Documentation=  :指定参考文档的列表,以空格分开的 URI 形式,如http://, https://, file:, info:, man:,这是有顺序的,最好是先解释这个服务的目的是什么,然后是它是如何配置的,再然后是其它文件,这个选项可以多次指定,会将多行的合并,如果指定了一个空的,那么会重置此项,前的配置不在起作用。
Requires=   :指定此服务依赖的其它服务,如果本服务被激活,那么 Requires 后面的服务也会被激活,反之,如果 Requires 后面的服务被停止或无法启动,则本服务也会停止。这个选项可以指定多次,那么就要求所有指定的服务都被激活。需要注意的是这个选项不影响启动或停止的顺序,启动顺序使用单句的 After= 和 Before= 来配置。例如,如果 foo.service 依赖  bar.serivce,但是只配置了 Requires= 而没有 After= 或 Before=,那么 foo.service 启动时会同时激活 foo.service 和 bar.service。通常使用 Wants= 代替 Requires= 是更好的选择,因为系统会更好的处理服务失败的情况。注意,这种依赖关系,也可以在文件之外来处理,即使用 .requires/ 目录,可以参看上面的说明。
RequiresOverridable=     :类似上面的 Requires= ,不过这种情况下,只要用户明确要求它启动,才会影响到被依赖的服务,不然服务出错什么的,不会影响被依赖服务的启动。
Requisite=RequisiteOverridable=  :分别类似上面的两个,不过如果是这个指定服务没有启动,被依赖的服务会不启动,立即失败。
Wants=    :相对弱化的 Requires= ,这里列出的服务会被启动,但如果无法启动或无法添加到事务处理,并不影响本服务做为一个整体的启动。这是推荐的两个服务关联的方式。这种依赖也可以配置文件外,通过 .wants/ 目录添加,具体可以看上面的说明。
BindsTo= :和 Requires= 很像,但是这种情况,如果他后面列出的服务停止运行或崩溃之类的,本服务也会同时停止。
PartOf=  :又一个类似 Requires= 的选项,但是限制在停止或重启动服务,如果这里列出的服务被停止或重启动,那么本服务也会停止或重启动,注意这个依赖是意向,即本服务停止或重启动,不会影响到这里列出服务的运行状态。
Conflicts= :配置一个依赖冲突,如果配置了些项,那么,当一个服务启动时,或停止此处列出的服务,反过来,如果这里列出的服务启动,那么本服务就会停止,即后启动的才起作用。注意,此设置和 After= 和 Before= 是互相独立的。如果服务 A 和 B 冲突,且在 B 启动的时候同时启动,那么有可能会启动失败(两都都是必需的)或修改以修复它(两者之一或两都都不是必需的),后一种情况,会将不需要的依赖删除,或停止冲突。
Before=After=  :配置服务间的启动顺序,比如一个 foo.service 包含了一行 Before=bar.service,那么当他们同时启动时,bar.service 会等待 foo.service 启动完成后才启动。注意这个设置和 Requires= 的相互独立的,同时包含 After= 和 Requires= 也是常见的。此选项可以指定一次以上,这时是按顺序全部启动。
OnFailure=       :列出一个或更多的服务,当本服务启动状态是 failed 的时候,激活这些服务。
PropagatesReloadTo=ReloadPropagatedFrom=  :这两个是列出一些服务,当其它服务 reload 时同时 reload 这个服务,或者反之。
RequiresMountsFor=  :用空格分开的绝对路径列表,是 Requires= 和 After= 添加的依赖中的 mount 文件需要访问的指定的路径。
OnFailureIsolate=  :是一个布尔值,如果是真,那么 OnFailure= 后面的服务会进入隔离模式,即所有不是它依赖的服务都会停止。如果只设置一个服务,可以放在 OnFailure= 后,默认值是假。
IgnoreOnIsolate=    :一个布尔值.如果是真则当隔离其它服务时本服务不会停止(不明白隔离是什么意思,大概在后面)。默认是假。

IgnoreOnSnapshot=  :一个布尔值.如果是真则本服务不包含快照(snapshots)。对 device 和 snapshot 服务默认为真,其它服务默认为假。
StopWhenUnneeded=  :一个布尔值。如果是真则当本服务不使用时会停止。 注意,为了尽量减少 systemd 的工作,默认情况下是不会停止不使用的服务的,除非和其它服务冲突,或用户明确要求停止。如果设置了这个选项,那么如果没有其它活动的服务需要此服务,它会自动停止。默认值是假。

RefuseManualStart=RefuseManualStop=  :布尔值。如果设为真值,则此服务只能间接的激活或停止。这种情况下,用户直接启动或停止此服务会被拒绝,只有做为其它的服务依赖关系,由其它服务进行启动或停止才可以。这主要是为了停止用户误操作。默认值是假。

AllowIsolate=      :布尔值。如果是真值,则此服务可以使用 systemctl isolate 命令进行操作。否则会拒绝此操作。最好的办法是不要动这处选项,除非目标服务的行为类似于 SysV 启动系统中的 runlevels。只是一种预防措施,避免系统无法使用的状态。默认值是假。

DefaultDependencies=  :布尔值。如果是真(默认值),一些本服务默认的依赖会隐式的建立,具体是哪些依赖,则于服务的类型决定。比如,对于普通的服务(.service类型),它会确保在系统基本服务启动后才启动本服务,会在系统关机前确保本服务已关闭。一般来说,只有早期开机服务和后期的关机服务,才需要把这个设成假。强烈对大多数普通服务,让这个选项启用即可。如果设成假,也不会禁用所有的隐式依赖,只是禁用那些非必要的。
JobTimeoutSec=  :当一个客户端等待本服务的某个 Job 完成时,所指定的超时时间。如果达到了限制的时间,此 Job 会取消运行,但服务不会更改状态,包括进入“failed”状态。除了设备服务(即.device类型),其它的默认值是0(即没有超时设置)。注意,这个是独立于特定服务所设置的超时设置的(比如对 .service 类型所设置的 Timeout=),它对服务本身没有影响,但特定服务的设置是有影响的(能用来更改服务状态)。《-这段不明白到底是什么意思,所以翻译的也是乱七八糟,真对不起)。
ConditionPathExists=ConditionPathExistsGlob=ConditionPathIsDirectory=ConditionPathIsSymbolicLink=ConditionPathIsMountPoint=ConditionPathIsReadWrite=ConditionDirectoryNotEmpty=,ConditionFileNotEmpty=ConditionFileIsExecutable=ConditionKernelCommandLine=ConditionVirtualization=ConditionSecurity=ConditionCapability=ConditionHost=ConditionACPower=,ConditionNull=  :这是一组类似的东西。检测特定的条件是不是真值,如果不是真值,服务会略过启动,但是它依赖的服务还是会正常运行的。这个条件测试失败不会让服务进入失败状态。条件是在服务开始运行时检查的。
  ConditionPathExists= 是指定在服务启动时检查指定文件的存在状态。如果指定的绝对路径名不存在,这个条件的结果就是失败。如果绝对路径的带有!前缀,则条件反转,即只有路径不存在时服务才启动。
  ConditionPathExistsGlob= 类似上面的选项,但支持通配符。
  ConditionPathIsDirectory= 判断指定路径是不是目录。
  ConditionPathIsSymbolicLink= 判断指定路径是不是链接。
  ConditionPathIsMountPoint= 判断指定路径是不是一个挂载点。
  ConditionPathIsReadWrite= 多年指定路径是否可读写(即不是做为只读系统挂载的)
  ConditionDirectoryNotEmpty= 判断指定目录是否存在且不为空。
  ConditionFileNotEmpty= 判断指定文件是否是常规文件且不为空(即大小不是0)。
  ConditionFileIsExecutable= 判断指定文件是否是常规文件且可执行。
  类似的,ConditionKernelCommandLine=是判断有没有指定的内核命令行启动参数(或带有!反之),这个参数必须是一个单词或用=分开的两个单词,前一种情况下,会寻找内核参数是否有此单词或是赋值的左边。后一种情况则必须是赋值的左右同时符合。
  ConditionVirtualization= 是判断是不是在虚拟化环境下执行的服务。这可以是个布尔值以判断是不是任意的虚拟化环境,或者下列的字符串之一: qemu, kvm, vmware, microsoft, oracle, xen, bochs, chroot, openvz, lxc, lxc-libvirt, systemd-nspawn,以判断是不是特定的虚拟化环境,多重嵌套的虚拟化环境,只判断最后一层。可以使用!进行反转判断。
  ConditionSecurity= 是判断系统是否启用了安全环境,当前仅能识别selinuxapparmor, 和 smack。可以使用!进行反转判断。
  ConditionCapability= 是判断服务管理器绑定的 capability 是否存在。(可以查看其它部分的详细信息。)设置为 capability 的名字,比如 CAP_MKNOD。可以通过在前面加!反转判断。
  ConditionHost= 是判断主机名 (hostname)或机器ID(machine ID)是否匹配。可以加!反转。
  ConditionACPower= 是判断机器是否在使用交流电源。如果设成 true,而只有至少连接一个交流电源时结果才为真,反过来,设成 false,则不连接所有交流电源时才为真。
  最后,ConditionNull= 是一个常量性质的判断条件,它应该是布尔值,如果设成 false ,则条件永远失败,反过来则永远成立。
  如果指定多个条件,则所有条件都需要成立(即条件之间是 AND 的关系)。条件前面可以加上 | 符号,这时条件变成一个触发条件,服务定义了触发条件,那么在满足其它非触发条件和这个触发条件的情况下,服务会至少执行一次。同时指定|和!前缀时,先处理|,后处理!。除了ConditionPathIsSymbolicLink=,其它条件均跟随链接。如果这些条件指定为空,则相当于重置,前面的任何设置都不再起作用。
SourcePath=  :这个服务生成的配置文件所在的路径,这主要是用在生成工具从外部配置文件的格式转换到本地服务的配置格式中。因此,对一般的服务不要使用此选项。
服务文件还可能包含一个 [Install] 段,这个段的内容服务的安装信息。它不在 systemd 的运行期间使用。只在使用 systemctl enable 和 systemctl disable 命令启用/禁用服务时有用。
Alias=  :在安装使用应该使用的额外名字(即别名)。名字必须和服务本身有同样的后缀(即同样的类型)。这个选项可以指定多次,所有的名字都起作用,当执行 systemctl enable 命令时,会建立相当的链接。

WantedBy=RequiredBy= :在 .wants/ 或 .requires/ 子目录中为服务建立相应的链接。这样做的效果是当列表中的服务启动,本服务也会启动。 在  bar.service 中的 WantedBy=foo.service  和 Alias=foo.service.wants/bar.service 基本是一个意思。

Also=  :当此服务安装时同时需要安装的附加服务。 如果用户请求安装的服务中配置了此项,则 systemctl enable 命令执行时会自动安装本项所指定的服务。

在 [Install] 段使用这些字符串有特定含义: %n, %N, %p, %i, %U, %u, %m, %H, %b. 详细信息看下一段。、

特殊字符串

许多设置支持使用特殊的字符串,可以在运行或加载时替换成特定的内容。下表是支持的字符串。

表 3. 在服务文件中可以使用的字符串

字符串 简介 详细信息
%n 完整的服务名称
%N 不转义的完整服务名称
%p 前缀名 对于实例化的服务,这是前@前面的部分,对于其它的服务,是指去掉后缀(即类型)的部分。
%P 不转义的前缀名
%i 实例名称 对于实例化的服务,这是指 @和后缀之间的部分。
%I 不转义的实例名。
%f 不转义的文件名。 这可以不转义的实例名(如果可用)或前缀名,带有/前缀。
%c 服务的控制组路径。?
%r systemd 的根控制组路径。?
%R systemd 的根控制组路径的父目录。
%t 运行时 Socket 目录。 这可以是 /run (系统管理器) 或 $XDG_RUNTIME_DIR (用户管理器).
%u 用户名 这是服务配置的用户或systemd运行实例的用户(如果没有配置的话)。
%U 用户 UID 这是服务配置的用户UID或systemd运行实例的用户UID(如果没有配置的话)
%h 用户家目录 这是服务配置的用户家目录或systemd运行实例的用户家目录(如果没有配置的话)
%s 用户Shell 这是服务配置的用户shell或systemd运行实例的用户shell(如果没有配置的话)
%m 机器 ID 运行系统的机器 ID ,格式是一个字符串。
%b 启动 ID 运行系统的启动 ID ,格式是一个字符串。.
%H 主机名 运行系统的主机名。
%% 转义 % 一个单百分号.

systemd 的手册页:http://www.freedesktop.org/software/systemd/man

fedora 的 systemd 说明页面:http://fedoraproject.org/wiki/Packaging:Systemd,中文:https://fedoraproject.org/wiki/Systemd/zh-cn

unbuntu 的 systemd 说明页面:https://wiki.edubuntu.org/systemd

arch 的 systemd 说明页面:https://wiki.archlinux.org/index.php/Systemd/,中文:https://wiki.archlinux.org/index.php/Systemd_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

发表回复