Rc.d属于libexec,而不是etc

2020-08-26 00:47:16

让我们从争议开始:FreeBSD、NetBSD和OpenBSD中/etc/rc.d/下的脚本位于错误的位置。它们都应该位于/libexec/rc.d/中,因为它们是代码,而不是配置。

这个错位一直困扰着我很长时间,但当我非常投入NetBSD的时候,我从来没有精力打开这个蠕虫罐头。我怀疑这将是一场令人精疲力竭的讨论,而且是一件很难改变的事情。

如果您管理过BSD系统,您肯定遇到过/etc/rc.d/目录;如果您管理过未安装systemd的Linux系统,那么您肯定遇到过/etc/init.d/。这些目录包含用于在引导时配置系统的启动脚本,并且是不可变的。他们的代码被参数化,允许通过配置文件而不是通过代码编辑来更改他们的行为。这就是我批评的基础。

但在讨论为什么当前状态有问题以及事情应该是什么样子之前,让我们先深入了解一下我们是如何做到这一点的。为此,我们需要回到历史。

4.4BSD(1993)的引导过程相当简单:内核启动init,init然后在每个控制台上启动getty之前运行/etc/rc脚本。Etc/rc整体负责配置机器的文件系统和进程,并委托给另外两个脚本:/etc/netstart用于网络配置,/etc/rc.local用于本地添加的服务。正如我们稍后将看到的,当前的BSD系统在这方面更先进,但是核心引导过程保持不变:/etc/rc是主要入口点,并引导一组shell脚本。

在早期,包管理和文件来源跟踪,就像我们在流行的Linux发行版中习惯的那样,并不是一件事。您需要通过编辑可能设计为支持编辑的文件,也可能不是设计为支持编辑的文件,来调整系统的行为。如果您必须编辑/etc/rc,这是系统附带的脚本,那也没问题。

另一方面,系统没有附带/etc/rc.local,如果您希望添加自定义启动命令而不修改/etc/rc,则需要创建它。这就是事情变得有趣的地方。不需要支持/etc/rc.local:如果您可以编辑/etc/rc,那么为什么要处理单独的文件呢?原因很可能是为了简化系统升级:在升级期间,您希望受益于对/etc/rc所做的任何上游更改(其中一些更改实际上可能是正常系统操作所必需的)。将更新应用到手动修改的文件很棘手,因此将尽可能多的手动覆盖放入/etc/rc.local有助于将此问题降至最低。

System V4(SVR4,1988)也有自己的非常不同的引导过程。关键的区别在于系统V有运行级的概念。因此,配置引导过程是一项更加复杂的工作,因为可以为每个运行级别选择不同的服务。

为了完成每个运行级的调优,系统使用配置目录而不是文件:每个运行级都有一个单独的/etc/rcX.d/目录(其中X是运行级的编号),这些目录包含一个文件,每个要在启动时执行的操作。为了避免重复,这些文件只是指向/etc/init.d/下的公共文件的符号链接-并且命名符号链接,而不是它们的目标,以便它们的字典序顺序确定启动执行顺序。

同样,我们在这里已经可以观察到问题:/etc/rcX.d/下的符号链接是配置的,因为它们的存在指示要启动什么,它们的名称决定它们的启动顺序。但是/etc/init.d/下的文件不是:它们是系统附带的shell脚本,不应该手动修改。

NetBSD在其1.5版(2000)中实现了引导过程的现代化,它通过两种方式实现了这一点:第一,它引入了/etc/rc.d/作为目录,以包含每个操作和服务的单独脚本;第二,它引入了rcorder(8)工具来确定这些服务的运行顺序。Rcorder(8)使用脚本中编码的依赖关系信息作为注释,而不是像System V那样按字典顺序排序。FreeBSD在其5.0版(2003)中继承了这种设计,OpenBSD在其4.9版(2011)中重新实现了类似的设计。

准备好这两部分之后,NetBSD和FreeBSD中的/etc/rc脚本更改为根据rcorder(8)的输出执行/etc/rc.d/中的所有文件。这些脚本中有/etc/rc.d/local,其目的是运行/etc/rc.local(如果存在)。仅此而已,真的。因此,/etc/rc脚本变得微不足道。

这里需要注意的关键一点是,/etc/rc.d/中附带的脚本是通过用户控制的/etc/rc.conf文件高度可配置的。这实际上使脚本成为只读的,因为它将本地定制转移到配置文件。系统管理员不应该编辑脚本。相反,他们应该:修改/etc/rc.conf以定制要运行的内容和运行方式;如果他们愿意,在/etc/rc.d/下添加新脚本;以及编辑/etc/rc.local以轻松运行任意命令。

