RedHat 系列包管理

Table of Contents

1 RPM

RPM 起初是 RedHat 系统的包管理工具,目前已有多个系统支持RPM包(如 SUSE)。

RPM 由以下几个部分组成:

  • RPM 包,开发者将软件的文件打包,并分发出来供用户安装的单文件,扩展名是 .rpm;
  • RPM Database,RPM 包文件包含了丰富的信息(如依赖关系),这些包安装到系统里以后,系统会把相关信息记录到本地的 RPM Database 中,RPM Database 位于 /var/lib/rpm:
$ file /var/lib/rpm/*
/var/lib/rpm/Basenames:            Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Conflictname:         Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/__db.001:             Applesoft BASIC program data, first line number 18
/var/lib/rpm/__db.002:             a.out little-endian 32-bit pure executable
/var/lib/rpm/__db.003:             a.out little-endian 32-bit pure executable not stripped
/var/lib/rpm/Dirnames:             Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Enhancename:          Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Filetriggername:      Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Group:                Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Installtid:           Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Name:                 Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Obsoletename:         Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Packages:             Berkeley DB (Hash, version 9, native byte-order)
/var/lib/rpm/Providename:          Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Recommendname:        Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Requirename:          Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Sha1header:           Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Sigmd5:               Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Suggestname:          Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Supplementname:       Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Transfiletriggername: Berkeley DB (Btree, version 9, native byte-order)
/var/lib/rpm/Triggername:          Berkeley DB (Btree, version 9, native byte-order)
  • rpm 相关命令,RPM Database 既然是数据库,就会有增查删改等操作,由 rpm 命令完成。rpm 命令本身比较复杂,也是本文的重点。

1.1 查看帮助

rpm 命令的参数选项太多,本人也尚未全部掌握,因此这里不会全部讲完,只讲重点。但是,即便是复杂也需要完整地把 man rpm(8) 看一遍,知道 rpm 命令能够做哪些工作,这样在使用时心里才有数。

man rpm(8) 内容有很好的结构,在“SYNOPSIS”一节就能看出 rpm 命令的职责:

QUERYING AND VERIFYING PACKAGES
INSTALLING, UPGRADING, AND REMOVING PACKAGES
MISCELLANEOUS

在实际使用中,需要看好参数选项和参数说明即可。

1.2 查询包

非常常用的参数就是 -q(–query),它负责对 RPM Database 进行查询,用法:

rpm {-q|--query} [select-options] [query-options]

不同的查询需求,对应不同的 select-options 和 query-options,需要具体查看 man。

1.2.1 列出系统中安装的包

命令:rpm -qa

也可以用通配符来匹配:

$ rpm -qa 'kernel*'
kernel-4.9.8-201.fc25.x86_64
kernel-devel-4.9.9-200.fc25.x86_64
kernel-devel-4.9.10-200.fc25.x86_64
kernel-debuginfo-common-x86_64-4.5.5-300.fc24.x86_64
kernel-modules-4.9.8-201.fc25.x86_64
kernel-4.9.9-200.fc25.x86_64
kernel-core-4.9.10-200.fc25.x86_64
kernel-4.9.10-200.fc25.x86_64
kernel-core-4.9.9-200.fc25.x86_64
kernel-debuginfo-common-x86_64-4.8.6-300.fc25.x86_64
kernel-modules-4.9.10-200.fc25.x86_64
kernel-modules-4.9.9-200.fc25.x86_64
kernel-headers-4.9.10-200.fc25.x86_64
kernel-core-4.9.8-201.fc25.x86_64

还可以结合管道做过滤:

$ rpm -qa | fgrep kernel
kernel-4.9.8-201.fc25.x86_64
kernel-devel-4.9.9-200.fc25.x86_64
kernel-devel-4.9.10-200.fc25.x86_64
kernel-debuginfo-common-x86_64-4.5.5-300.fc24.x86_64
kernel-modules-4.9.8-201.fc25.x86_64
kernel-4.9.9-200.fc25.x86_64
kernel-core-4.9.10-200.fc25.x86_64
texlive-l3kernel-svn41246-30.fc25.noarch
kernel-4.9.10-200.fc25.x86_64
kernel-core-4.9.9-200.fc25.x86_64
kernel-debuginfo-common-x86_64-4.8.6-300.fc25.x86_64
kernel-modules-4.9.10-200.fc25.x86_64
kernel-modules-4.9.9-200.fc25.x86_64
kernel-headers-4.9.10-200.fc25.x86_64
kernel-core-4.9.8-201.fc25.x86_64

上面两个示例列出了系统安装的内核相关包,想知道哪个内核包是最近安装的、哪个时辰安装的,可以带上 –last 参数:

$ rpm -qa 'kernel*' --last
kernel-devel-4.9.10-200.fc25.x86_64           2017年02月21日 星期二 22时05分12秒
kernel-headers-4.9.10-200.fc25.x86_64         2017年02月21日 星期二 22时04分54秒
kernel-4.9.10-200.fc25.x86_64                 2017年02月21日 星期二 22时04分42秒
kernel-modules-4.9.10-200.fc25.x86_64         2017年02月21日 星期二 22时04分25秒
kernel-core-4.9.10-200.fc25.x86_64            2017年02月21日 星期二 22时04分22秒
kernel-devel-4.9.9-200.fc25.x86_64            2017年02月17日 星期五 16时17分42秒
kernel-modules-4.9.9-200.fc25.x86_64          2017年02月17日 星期五 15时21分04秒
kernel-core-4.9.9-200.fc25.x86_64             2017年02月17日 星期五 15时20分00秒
kernel-4.9.9-200.fc25.x86_64                  2017年02月17日 星期五 15时15分46秒
kernel-4.9.8-201.fc25.x86_64                  2017年02月10日 星期五 13时54分42秒
kernel-modules-4.9.8-201.fc25.x86_64          2017年02月10日 星期五 13时54分32秒
kernel-core-4.9.8-201.fc25.x86_64             2017年02月10日 星期五 13时54分30秒
kernel-debuginfo-common-x86_64-4.8.6-300.fc25.x86_64 2016年11月23日 星期三 12时33分03秒
kernel-debuginfo-common-x86_64-4.5.5-300.fc24.x86_64 2016年06月26日 星期日 16时14分42秒

1.2.2 查询包信息

查询某个包是否安装,命令用法如下:

rpm -q [包名]

如果没有打印出内容,说明该包尚未安装到系统中。

例,在编译驱动时,先查看是否安装内核源码的头文件:

$ rpm -q kernel-headers
kernel-headers-4.9.10-200.fc25.x86_64

例,查看 kernel-headers 的具体信息:

$ rpm -q --info kernel-headers
Name        : kernel-headers
Version     : 4.9.10
Release     : 200.fc25
Architecture: x86_64
Install Date: 2017年02月21日 星期二 22时04分54秒
Group       : Development/System
Size        : 3951044
License     : GPLv2 and Redistributable, no modification permitted
Signature   : RSA/SHA256, 2017年02月16日 星期四 21时29分59秒, Key ID 4089d8f2fdb19c98
Source RPM  : kernel-4.9.10-200.fc25.src.rpm
Build Date  : 2017年02月16日 星期四 08时10分58秒
Build Host  : bkernel01.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager    : Fedora Project
Vendor      : Fedora Project
URL         : http://www.kernel.org/
Summary     : Header files for the Linux kernel for use by glibc
Description :
Kernel-headers includes the C header files that specify the interface
between the Linux kernel and userspace libraries and programs.  The
header files define structures and constants that are needed for
building most standard programs and are also needed for rebuilding the
glibc package.

例,查看某个文件所属包:

rpm -qf `which ifconfig`
net-tools-2.0-0.40.20160329git.fc25.x86_6

例,列出包中包含哪些文件:

$ rpm -ql glibc
/etc/gai.conf
/etc/ld.so.cache
/etc/ld.so.conf
/etc/ld.so.conf.d
/etc/nsswitch.conf
/etc/rpc
/lib64/ld-2.24.so
/lib64/ld-linux-x86-64.so.2
/lib64/libBrokenLocale-2.24.so
...

例,列出包中的配置文件清单:

$ rpm -qc bash
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc

例,查询包安装和卸载时运行的脚本代码,这个非常有用,可以看到包安装之后额外做了哪些操作:

$ rpm -q --scripts bash
postinstall scriptlet (using <lua>):
nl        = '\n'
sh        = '/bin/sh'..nl
bash      = '/bin/bash'..nl
f = io.open('/etc/shells', 'a+')
if f then
  local shells = nl..f:read('*all')..nl
  if not shells:find(nl..sh) then f:write(sh) end
  if not shells:find(nl..bash) then f:write(bash) end
  f:close()
end
postuninstall scriptlet (using <lua>):
-- Run it only if we are uninstalling
if arg[2] == "0"
then
  t={}
  for line in io.lines("/etc/shells")
  do
    if line ~= "/bin/bash" and line ~= "/bin/sh"
    then
      table.insert(t,line)
    end
  end

  f = io.open("/etc/shells", "w+")
  for n,line in pairs(t)
  do
    f:write(line.."\n")
  end
  f:close()
end

例,查询包的依赖关系:

$ rpm -q --requires bash
/bin/sh
config(bash) = 4.3.43-4.fc25
filesystem >= 3
libc.so.6()(64bit)
libc.so.6(GLIBC_2.11)(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.15)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_2.8)(64bit)
libdl.so.2()(64bit)
libdl.so.2(GLIBC_2.2.5)(64bit)
libtinfo.so.6()(64bit)
rpmlib(BuiltinLuaScripts) <= 4.2.2-1
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(PayloadIsXz) <= 5.2-1
rtld(GNU_HASH)

1.3 安装、更新和卸载

1.3.1 安装包

用法如下:

rpm {-i|--install} [install-options] PACKAGE_FILE ...

例,安装一个包:

$ sudo rpm -ivh emacs-25.1-3.fc25.x86_64.rpm

参数 v 和参数 h 分别表示显示安装详情和进度。

例,忽略依赖关系安装:

$ sudo rpm -ivh --nodeps emacs-25.1-3.fc25.x86_64.rpm

1.3.2 升级包

用法如下:

rpm {-U|--upgrade} [install-options] PACKAGE_FILE ...

例,更新一个包(卸载旧版本、安装新版本):

$ sudo rpm -Uvh git-2.9.3-2.fc25.x86_64.rpm

1.3.3 卸载包

用法如下:

rpm {-e|--erase} [--allmatches] [--nodeps] [--noscripts] [--notriggers] [--test] PACKAGE_NAME ...

例,卸载一个软件包:

$ sudo rpm -e git

如果软件包有依赖关系是无法这样卸载的。带上 –nodeps 参数,将不会删除相关的依赖,而只删除包本身。比如我在 Fedora 上自己编译了内核,并打成 RPM 包,结果在卸载时要提示我卸载很多系统关键依赖,所以就得带上 –nodeps 参数。

1.4 验证包

RPM 不仅提供增删改能力,还提供了验证机制,对已安装的包进行验证,知道每个文件变动情况。

例,bash 包提供了多个配置文件,其中 /etc/skel/.bashrc 被我误删了,RPM 的验证机制会发现这一变化:

$ rpm -V bash
遗漏   c /etc/skel/.bashrc

1.4.1 效验属性

如下,验证 python-pip3 包的输出结果:

$ rpm -V python3-pip | head -20
.......T.    /usr/bin/pip3
.......T.    /usr/bin/pip3.5
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/DESCRIPTION.rst
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/INSTALLER
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/METADATA
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/RECORD
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/WHEEL
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/entry_points.txt
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/metadata.json
遗漏     /usr/lib/python3.5/site-packages/pip-8.1.2.dist-info/top_level.txt
S.5....T.    /usr/lib/python3.5/site-packages/pip/__init__.py
.......T.    /usr/lib/python3.5/site-packages/pip/__main__.py
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/__init__.cpython-35.pyc
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/__main__.cpython-35.pyc
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/basecommand.cpython-35.pyc
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/baseparser.cpython-35.pyc
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/cmdoptions.cpython-35.pyc
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/download.cpython-35.pyc
S.5....T.    /usr/lib/python3.5/site-packages/pip/__pycache__/exceptions.cpython-35.pyc

可见第一列有大量类似“S.5….T.”的输出,这表示验证的属性。根据 man rpm(8)描述,每位对应的属性如下:

S file Size differs
M Mode differs (includes permissions and file type)
5 digest (formerly MD5 sum) differs
D Device major/minor number mismatch
L readLink(2) path mismatch
U User ownership differs
G Group ownership differs
T mTime differs
P caPabilities differ

如果对应的属性没有问题,该位置以“.”表示。“S.5….T.”表示对应的文件大小(S)、文件 MD5(5)和最后修改时间(T)不对。

一般看到大量这样的改变,可能的原因要么是手动安装过对应的软件,要么是通过其他途径升级过软件,或者是出了安全事故。上例的情况就是我用 pip3 命令做了自身升级。

例,对系统中所有包进行全面验证:

$ rpm -Va

1.5 RPM Database 管理

1.5.1 重新构造 rpm 数据库

例,当我运行 dnf 命令时,提示:

错误:rpmdb: BDB0113 Thread/process 29848/140606613985024 failed: BDB1507 Thread died in Berkeley DB library
错误:db5 错误(-30973) 源自 dbenv->failchk: BDB0087 DB_RUNRECOVERY: Fatal error, run database recovery
错误:cannot open Packages index using db5 -  (-30973)
错误:无法从 /var/lib/rpm 打开软件包数据库
错误:Error: rpmdb open failed

说明 RPM 的数据库损坏,这时需要重建数据。

1、先备份 /var/lib/rpm,拷贝一份到其他目录,防止彻底丢失;

2、验证存软件包的库是否有问题:dbverify /var/lib/rpm/Packages;

3、重建:rpm –rebuilddb

4、dnf clean all,再dnf update

1.6 其他

1.6.1 deb 包转 rpm 包

系统需要安装 alien。然执行:

alien -r xxx.deb

-r 参数表示转换成 rpm 包。

1.6.2 解压 RPM 包

RPM 实际上使用的 CPIO 格式打包的,用 rpm2cpio 命令转成 CPIO 格式即可:

rpm2cpio chkconfig-1.4-1.fc22.src.rpm | cpio -div

或者转成 gzip 格式解压:

$ rpm2archive git-2.9.3-2.fc25.x86_64.rpm
$ file git-2.9.3-2.fc25.x86_64.rpm.tgz
git-2.9.3-2.fc25.x86_64.rpm.tgz: gzip compressed data, last modified: Wed Feb 22 14:16:00 2017, from Unix

1.6.3 关于 rpm 分发签名

  1. 每个发行版的包都经过签名;
  2. 客户端需要导入公钥;
  3. 客户端在安装官方源时,需要经过签名验证,以防止所安装的包被篡改过。

1.7 制作 RPM 包

一个软件包(package)包含以下信息:

  • 包的基本信息,如软件名称、简介、作者、许可协议等;
  • 依赖关系;
  • 安装步骤;
  • 运行的文件、所需配置文件;

以上全部过程和信息定义在spec文件中。

1.7.1 准备工作

先创建好 rpmbuild 规范定义的目录,然后把源码等文件放到适当的位置,再创建 spec 文件。

1.7.2 目录定义

早期的 RPM(<= 4.4)包只能由管理员创建,因为相关打包操作都在 /usr/src/redhat 目录中进行;现普通用户也可以打包,相关文件放在 home 目录的 rpmbuild 目录中。

把目录定义在哪儿、要定义哪些目录,可用以下命令来查看相关配置:

rpmbuild --showrc | fgrep _topdir

如下:

-14: _builddir	%{_topdir}/BUILD
-14: _buildrootdir	%{_topdir}/BUILDROOT
-14: _rpmdir	%{_topdir}/RPMS
-14: _sourcedir	%{_topdir}/SOURCES
-14: _specdir	%{_topdir}/SPECS
-14: _srcrpmdir	%{_topdir}/SRPMS

依照规范,需要定义以下目录:

  • BUILD:编译过程中暂存空间
  • BUILDROOT
  • RPMS:编译产生的二进制RPM位置;
  • SOURCES:存放源码;
  • SPECS:存放spec文件;
  • SRPMS:构造的源RPM。

在 home 下建立 rpmbuild 目录,然后依次建立以上目录。如果安装了 rpmdevtools,可用 rpmdev-setuptree 命令完成环境初始化。

1.7.3 编写 spec 文件

这里以打包 wget 为例,从http://ftp.gnu.org/gnu/wget/ 下载源码压缩包,并把源码压缩包放至 SOURCES 目录中。

编辑 SPECS/wget-1.19.spec:

Summary: 		GNU wget
License: 		GPL
Name: 			wget
Version: 		1.19
Release: 		1
Source: 		%{name}-%{version}.tar.xz
Group: 			Development/Tools

%description
The GNU wget program downloads files from the Internet using the command-line.

%prep
# 编译之前运行这段定义
# %setup -q 是解压 tarball 文件
%setup -q

%build
./configure
make

%install
# 不要在 %build 的 make 中写 --prefix 参数,因为安装阶段只是在一个临时目录,否则可能运行时发生问题
make install DESTDIR=$RPM_BUILD_ROOT

%files
# 列出要打包的文件
%defattr(-,root,root)
/usr/local

# 这里还可以用 %config 指定配置文件,这样不会在安装时覆盖掉存在的配置文件
# 用 %doc 指定帮助文档,在使用 --excludedocs,这部分会被跳过

有一些宏是系统定义好了的,位于 /usr/lib/rpm/macros,不要去修改这个文件,如果要改变某些全局宏,就写在 home 目录下的 .rpmmacros 中。

1.7.4 spec其他参数参考

参数 含义
Prefix 安装路径
BuildRequires build 过程中需要的包
Requires 安装过程需要的包

几个段说明:

段名 含义
%pre 安装前运行
%post 安装后运行
%preun 卸载包之前运行
%postun 卸载包之后运行

1.7.5 打包

使用 rpmbuild 命令:

rpmbuild -bb SPECS/wget-1.19.spec

-bb 表示打包二进制包。 用 -bs 可以打包源码包。

rpmbuild 会根据 spec 的定义依次运行:

1、%prep 段,将源码解压到临时目录中;

2、运行 %build 段,编译源码;

3、运行 %install 段,把编译后的源码安装到某个目录中;

4、根据 %files 段中定义的文件,打包 RPM 包。

1.7.6 额外资料

2 DNF/Yum

从 Fedora 22 开始,DNF 正式取代 Yum 成为系统默认的包管理器。相比 Yum 而言,DNF 用 Python 重写,同时支持 Python2 和 Python3,更干净代码维护起来比 Yum 省心多了;同时 DNF 也更换了依赖解决的算法。

2.1 搜索包

dnf search python-pip

2.2 安装包

sudo dnf install python-pip

2.2.1 安装编译依赖

对应 yum 的 yum-builddep。需要手动编译软件时自动安装所需的依赖:

sudo dnf builddep 包名

2.3 卸载包

sudo dnf remove python-pip

2.4 清理系统中无用的包

类似 apt-get autoremove:

sudo dnf autoremove

2.5 更新包

升级一个包:

sudo dnf upgrade python-pip

或更新系统中所有包:

sudo dnf upgrade python-pip

2.6 列出包

列出已安装的包:

dnf list installed <可选:包名/关键字>

列出可安装的包:

dnf list available <可选:包名/关键字>

2.7 查找包

查找命令属于哪个软件包

需要提供命令的绝对路径。一般我需要下载某个命令的源码时,就需要先知道它属于哪个包:

dnf provides /bin/bash

也可以使用 rpm 命令:

rpm -qf /bin/bash

查找哪个包包含了指定文件

如果编译某个软件、运行某个命令,系统找不到时,可以用“*”代替上层路径搜索:

dnf provides '*/magic.h'

