6

Dpkg Conffiles | Blog·Tanky Woo

 2 years ago
source link: https://blog.tankywoo.com/2021/01/01/dpkg-conffiles.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Dpkg Conffiles

#Linux #Ubuntu ] [ January 1, 2021 ]

最近通过 dpkg 安装升级某 deb 包,发现旧版本在 /etc/ 下的一个文件被误删了,但是新版本也未将其安装或恢复,于是研究了打包中的 conffiles

首先看看官方的介绍

One of the most annoying things about software is when you spend a great deal of time and effort customizing a program, only to have an upgrade stomp all over your changes. Debian solves this problem by marking such configuration files as conffiles. [55] When you upgrade a package, you’ll be asked whether you want to keep your old configuration files or not.

dh_installdeb(1) automatically flags any files under the /etc directory as conffiles, so if your program only has conffiles there you do not need to specify them in this file. For most package types, the only place conffiles should ever be is under /etc, and so this file doesn’t need to exist.

If your program uses configuration files but also rewrites them on its own, it’s best not to make them conffiles because dpkg will then prompt users to verify the changes all the time.

conffiles 的作用就是在软件包升级时,不同于其他文件只需要简单的暴力覆盖即可,放置于 /etc/ 下的(配置)文件需要需要特殊考虑,是保留旧配置还是使用新配置,所以有了这个特殊的行为。

经常会遇到修改后的配置文件,在软件包升级时,出现如下询问窗口:

Configuration file '/etc/default/nginx'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** nginx (Y/I/N/O/D/Z) [default=N] ?

通过如 nginx deb 包解压,可以看到有 <DEBIAN>/conffiles 文件,里面存放的都是要放到 /etc 下的文件:

apt download nginx-common
ar xv nginx-common_1.14.0-0ubuntu1.7_all.deb
tar Jxvf control.tar.xz
cat conffiles

dpkg 对于 conffiles 更新有强制执行的选项:

--force-<things>
--no-force-<things>, --refuse-<things>

   confmiss: Always install the missing conffile without prompting. This is dangerous, since it means not preserving a change (removing) made to the file.

   confnew: If a conffile has been modified and the version in the package did change, always install the new version without prompting, unless the `--force-confdef` is also specified, in which case the default action is preferred.

   confold: If a conffile has been modified and the version in the package did change, always keep the old version without prompting, unless the `--force-confdef` is also specified, in which case the default action is preferred.

   confdef: If a conffile has been modified and the version in the package did change, always choose the default action without prompting. If there is no default action it will stop to ask the user unless `--force-confnew` or `--force-confold` is also been given, in which case it will use that to decide the final action.

   confask: If a conffile has been modified always offer to replace it with the version in the package, even if the version in the package did not change (since dpkg 1.15.8). If any of `--force-confnew`, `--force-confold`, or `--force-confdef` is also given, it will be used to decide the final action.

说实话,不确定是不是我理解能力不行,这段写的太有歧义了,导致产生了几个疑问:

  • 里面的一些 it 都无法确定到底指谁?
  • default action 从哪里可以看到?
  • confold/confnew 时提到同时有 confdef 时,使用默认的行为;但是 confdef 又写到如果没有 default action 时,同时有 confnew/confold 会考虑作为最终的行为。 感觉逻辑写的有问题

反复读了很多遍,最终还是看源码 dpkg/src/configure.c 理解了这块的逻辑。

首先,这里面的 a conffile 指的是 conffiles 文件中维护的 /etc 下的任一文件,在 dpkg 安装 deb 包时(apt/aptitute 同理),涉及文件的三个 hash 值(这里加个简称):

  • hash_real: 某 conffile 文件的实际 hash
  • hash_old: 某 conffile 在旧版本安装时维护的 hash(维护在 /var/lib/dpkg/status 文件中,可通过命令 dpkg --status <package> 查看指定包的 conffiles)
  • hash_new: 某 conffile 在即将安装的新版本中的 hash

大致的逻辑就是:

  • 首先对比 hash_old 和 hash_new,如果相同,则保持原样(这个就是开始我遇到的问题,某个被误删掉的 conffile,在新旧版本未变更,所以保持原样,也就是继续不存在……)
  • 如果 conffile 不存在且配置了 --force-confmiss,则会将文件重新安装上(这个就是上面遇到问题的解决方法)
  • 判断 hash_real 和 hash_old 是否一致,不一致则标识为 useredited 状态,即用户修改了 conffile
  • 判断 hash_real 和 hash_new 是否一致,不一致则标识为 distedited 状态,即软件包升级会修改 conffile
  • 然后进入 prompt 环节,也就是依据这些状态和 dpkg 的选项判断是否交互式询问是否覆盖或直接变更
  • 如果用户未修改 conffile,且软件包升级变更此文件,则任何 --force-<things> 选项都无用,会自动更新 conffile
  • 如果用户修改了 conffile,且软件包升级不会变更此文件,则可以通过 --force-confask 进入提示是使用旧配置还是新配置
  • 如果用户修改了 conffile,且软件包升级变更此文件,则默认是 confask 的行为,此时也可以通过 confold/confnew/confdef 来取消询问的步骤

基于上面的疑问,dpkg 源码实现(版本 1.19.0.5)对默认的行为是保留原配置,并没有看到可以修改 default action 的配置,且如果同时设置 confdefconfnew/confold,以 confdef 为主。

之前 Google 搜索 “dpkg conffiles” 出来靠前的一篇文章 提到了同时配置 --force-confdef--force-confold,包括 StackOverflow 上有个回答也是这么说,按对方意思是用户未修改文件按 confdef 行为升级,有修改的文件按 confold 行为升级(保留),可能这个是 dpkg 之前版本的行为,目前来看这个说法是有问题的。

简单而言,一是非配置文件,不要放 /etc 下,防止升级引发的遗漏问题;二是 /etc 下配置文件,如果做了变更(包括删除),那么不论是升级还是恢复,这个就交给用户自己了,除非指定了上面提到的强制选项。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK