文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>构造Linux的图形化安装程序(4)

构造Linux的图形化安装程序(4)

时间:2007-02-17  来源:PHP爱好者

   rpm包管理和安装盘定制 

   本文是构造Linux的图形化安装程序系列文章的第四部分,内容主要包括RPM基本命令介绍,RPM包的定制过程,RPM SPEC文件的主要内容,RPM函数库简单参考和安装程序中关于RPM包管理部分源码的简单介绍。通过这部分的介绍,希望读者能对Linux系统下RPM包的定制过程和RPM包的系统安装过程有一个基本的了解。
   在安装程序进行了磁盘分区工作之后,安装程序就可以进行RPM系统包的安装了。这是整个安装过程中很重要的一步。在这一环节,安装程序要读出所有rpm包的描述信息并根据包之间的依赖关系,构造出正确的包安装顺序。这种构造的机制主要是对包依赖关系的树形结构进行深度搜索,对于最基本的系统包(比如Glibc和Bash)一定要最先安装。为了保证在安装了所有的系统包之后,RPM数据库运行良好,还要在安装过程中构造正确的RPM数据库。最后为了调试的方便,也便于用户检查安装的系统包,还需要对包的安装过程建立日志。

   1 RPM包的基本概念
   RPM(Redhat Package Management)是由RedHat开发的,Linux系统下的系统包管理工具。它的目标是:使包的安装和卸载过程更容易,能够证实一个包是否已经正确安装了,简化包的建立过程,可以从源代码建立整个包,使它能用于不同的体系结构。RPM系统已经成为现在Linux系统下包管理工具事实上的标准,并且它也移植到很多商业的unix系统之下。

   RPM包由包标签标识,它包含这样几个部分,软件名,软件版本,包的发行版本。在包的内部还包含如下信息:包的建立时间,包的内容描述,安装包的所有文件的大小,数字签名以证实包的完整性。RMP包还包含包内的文件信息,其中包括:每个文件的文件名,每个文件的权限,文件的属组和拥有者,每个文件的md5校验和,文件的内容。

   RPM的包管理系统提供了下列功能:安装新的包,除去旧的包,将一个旧包升级为新的包,获得已经安装包的信息。

   常用的RPM命令:

   rpm -i
   使用此命令可以安装一个rpm包。在安装的过程中,此命令依次要进行包依赖性检测,包冲突检测,完成安装前必须执行的任务,处理相应的配置文件,解开包中的文件并将其拷贝到正确的位置,完成安装后必须执行的任务,对包进行的处理进行跟踪记录。
   例如:

   rpm -i bzip2-1.0.1-3.i586.rpm
   //安装bzip2包。

   rpm -ivh bzip2-1.0.1-3.i586.rpm
   //安装bzip2包的同时,显示更多的文本提示信息,以及在屏幕上显示连续的#号来表示的安装进度。

   有时在安装一个新包时,根据依赖性检查的结果,需要首先安装其他的包。但可能这时系统中并没有安装所需要包的合适版本,这样rpm会终止包的安装。为了直接安装这个包,您需要加入--nodeps选项。下例表示在安装bzip2包时,不进行依赖性检测。
   rpm -ivh bzip2-1.0.1-3.i586.rpm --nodeps

   rpm -ivh bzip2-1.0.1-3.i586.rpm --force
   //强制安装rpm包。这条命令实际上等价于
   rpm -ivh bzip2-1.0.1-3.i586.rpm --replacepkgs --replacefiles

   rpm -e
   使用此命令可以删除一个rpm包。删除rpm包时,此命令要完成如下工作:

   检查rpm数据库确保没有其他包依赖将要删除的包。
   如果包存在卸载前脚本,执行此脚本。
   检测安装包时是否对包配置文件进行了修改。如果进行了修改,则保存备份。
   查找rpm数据库中此RPM包所包含的文件。如果这些文件不属于任何其它的包,则删除它。
   如果包存在卸载后脚本,执行此脚本。
   从rpm数据库除去所有包跟踪记录。
   例如:
   rpm -e bzip2
   //从系统中除去bzip2包。添加--nodeps选项可以在删除包时,禁止包的依赖性检查。

   rpm -U
   这条命令完成rpm包的升级。它执行的操作包括安装合意的包,删除所有存在的老版本的包。例如:
   rpm -U bzip2
   //升级包bzip2。

   rpm -q
   这条命令可以获得rpm包的信息。通过这条命令可以查询包的文件列表,包的版本,包的描述性信息。同样的,你也可以通过这条命令查得一个文件属于哪个rpm包。例如:

   rpm -qf `which fdisk`
   //检查fdisk文件属于哪个系统包。

   rpm -qi bzip2
   //获得已安装包bzip2的描述性信息。

   rpm -ql bzip2
   //获得安装包bzip2的文件列表。

   rpm -qa
   //获得系统安装的所有rpm包的列表。这条命令和grep命令一起使用,可以快速找到系统中包含的某个rpm包,例如:
   rpm -qa | grep bzip2

   2 RPM包建立过程
   为了完成RPM包的建立过程,需要执行以下步骤:

   执行Spec文件prep节的命令和宏。
   检查文件列表的内容。
   执行Spec文件build节的命令和宏。
   执行Spec文件install节的命令和宏,同时也执行文件列表中的宏。
   创建二进制包文件。
   创建源码包。

   为了执行打包的工作,RPM需要一系列目录完成建立的工作。正常的目录结构通常由一个顶级目录和五个子目录构成。这五个子目录分别是:

   SOURCES------包含原始的源文件、补丁和像标文件。
   SPECS--------包含控制建立过程的spec文件。
   BUILD--------包含源码解包和软件建立的目录。
   RPMS---------包含建立过程创建的二进制包文件。
   SRPMS--------包含建立过程创建的源码包文件。

   除了上述这五个主要的目录外,在RPMS或SRPMS目录下通常还会有关于包目标平台的目录。例如,i386、i586、i686等代表与Intel兼容cpu的平台,noarch目录下的包代表可以在任何平台下执行。

   2.1 SPEC文件
   Spec文件是整个RPM包建立过程的中心,它的作用就如同编译程序时的Makefile文件。Spec文件包含建立一个rpm包必需的信息,包括哪些文件是包的一部分以及它们安装在哪个目录下。这个文件一般分为如下的几节:

   Preamle(序言)
   序言包含用户请求包的信息时所显示的内容。它可以包含包的功能描述,包的软件版本,版权信息,所属的包组等。

   Prep节 Prep节进行实际的打包准备工作,它是使用节前缀%prep表示的。一般而言,这一节的主要工作是检查标签语法是否正确,删除旧的软件源程序,对包含源程序的tar文件进行解码。如果包含补丁(patch)文件,将补丁文件应用到解开的源码中。它一般包含%setup与%patch两个命令。%setup用于将软件包打开,执行%patch可将补丁文件加入解开的源程序中。

   %setup
   -n newdir---------将压缩的软件源程序在newdir目录下解开。
   -c ---------------在解开源程序之前先创建目录。
   -b num------------在包含多个源程序时,将第num个源程序解压缩。
   -T----------------不使用缺省的解压缩操作。

   例如:
   %setup -T -b 0
   //解开第一个源程序文件。

   %setup -c -n newdir
   //创建目录newdir,并在此目录之下解开源程序。

   %patch
   %patchN----------这里N是数字,表示使用第N个补丁文件,等价于%patch -P N
   -p0--------------指定使用第一个补丁文件,-p1指定使用第二个补丁文件。
   -s---------------在使用补丁时,不显示任何信息。
   -b name----------在加入补丁文件之前,将源文件名上加入name。若为指定此参数,则缺省源文件加入.orig。
   -T---------------将所有打补丁时产生的输出文件删除。

   Build节
   这一节主要用于编译源码,它是使用节前缀%build表示的。这一节一般由多个make命令组成。

   Install节
   这一节主要用于完成实际安装软件必须执行的命令,它是使用节前缀%install表示的。这一节一般是由make install指令构成,但是有时也会包含cp、mv、install等指令。

   这一节还能指定在用户安装的系统上,包安装时运行的脚本。这样的脚本称为安装(卸载)脚本。它可以指定包安装前、包安装后、包除去前、包除去后的系统必须运行的外壳程序段。在用户安装的系统上,为了验证一个包是否已经成功安装的验证脚本也可由这一节指定。

   Clean节
   这一节所描述的内容表示在完成包建立的工作之后,自动执行此节下的脚本进行附加的清除工作,它是使用节前缀%clean表示的。一般而言,这一节的内容是简单地使用rm -rf $RPM_BUILD_ROOT命令,不需要指定此节的其它内容。

   文件列表
   这一节指定构成包的文件的列表,它是使用节前缀%files表示的。此外,它还包含一系列宏控制安装后的文件属性和配置信息。

   改动日志
   这一节主要描述软件的开发记录,它是使用节前缀%changlog表示的。这个段的内容是为了开发人员能详细的了解该软件的开发过程,对于包的维护极有好处。

   2.2 建立rpm包
   有时您可能只有一个tar.gz格式的源程序包,为了生成正确的rpm包,您可以使用autospec自动创建spec文件。

   举例来说,您有一个源程序文件some.tar.gz。为了定制rpm包,您要进行如下操作:

   解压缩源程序包
   tar xvzf some.tar.gz

   手动编译和安装此源程序包
   make; make install

   自动生成spec文件
   make -n install | autospec -i > some.spec

   编译生成rpm包
   rpm -ba some.spec

   在创建spec文件之前,必须成功编译源程序包。否则autospec生成的spec文件将不会包含%build、%install、%file。对于一般的源程序包,您只需到SPEC目录下,直接执行上面操作的第四步就可以了。

   3 定制系统安装盘
   定制系统安装盘的过程主要是指对于用户提供的一组RPM包,安装程序能够根据用户提供的描述文件,自动进行包的安装。这个过程包括对包的依赖性进行检查、根据类别选择需要安装的包。

   使用HappyLinux发布盘上附带的RPM包管理工具可以定制系统发布盘,这些包管理工具保存在安装盘的/misc目录下。为了保证安装程序能够工作,必须在/HappyLinux/base目录下提供以下的文件:

   compss
   在HappyLinux系统下,它是由系统命令gendistrib自动生成的。它可以根据包所属的包组对包进行分类。compss描述发布盘上所有包的一个列表。这个文件对于安装过程没有影响,它的存在只是为了与以前的版本兼容。

   compssUsers
   此文件的内容是对rpmsrate文件所描述的包类别进行进一步的包装,以形成更高层的包类表述。

   depslist
   depslist.ordered
   这两个文件是命令gendistrib生成的,它描述每个包所依赖的系统包。depslist.ordered使安装程序实际使用的文件,它将依赖包的文本描述转换为包的编号,这样可以加快整个安装的进程。

   happyinst_stage2.bz2
   这个文件保存的是整个安装环境(也就是安装盘上/HappyLinux/happyinst目录下的所有文件)的压缩镜像,它会在启动过程中由系统加载程序调入内存。这个文件是由命令make_happyinst_stage2生成的。

   hdlists
   这个文件是对安装盘rpm包所在的目录进行描述。当要进行多盘安装时,此文件的每一行表示一张发布介质的RPM包所在的目录。这个文件必须由用户手动生成。

   hdlist.cz2
   这是由命令gendistrib生成的,包头信息的描述文件。安装程序由此文件读出每个包的大小、版本等信息。它的名字是由hdlists文件的对应行表示的。

   provides
   这个文件也是命令gendistrib生成的,它将包冲突检测的信息保存到此文件中。

   rpmsrate
   这个文件是由用户自己定制的。它主要描述同类别包的编组。同时,对包组的文件指定了优先级,使得不同的场景可以自动安装不同的包组。这些包类别将要写入compssUsers文件中,构成更大的包逻辑分组。

   在发布盘上的任何包的发生改变时,为了使此改变生效,必须运行命令gendistrib,重新生成所有与包描述有关的文件。

   gendistrib --distrib /export
   根据目录/export下保存的所有包信息生成相应的描述文件。

   在对包的安装环境进行了任何修改之后,必须重新运行命令make_happyinst_stage2。

   make_happyinst_stage2 /export/HappyLinux/happyinst /export/HappyLinux/base/happyinst_stage2.bz2
   生成整个安装程序的压缩镜像。

   在进行了上述步骤之后,您就可以使用mkisofs创建iso文件,以生成正确的光盘发布了。

   4 rpmlib库函数
   rpmlib库函数是包含在librpm.a库中的。在c语言中,使用这些函数必须包含头文件rpmlib.h,对于与header有关的函数还需要包含头文件header.h。这些函数完成的功能包括从底层的rpm数据库记录遍历到高层的包处理。

   在安装程序中,使用rpmlib中的函数完成包安装的功能,具有以下的一些优点:

   大大减小了整个安装程序占用的内存。这样安装程序就无需提供rpm系统文件了。
   直接使用c语言的库函数提高了包的安装速度。
   通过安装回调函数,可以在包的安装过程中,实现一些特殊的效果,比如安装过程中更换显示图片,显示安装进度等。

   下面包含的函数只是rpmlib中与安装程序有关的部分函数接口。

   4.1 错误处理
   int rpmErrorCode(void);
   这个函数返回最后一个失败的rpmlib函数返回的错误码。此函数仅用于rpmErrorSetCallBack()定义的错误回调函数中。

   char *rpmErrorString(void);
   这个函数返回最后一个失败的rpmlib函数返回的错误串。此函数仅用于rpmErrorSetCallBack()定义的错误回调函数中。

   rpmErrorCallBackType rpmErrorSetCallback(rpmErrorCallBackType);
   这个函数设置当前错误回调函数,它返回以前设置的错误回调函数

   4.2 获得包信息
   下列函数用于获得包文件信息。返回的信息以Header结构的形式保存。
   int rpmReadPackageInfo(int fd, Header * signatures, Header * hdr);
   使用给定fd(表示对应的rpm包)读入头信息和标记信息。如果指定signatures或hdr,则其对应的信息不返回。

   int rpmReadPackageHeader(int fd, Header * hdr, int * isSource, int * major, int * minor);
   使用给定fd(表示对应的rpm包)读入头信息,以及此包文件是否是源码包,包的主、次版本号。

   4.3 RPM数据库处理
   这一节的函数完成RPM数据库的基本操作。这包括打开和关闭数据库,以及在数据库损坏时进行重建。每个存取RPM数据库的函数都要使用rpmdb结构,它是RPM数据库的句柄。

   int rpmdbOpen(char * root, rpmdb * dbp, int mode, int perms);
   此过程打开环境变量RPMVAR_DBPATH指定的RPM数据库,返回rpmdb结构。

   void rpmdbClose(rpmdb db);
   此过程关闭rpmdb结构指定的RPM数据库。

   int rpmdbInit(char * root, int perms);
   此过程在环境变量RPMVAR_DBPATH指定的位置创建一个新的RPM数据库。如果数据库存在,此函数不做任何操作。

   int rpmdbRebuild(char * root);
   此过程在环境变量RPMVAR_DBPATH处重建RPM数据库。

   4.4 RPM数据库遍历
   unsigned int rpmdbFirstRecNum(rpmdb db);
   此过程返回db指定的数据库第一个记录的记录编号。

   unsigned int rpmdbNextRecNum(rpmdb db, unsigned int lastOffset);
   此过程返回紧接着lastOffset的记录的编号。

   Header rpmdbGetRecord(rpmdb db, unsigned int offset);
   此过程返回指定偏移处的记录。

   4.5 RPM数据库查询
   这一节的函数查询RPM数据库,并且返回dbiIndexSet结构。在不使用此返回值的时候,使用dbiFreeIndexRecord()函数释放。

   typedef struct {
     dbiIndexRecord * recs;
     int count;
   } dbiIndexSet;
   typedef struct {
     unsigned int recOffset;
     unsigned int fileNumber;
   } dbiIndexRecord;

   recOffset是记录在数据库中的偏移,fileNumber仅用于rpmdbFindByFile()。

   int rpmdbFindByFile(rpmdb db, char * filespec, dbiIndexSet * matches);
   此过程搜索db指定的RPM数据库,找到拥有filespec指定文件的记录,并保存在matches中。

   int rpmdbFindByGroup(rpmdb db, char * group, dbiIndexSet * matches);
   此过程搜索db指定的RPM数据库,查找是group组指定的成员的包,返回值保存在matches中。

   int rpmdbFindPackage(rpmdb db, char * name, dbiIndexSet * matches);
   此过程搜索db指定的RPM数据库,查找包名是name的包,返回值保存在matches中。

   int rpmdbFindByProvides(rpmdb db, char * provides, dbiIndexSet * matches);
   此过程搜索db指定的RPM数据库,查找包的provides信息和指定的provides信息一致的包,返回值保存在matches中。

   int rpmdbFindByRequiredBy(rpmdb db, char * requires, dbiIndexSet * matches);
   此过程搜索db指定的RPM数据库,查找包的requires信息和指定的requires信息一致的包,返回值保存在matches中。

   int rpmdbFindByConflicts(rpmdb db, char * conflicts, dbiIndexSet * matches);
   此过程搜索db指定的RPM数据库,查找包的conflicts信息和指定的conflicts信息一致的包,返回值保存在matches中。

   4.6 包处理
   int rpmInstallSourcePackage(char * root, int fd, char ** specFile, rpmNotifyFunction notify, char * labelFormat);
   此函数安装fd指定的源码包。如果指定了root字段则将包安装到此目录下。notify指定安装过程中调用的进程跟踪过程。labelformat指定包标签的打印格式。

   int rpmInstallPackage(char * rootdir,rpmdb db, int fd, char * prefix, int flags, rpmNotifyFunction notify, char * labelFormat, char * netsharedPath);
   此过程安装db指定的二进制包,如果指定了rootdir,包安装到此目录下。flags参数控制包的安装行为。

   RPMINSTALL_REPLACEPKG------------即使包已经安装,仍继续安装。
   RPMINSTALL_REPLACEFILES----------即使会替换另外一个包的文件也安装此包。
   RPMINSTALL_TEST------------------只进行安装时检测,不安装包。
   RPMINSTALL_UPGRADE---------------安装包并除去包的老版本。
   RPMINSTALL_UPGRADETOOLD----------即使此包是老版本,仍进行安装。
   RPMINSTALL_NODOCS----------------不安装包的文档文件。
   RPMINSTALL_NOSCRIPTS-------------不执行包的安装和删除脚本。
   RPMINSTALL_NOARCH----------------不完成结构兼容性测试。
   RPMINSTALL_NOOS------------------不进行操作系统兼容性测试。

   Notify参数指定安装过程中系统调用的进度跟踪过程。回调过程的函数指针定义:
   typedef void (*rpmNotifyFunction)(const unsigned long amount, const unsigned long total);
   amount指定已经安装的字节数,total指定安装的总字节数。此过程可用于在安装过程中动态更新进程条等信息。
   LabelFormat指定包标签的格式。NetsharedPath参数指定和其它系统共享的本地文件系统部分。如果共享多个目录,路径要用冒号隔开。

   int rpmRemovePackage(char * root, rpmdb db, unsigned int offset, int flags);
   此过程除去在rpm数据库中offset位置处的包。root指定包所在的根目录,flags的值定义在rpmlib.h中,定义了如下标志:

   RPMUNINSTALL_TEST 进行删除检测,但是不除去任何包
   RPMUNINSTALL_NOSCRIPTS 不执行包删除脚本。

   4.7 与包依赖性相关的操作
   依赖性操作是完全和正常的基于包的操作隔离的。包的安装和除去过程自身并不完成依赖性处理。因此依赖性处理函数和其他rpmlib函数不同。依赖性处理的中心是rpmDependencies数据结构。这些函数只是随着操作的不同管理此数据结构,它们并不对包进行操作。在依赖性检测过程发现存在包依赖性冲突问题时,rpmDependencyConflict结构返回依赖性冲突。

   rpmDependencies rpmdepDependencies(rpmdb db);
   此过程返回初始化了的rpmDependencies结构。此时基于db参数指定的rpm数据库进行依赖性检测。

   void rpmdepAddPackage(rpmDependencies rpmdep, Header h);
   此过程加入头为h的包到rpmDependencies数据结构rpmdep中。

   void rpmdepUpgradePackage(rpmDependencies rpmdep, Header h);
   此过程加入头为h的包到rpmDependencies数据结构rpmdep中,同时此过程除去包依赖的老版本。

   void rpmdepRemovePackage(rpmDependencies rpmdep, int dboffset);
   此过程在rpmdep依赖结构中除去rpm数据库中偏移为dboffset的包。

   void rpmdepAvailablePackage(rpmDependencies rpmdep, Header h, void * key);
   此过程加入头为h的包到rpmDependencies数据结构rpmdep中。Key用于标识加入的包,这个参数会作为rpmdepCheck()函数返回的rpmDependencyConflict结构的一部分。

   int rpmdepCheck(rpmDependencies rpmdep, struct rpmDependencyConflict ** conflicts, int * numConflicts);
   此函数在rpmDependencies类型的结构变量rpmdep上进行依赖性检测。它返回一个numConflicts大小的数组,数组保存在conflicts指针中。

   void rpmdepFreeConflicts(struct rpmDependencyConflict * conflicts, int numConflicts);
   此函数释放conflicts指针指向的依赖性冲突结构。

   void rpmdepDone(rpmDependencies rpmdep);
   此函数释放rpmDependencies类型的结构变量rpmdep。

   4.8 头信息处理
   rpm头信息是为最小化的rpm数据库服务的,在这个数据库中可以进行特定信息的检索。
   Header headerRead(int fd, int magicp);
   此函数从文件fd中读入header并且转换网络字节序为主机系统字节序。如果magicp定义为HEADER_MAGIC_YES,此函数期望接收头幻数。如果magicp定义为HEADER_MAGIC_NO,此函数不包含头幻数。

   void headerWrite(int fd, Header h, int magicp);
   此函数将头h写入文件fd中并且转换主机系统字节序为网络字节序。如果magicp定义为HEADER_MAGIC_YES,此函数在写入的头信息中加入适当的幻数。如果magicp定义为HEADER_MAGIC_NO,此函数写入的信息不包含幻数。

   Header headerCopy(Header h);
   此函数返回头h的副本。

   unsigned int headerSizeof(Header h, int magicp);
   此函数返回头h占用的字节数。

   Header headerNew(void);
   此函数返回一个新的头。

   void headerFree(Header h);
   此函数释放h所指定的头信息。

   void headerDump(Header h, FILE *f, int flags);
   此函数打印头h结构到文件f中。若flags标志为HEADER_DUMP_INLINE,头数据也被打印。

   4.9 包头信息处理
   此节的这些函数提供处理头入口的基本操作,包含下列头入口类型:

   RPM_NULL_TYPE - 不使用此类型
   RPM_CHAR_TYPE - 此入口包含单一字符。
   RPM_INT8_TYPE - 此入口包含八位字符。
   RPM_INT16_TYPE - 此入口包含16位字符。
   RPM_INT32_TYPE - 此入口包含32位字符。
   RPM_INT64_TYPE - 此入口包含64位字符。
   RPM_STRING_TYPE - 此入口包含字符串类型。
   RPM_BIN_TYPE - 此入口包含rpmlib无法处理的二进制数据。
   RPM_STRING_ARRAY_TYPE - 此入口包含字符数组。

   int headerGetEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c);
   此过程从h中取出与tag标志匹配的入口。入口类型由type返回,数据指针由p返回,数据大小由c返回。

   int headerAddEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c);
   此函数向头h中加入新的入口, tag参数指定入口的标志,type指定入口的类型。P指定入口数据,c是此数据的大小。

   4.10 头重复器支持
   rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag, const void * key, size_t keylen);
   此函数返回新创建的重复器。

   Header rpmdbNextIterator(rpmdbMatchIterator iter);
   此函数获得下一个头信息。

   void rpmdbFreeIterator(rpmdbMatchIterator iter);
   此函数释放重复器iter占用的资源。

   5 RPM包管理过程实现
   在安装程序中与包管理有关的函数基本上都保存在文件pkgs.pm中。这个模块中包含的主要函数如下:

   getDeps
   读入文件depslist.ordered,通过它形成所有包所依赖rpm包的描述结构。

   getProvides
   读入所有rpm文件的头信息描述结构,通过provides字段形成包的Provides结构。

   init_db,rebuild_db_open_for_traversal,clean_old_rpm_db
   初始化(重建、清除)包的rpm配置数据库。

   install
   安装指定的rpm包。

   packageByXXX
   通过对读入的包头信息的解释,生成相应的包内部信息。比如读出包的大小,版本号,描述,名字等。

   psUsingHdlists
   读入hdlists,由它每一行保存的信息,读入对应的hdlist文件。

   psUsingHdlist
   由psUsingHdlists函数调用,读入所有的rpm包的文件头描述信息。

   remove
   删除指定的rpm包。

   read_rpmsrate
   读入rpmsrate,形成最基本的包类描述。

   readCompssUsers
   读入高层的包类描述信息,返回compssUsers结构。每选中一个compssUsers类,对应多个rpmsrate文件描述的子类。

   selectPackage
   选中指定的包。

   unselectPackage
   取消指定包的选中状态。

   包的处理过程的一般流程:


   #- 下面的程序是RPM包安装过程代码,它是整个包安装过程中作关键的部分。它使用了安装回调的机
   #- 制,使得包安装过程中,系统可以随时显示安装进度并更换安装插图。
   #- 下面的c::*方式调用的函数是对rpmlib.a中的库函数进行封装之后生成的函数,它的封装保存在#- perl语言的存根文件stuff.xs中。
   sub install($$$;$$) {
     my ($prefix, $isUpgrade, $toInstall, $depOrder, $media) = @_;
     my %packages;

     return if $::g_auto_install || !scalar(@$toInstall);

     my $loop_boot = loopback::prepare_boot($prefix);

   my ($total, $nb);
   #- 根据数组toInstall中的内容,选择安装包,同时计算安装的大小。
     foreach my $pkg (@$toInstall) {
     $packages{packageName($pkg)} = $pkg;
     $nb++;
     $total += packageSize($pkg);
     }

     #- 将安装信息写入日志文件,在成功安装系统之后,会形成安装日志文件install.log
     log::l("pkgs::install $prefix");
     log::l("pkgs::install the following: ", join(" ", keys %packages));
     eval { fs::mount("/proc", "$prefix/proc", "proc", 0) } unless -e "$prefix/proc/cpuinfo";

   log::l("reading /usr/lib/rpm/rpmrc");
   #- 读入rpm包的配置文件rpmrc
     c::rpmReadConfigFiles() or die "can't read rpm config files";
     log::l("tdone");

     #- 打开包所在文件的回调函数。
     my $callbackOpen = sub {
     my $p = $packages{$_[0]};
     my $f = packageFile($p);
     print LOG "$f $p->[$MEDIUM]{descr}n";
     my $fd = install_any::getFile($f, $p->[$MEDIUM]{descr});
     $fd ? fileno $fd : -1;
   };
   #- 删除包所在的描述结构,并设置包的安装标志
     my $callbackClose = sub { packageSetFlagInstalled(delete $packages{$_[0]}, 1) };
     installCallback("Starting installation", $nb, $total);

     my ($i, $min, $medium) = (0, 0, 1);
     do {
     my @transToInstall;

     if (!$depOrder || !$media) {
       @transToInstall = values %packages;
       $nb = 0;
     } else {
       do {
       #- 如果需要,改变安装介质(光盘、硬盘)
       if ($i > $media->{$medium}{max}) {
         #- 寻找包含指定安装包的介质
         foreach (keys %$media) {
         $i >= $media->{$_}{min} && $i <= $media->{$_}{max} and $medium = $_, last;
         }
       }
       $i >= $media->{$medium}{min} && $i <= $media->{$medium}{max} or die "unable to find right medium";
       #- 检查使用的安装介质,比如是光盘安装还是别的方式
       install_any::useMedium($medium);

       while ($i <= $media->{$medium}{max} && ($i <$min || scalar @transToInstall <$limitMinTrans)) {
         my $dep = $packages{packageName($depOrder->[$i++])} or next;
         if ($dep->[$MEDIUM]{selected}) {
         push @transToInstall, $dep;
         foreach (map { split '|' } packageDepsId($dep)) {
           $min <$_ and $min = $_;
         }
         } else {
         log::l("ignoring package $dep->[$FILE] as its medium is not selected");
         }
         --$nb;
       }
       } while ($nb > 0 && scalar(@transToInstall) == 0);
     }
     #- 在任何介质都没有选择的包时,退出安装。
     if ($nb == 0 && scalar(@transToInstall) == 0) {
       cleanHeaders($prefix);

       loopback::save_boot($loop_boot);
       return;
     }

     extractHeaders($prefix, @transToInstall, $media->{$medium});
     #- 重设文件描述符
     if ($media->{$medium}{method} eq 'cdrom') {
       install_any::getFile(packageFile($transToInstall[0]), $transToInstall[0][$MEDIUM]{descr});
     }

     install_any::getFile('XXX');

     my $retry = 3;
     #- 为了保证安装过程具有更好的并发性和健壮性,在启动安装时,创建了一个子进程进行安装。
     #- 同时,在父进程中对整个输出过程进行了改向。
     while (@transToInstall) {
       local (*INPUT, *OUTPUT); pipe INPUT, OUTPUT;
       if (my $pid = fork()) {
       close OUTPUT;
       my $error_msg = '';
       local $_;
       while ( ) {
         if (/^die:(.*)/) {
         $error_msg = $1;
         last;
         } else {
         chomp;
         my @params = split ":";
         if ($params[0] eq 'close') {
           &$callbackClose($params[1]);
         } else {
           installCallback(@params);
         }
         }
       }
       $error_msg and $error_msg .= join('', );
       waitpid $pid, 0;
       close INPUT;
       $error_msg and die $error_msg;
       } else {
       #- 子进程执行所有的安装操作
       $SIG{SEGV} = sub { log::l("segmentation fault on transactions"); c::_exit(0) };
       my $db;
       eval {
         close INPUT;
         select((select(OUTPUT), $| = 1)[0]);
         $db = c::rpmdbOpen($prefix) or die "error opening RPM database: ", c::rpmErrorString();
         my $trans = c::rpmtransCreateSet($db, $prefix);
         log::l("opened rpm database for transaction of ". scalar @transToInstall ." new packages,
             still $nb after that to do");

         c::rpmtransAddPackage($trans, $_->[$HEADER], packageName($_), $isUpgrade && allowedToUpgrade(packageName($_)))
         foreach @transToInstall;

         c::rpmdepOrder($trans) or die "error ordering package list: " . c::rpmErrorString();
         c::rpmtransSetScriptFd($trans, fileno LOG);

         log::l("rpmRunTransactions start");
         my @probs = c::rpmRunTransactions($trans, $callbackOpen,
                  sub { print OUTPUT "close:$_[0]n"; },
                  sub { print OUTPUT join(":", @_), "n"; },
                  1);
         log::l("rpmRunTransactions done, now trying to close still opened fd");
         install_any::getFile('XXX');

         if (@probs) {
         my %parts;
         @probs = reverse grep {
           if (s/(installing package) .* (needs (?:.*) on the (.*) filesystem)/$1 $2/) {
           $parts{$3} ? 0 : ($parts{$3} = 1);
           } else {
           1;
           }
         } reverse map { s|/mnt||; $_ } @probs;

         c::rpmdbClose($db);
         die "installation of rpms failed:n ", join("n ", @probs);
         }
       }; $@ and print OUTPUT "die:$@n";

       c::rpmdbClose($db);
       log::l("rpm database closed");

       close OUTPUT;

       my (@killpid, %tree, $pid);
       local (*DIR, *F, $_);
       opendir DIR, "/proc";
       while ($pid = readdir DIR) {
         $pid =~ /^d+$/ or next;
         open F, "/proc/$pid/status";
         while ( ) {
         /^Pid:s+(d+)/ and $pid == $1 || die "incorrect pid reported for $pid (found $1)";
         if (/^PPid:s+(d+)/) {
           $tree{$pid} and die "PPID already found for $pid, previously $tree{$pid}, now $1";
           $tree{$pid} = $1;
         }
         }
         close F;
       }
       closedir DIR;
       foreach (keys %tree) {
         $pid = $_; while ($pid = $tree{$pid}) { $pid == $$ and push @killpid, $_ }
       }
       if (@killpid) {
         log::l("killing process ". join(", ", @killpid));
         kill 15, @killpid;
         sleep 2;
         kill 9, @killpid;
       }

       c::_exit(0);
       }

       my @badPackages;
       foreach (@transToInstall) {
       if (!packageFlagInstalled($_) && $_->[$MEDIUM]{selected} && !exists($ignoreBadPkg{packageName($_)})) {
         push @badPackages, $_;
       } else {
         packageFreeHeader($_);
       }
       }
       @transToInstall = @badPackages;
       $retry or last;

       if (@transToInstall) {
       foreach (@transToInstall) {
         log::l("bad package $_->[$FILE]");
       }
       log::l("retrying transaction on bad packages");
       --$retry;
       }
     }
     packageFreeHeader($_) foreach @transToInstall;
     cleanHeaders($prefix);

     if (@transToInstall) {
       foreach (@transToInstall) {
       log::l("bad package $_->[$FILE] unable to be installed");
       packageSetFlagSelected($_, 0);
       }
       cdie ("error installing package list: " . join(", ", map { $_->[$FILE] } @transToInstall));
     }
     } while ($nb > 0 && !$pkgs::cancel_install);

     cleanHeaders($prefix);
     loopback::save_boot($loop_boot);
   }
php爱好者站 http://www.phpfans.net php基础|php进阶|php模板.
相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载