rpm 系 linux 系统中 /etc/yum.repo.d/ 目录下的 .repo 文件中的 $releasever 到底等于多少?

rpm 系 linux 系统中 /etc/yum.repo.d/ 目录下的 .repo 文件中的 $releasever 到底等于多少?

结论

对于 8 来说,通过以下命令

#/usr/libexec/platform-python -c 'import dnf, json; db = dnf.dnf.Base(); print(json.dumps(db.conf.substitutions, indent=2))' {   "arch": "x86_64",   "basearch": "x86_64",   "releasever": "8" } 

对于 7 来说,通过以下命令

#python -c 'import yum, json; yb = yum.YumBase(); print json.dumps(yb.conf.yumvar, indent=2)' 

或者通过以下命令获取 releasever 的值,repo 为 @system 代表当前系统安装的包。

#yum provides system-release/(releasever/) anolis-release-8.2-15.an8.x86_64 : Anolis OS 8 release file Repo        : @System Matched from: Provide    : system-release(releasever) = 8  anolis-release-8.2-15.an8.x86_64 : Anolis OS 8 release file Repo        : BaseOS Matched from: Provide    : system-release(releasever) = 8 

例子:

以下取自 stackover

# CentOS 8: # --- [root@0928d3917e32 /]# /usr/libexec/platform-python -c 'import dnf, json; db = dnf.dnf.Base(); print(json.dumps(db.conf.substitutions, indent=2))' Failed to set locale, defaulting to C {   "arch": "x86_64",   "basearch": "x86_64",   "releasever": "8" } [root@0928d3917e32 /]#    # CentOS 7: # --- [root@c41adb7f40c2 /]# python -c 'import yum, json; yb = yum.YumBase(); print json.dumps(yb.conf.yumvar, indent=2)' Loaded plugins: fastestmirror, ovl {   "uuid": "cb5f5f60-d45c-4270-8c36-a4e64d2dece4",    "contentdir": "centos",    "basearch": "x86_64",    "infra": "container",    "releasever": "7",    "arch": "ia32e" } [root@c41adb7f40c2 /]#   # CentOS 6: # --- [root@bfd11c9a0880 /]# python -c 'import yum, json; yb = yum.YumBase(); print json.dumps(yb.conf.yumvar, indent=2)' Loaded plugins: fastestmirror, ovl {   "releasever": "6",    "basearch": "x86_64",    "arch": "ia32e",    "uuid": "3e0273f1-f5b6-481b-987c-b5f21dde4310",    "infra": "container" } [root@bfd11c9a0880 /]#  

原理

以 8 为例

/usr/libexec/platform-python -c 'import dnf, json; db = dnf.dnf.Base(); print(json.dumps(db.conf.substitutions, indent=2))'  {   "arch": "x86_64",   "basearch": "x86_64",   "releasever": "8" } 

主要是以下几行代码

import dnf, json db = dnf.dnf.Base() print(json.dumps(db.conf.substitutions, indent=2)) 

然后对源码进行粗略的分析

# /usr/lib/python3.6/site-packages/dnf/base.py class Base(object):      def __init__(self, conf=None):         # :api         self._closed = False         self._conf = conf or self._setup_default_conf()         self._goal = None     # ...     @staticmethod     def _setup_default_conf():         conf = dnf.conf.Conf()         subst = conf.substitutions         if 'releasever' not in subst:             subst['releasever'] = /                 dnf.rpm.detect_releasever(conf.installroot)         return conf 

主要是这行代码

subst['releasever'] = /                 dnf.rpm.detect_releasever(conf.installroot) 

首先 subst 是从 os.environ.items()varsdir=("/etc/yum/vars/", "/etc/dnf/vars/") 得来的,如果 releasever 这个键不在 subset 字典里面,他就会去执行下面的代码 dnf.rpm.detect_releasever(conf.installroot)
这个函数我在下面也贴出来了,它是会遍历以下元组,作为 distroverpkg 的值,通过该函数 ts.dbMatch('provides', distroverpkg) 去获取对应值并提取 ,个人感觉这个类似于 yum provides 这个命令,然后他是按照顺序搞的,所以如果第一个值能获得 $releasever ,就选用该值。

DISTROVERPKG=('system-release(releasever)', 'system-release',               'distribution-release(releasever)', 'distribution-release',               'redhat-release', 'suse-release', 'anolis-release') 

在我系统上测试,发现第一个 system-release(releasever) 就可以匹配到该值了,所以此时 $releasever 的值就是该值。

# yum provides system-release/(releasever/) anolis-release-8.2-15.an8.x86_64 : Anolis OS 8 release file Repo        : @System Matched from: Provide    : system-release(releasever) = 8  anolis-release-8.2-15.an8.x86_64 : Anolis OS 8 release file Repo        : BaseOS Matched from: Provide    : system-release(releasever) = 8    

以下是部分源码及路径

#/usr/lib/python3.6/site-packages/dnf/conf/__init__.py from dnf.conf.config import BaseConfig, MainConf, RepoConf  Conf = MainConf #/usr/lib/python3.6/site-packages/dnf/conf/config.py class MainConf(BaseConfig):     # :api     """Configuration option definitions for dnf.conf's [main] section."""     def __init__(self, section='main', parser=None):         self.substitutions = dnf.conf.substitutions.Substitutions()  #/usr/lib/python3.6/site-packages/dnf/conf/substitutions.py class Substitutions(dict):     # :api      def __init__(self):         super(Substitutions, self).__init__()         self._update_from_env()      def _update_from_env(self):         numericvars = ['DNF%d' % num for num in range(0, 10)]         for key, val in os.environ.items():             if ENVIRONMENT_VARS_RE.match(key):                 self[key[8:]] = val  # remove "DNF_VAR_" prefix             elif key in numericvars:                 self[key] = val      def update_from_etc(self, installroot, varsdir=("/etc/yum/vars/", "/etc/dnf/vars/")):         # :api          for vars_path in varsdir:             fsvars = []             try:                 dir_fsvars = os.path.join(installroot, vars_path.lstrip('/'))                 fsvars = os.listdir(dir_fsvars)             except OSError:                 continue             for fsvar in fsvars:                 filepath = os.path.join(dir_fsvars, fsvar)                 if os.path.isfile(filepath):                     try:                         with open(filepath) as fp:                             val = fp.readline()                         if val and val[-1] == '/n':                             val = val[:-1]                     except (OSError, IOError):                         continue                 self[fsvar] = val # /usr/lib/python3.6/site-packages/dnf/rpm/__init__.py def detect_releasever(installroot):     # :api     """Calculate the release version for the system."""      ts = transaction.initReadOnlyTransaction(root=installroot)     ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES | rpm._RPMVSF_NODIGESTS))     for distroverpkg in dnf.const.DISTROVERPKG:         if dnf.pycomp.PY3:             distroverpkg = bytes(distroverpkg, 'utf-8')         try:             idx = ts.dbMatch('provides', distroverpkg)         except (TypeError, rpm.error) as e:             raise dnf.exceptions.Error('Error: %s' % str(e))         if not len(idx):             continue         try:             hdr = next(idx)         except StopIteration:             msg = 'Error: rpmdb failed to list provides. Try: rpm --rebuilddb'             raise dnf.exceptions.Error(msg)         releasever = hdr['version']         try:             try:                 # header returns bytes -> look for bytes                 # it may fail because rpm returns a decoded string since 10 Apr 2019                 off = hdr[rpm.RPMTAG_PROVIDENAME].index(distroverpkg)             except ValueError:                 # header returns a string -> look for a string                 off = hdr[rpm.RPMTAG_PROVIDENAME].index(distroverpkg.decode("utf8"))             flag = hdr[rpm.RPMTAG_PROVIDEFLAGS][off]             ver = hdr[rpm.RPMTAG_PROVIDEVERSION][off]             if flag == rpm.RPMSENSE_EQUAL and ver:                 if hdr['name'] not in (distroverpkg, distroverpkg.decode("utf8")):                     # override the package version                     releasever = ver         except (ValueError, KeyError, IndexError):             pass # /usr/lib/python3.6/site-packages/dnf/const.py DISTROVERPKG=('system-release(releasever)', 'system-release',               'distribution-release(releasever)', 'distribution-release',               'redhat-release', 'suse-release', 'anolis-release') 

原理证明

既然经过一番探索,自然得验证下,以下以 Anolis OS 8 系统为例子。

anolis-release 这个包的代码分析。源码路径

anolis-release.spec 文件中可以看到,这个包会提供 system-release(releasever) = %{base_release_version}

%{base_release_version} 在文件定义中是 8,从 koji 的构建记录也可以看出,确实会产出 system-release(releasever) = 8

%define anolis_release 15  %define debug_package %{nil} %define product_family Anolis OS %define base_release_version 8 %define full_release_version 8.2 %define compat_release_version 8 %define beta Beta %define dist .an%{base_release_version}  Name:           anolis-release Version:        %{full_release_version} Release:        %{anolis_release}%{?dist} Summary:        %{product_family} %{base_release_version} release file Group:          System Environment/Base License:        MulanPSLv2 Obsoletes:      rawhide-release redhat-release-as redhat-release-es redhat-release-ws redhat-release-de comps rpmdb-redhat fedora-release redhat-release centos-release Provides:       redhat-release = %{full_release_version} Provides:       centos-release = %{full_release_version} Provides:       system-release = %{version}-%{release} Provides:       system-release(releasever) = %{base_release_version} 

该包在 koji 上的构建记录 http://8.131.87.1/koji/rpminfo?rpmID=161662

Provides  anolis-release = 8.2-13.an8 anolis-release(x86-64) = 8.2-13.an8 centos-release = 8.2 config(anolis-release) = 8.2-13.an8 redhat-release = 8.2 system-release = 8.2-13.an8 system-release(releasever) = 8 

结论

然后,这个 system-release(releasever) 并不会像 os-release 一样写进系统文件里(/etc/os-release),但是确实会在系统中 provides ,而 $releasever 会按照元组中定义的顺序选取值,最终选到了该值。

反推

既然这样我是不是可以修改这个值,让系统的 $releasever 显示别的值,答案是可以的。

我通过修改 spec 文件中的变量为 %define base_release_version 7

然后通过 mock 重新构建该包,安装到我的系统上,成功让 $releasever 这个变量变成了我想要的值 ( 如下 ),当然此时 yum 源可能会有问题,你可能需要修改 .repo文件中 $releasever 为原来的值,再次下载 yum 源里的 anolis-release 这个包,就可以恢复原来的值了。

# yum provides system-release/(releasever/) Failed to set locale, defaulting to C.UTF-8 Repository AppStream is listed more than once in the configuration Repository BaseOS is listed more than once in the configuration Repository Plus is listed more than once in the configuration Repository PowerTools is listed more than once in the configuration Last metadata expiration check: 0:01:02 ago on Sat Jun 18 10:42:33 2022. anolis-release-8.2-14.an8.x86_64 : Anolis OS 8 release file Repo        : @System Matched from: Provide    : system-release(releasever) = 7  

mock 构建命令如下

rpmbuild -bs ~/rpmbuild/SPECS/*.spec --define "dist ${dist}" mock -r xx.cfg clean mock -r xx.cfg init mock -r xx.cfg rebuild xx.src.rpm 

通过 rpm -Uvh 安装构建出的 rpm 包

结语

想搞懂这个一个是对这方面比较好奇,而网上有很多误导,比如网上很多说是看 /etc/os-release 的值,所以想自己去找找真实的答案;另一方面工作中遇到了这方面的问题,故某天熬到2点多,仔细探查了一番。

有可能,我的分析也不对,大家参考就行。✌️

商匡云商
Logo
注册新帐户
对比商品
  • 合计 (0)
对比
0
购物车