列出 yum 仓库中某个包的文件列表

例,想用 pip3 安装一个包,系统找不到 pip3 命令,而用 dnf 安装 python3-pip 时提示已安装。用以下命令即可列出 python3-pip 包有哪些文件:

dnf repoquery -l python3-pip

2.8 下载包

dnf download python
dnf download --source python # 加 --source 表示下载源码

# 老系统中用的 yum-utils 的命令
yumdownloader python
yumdownloader --source python

给定的参数是包的名字,如果要下载某个命令的源码,需要先知道命令所属的包。以 alternatives 命令为例:

1、得到命令具体位置:

whereis alternatives

2、查询命令所属的包:

dnf provides /usr/sbin/alternatives

或:

rpm -qf /usr/sbin/alternatives

3、下载

dnf download chkconfig

2.9 源管理

禁用仓库:

yum-config-manager --disable '*SaltStack*'

2.10 Copr

Copr(Cool Other Package Repositories),类似 Ubuntu 的 PPA。

第三方软件可以通过https://copr.fedorainfracloud.org搜索,下面以安装 PyCharm 社区版为例:

sudo dnf copr enable phracek/PyCharm # 启用 PyCharm 的仓库
sudo dnf search pycharm # 搜索 PyCharm 包
sudo dnf install pycharm-comunity # 安装 PyCharm 社区版

可以用 list 子命令列出当前安装的仓库:

$ sudo dnf copr list
copr.fedorainfracloud.org/phracek/PyCharm

通过 disable 禁用仓库,或使用 remove 删除仓库:

sudo dnf copr disable phracek/PyCharm
sudo dnf copr remove phracek/PyCharm

2.11 DNF 设置

DNF 默认配置文件是 /etc/dnf/dnf.conf;yum 的是 /etc/yum.conf。

设置 socket 代理:

proxy=socks5://[host]:[port]

排除对某个包的升级,如设置不升级 synergy:

exclude=synergy

2.12 查看 DNF 日志

dnf history
dnf history [包名] # 查看某个包的操作历史

或直接看 /var/log/dnf.log。

2.13 更多参考

  • DNF官方文档
  • man dnf
  • man dnf.plugin.插件名,查看某个 DNF 插件详细帮助