我的主要抱怨是/etc/rc.d/下的文件是不变的脚本。它们不属于/etc/,并且它们在那里的存在无缘无故地增加了系统升级的难度。

您可以看到:在NetBSD和FreeBSD中,系统升级是通过解压根目录中的新分发集,然后运行脚本来合并配置更新来进行的。此脚本是交互式的,有助于突出显示系统提供的新配置文件更新如何与以前的手动编辑冲突。(这个过程对您来说可能是初级的,但它实际上相当健壮且易于理解-并且您可以使用诸如sysupgrade之类的工具来使其变得微不足道。)。

-/etc/rc.d/npf 2019-08-09 19:09:42.800758233-0400++/tmp/temproot/etc/rc.d/npf 2019-11-16 10:39:27.000000000-0500@@-1,6+1,6@@#/bin/sh#-#$netbsd:npf,v1.32012/11/01 06:06:14 mrg Exp$+#$netbsd:npf,v1.42019年/04/。#@@-36,7+36,11@@echo";启用NPF。";npf_cfg_check/sbin/npfctl reload-/sbin/npfctl start++#npf_boot脚本已启用NPF。+IF[";$autoboot";!=";yes";];则+/sbin/npfctl start+fi}npf_stop()文件:/etc/rc.d/npf(Modified)请选择以下操作之一:d不安装新文件(保留旧文件)我安装新文件(覆盖本地修改!)。M合并当前安装的文件和新文件s显示当前安装的文件和新文件之间的差异su以统一格式显示差异(";diff-u";)sc显示上下文格式的差异(";diff-c";)ss并排显示差异(";sdiff-w187";)s命令使用指定的类似diff的命令显示差异v显示新文件您想做什么?[留待以后再说]

说真的,谁在乎呢?当您要做的是评估配置冲突时,为什么要分心审查代码更改呢?你实际上反对这些合并有多少次了?

您可能会说:嗯,我想确切地知道在升级期间我的机器的引导过程是如何改变的。当然,这是一个很好的目标,但这个程序是有缺陷的,完全不足以实现这样的目标。在上面的示例中,无论/etc/rc.d/npf做什么,也可以在它调用…的/sbin/npfctl二进制文件中完成。在升级过程中,您从未被要求检查对后者的更改,对吗?当然,您可以在自己的系统构建中检查二进制文件的代码,但是如果您这样做了,那么您也可以检查启动脚本,对吗?

系统提供的启动脚本需要位于可以包含可执行文件的位置,但我们不希望这些可执行文件出现在路径中。这些要求丢弃了bin和sbin,并将我们指向BSD系统上的libexec和Linux上的lib下的某个位置。

因此,只读启动脚本应该从/etc/rc.d/移到/libexec/rc.d/(顺便说一句,这也适用于/etc/rc、/etc/rc.subr和/etc/rc.shutdown)。就是这样。/etc/rc/libexec/rc应该继续使用rcorder(8)检查需要运行什么,但是它应该从/libexec/rc.d/读取文件。您甚至可能希望为用户创建的服务支持一个单独的位置,该位置可能仍然是/etc/rc.d/,或者更合适的位置,比如/usr/pkg/libexec/rc.d/(不过,如果您有多个文件系统,这很快就会遇到问题)。

使用这种设计,系统升级将更加合理,因为配置合并过程将纯粹关注实际的配置更改,而不是不相关的代码更改。对/libexec/rc.d/的所有更新都将通过解压缩新的分发集(在本例中为base.txz)来应用,而不会打扰您了解它们到底是如何更改的。

这与Linux发行版有关系吗?我在开头简要提到了/etc/init.d/,那里的问题也是类似的。但是这也是一个过时的问题,因为Linux发行版现在已经迁移到systemd上了。也就是说,systemd仍然必须管理各个服务,并且,不管你喜欢与否,它已经正确地做到了这一点。如果我们查看systemd.unit(5)手册页,它描述了从何处加载单元,系统首先会查看各种/etc/和/run/目录,然后还会查看/usr/lib/systemd/system/(由包管理器正确控制的只读位置)-绝大多数脚本都位于后者内部。

我目前不再运行BSD系统,因此我实现这一目标的动机是低…。但是,如果这一切都是有意义的,并且是您想要追求的东西,请一定要这样做!我很乐意提供帮助,但我并不期待通过🙄所需的讨论。