亚洲成精品动漫久久精久,九九在线精品视频播放,黄色成人免费观看,三级成人影院,久碰久,四虎成人欧美精品在永久在线

掃一掃
關(guān)注微信公眾號

構(gòu)造Linux的圖形化安裝程序(4)
2009-05-22   

本文主要包括RPM基本命令介紹,RPM包的定制過程,RPM SPEC文件的主要內(nèi)容,RPM函數(shù)庫簡單參考和安裝程序中關(guān)于RPM包管理部分源碼的簡單介紹。通過這部分的介紹,希望讀者能對Linux系統(tǒng)下RPM 包的定制過程和RPM包的系統(tǒng)安裝過程有一個(gè)基本的了解。
在安裝程序進(jìn)行了磁盤分區(qū)工作之后,安裝程序就可以進(jìn)行RPM系統(tǒng)包的安裝了。這是整個(gè)安裝過程中很重要的一步。在這一環(huán)節(jié),安裝程序要讀出所有rpm包的描述信息并根據(jù)包之間的依賴關(guān)系,構(gòu)造出正確的包安裝順序。這種構(gòu)造的機(jī)制主要是對包依賴關(guān)系的樹形結(jié)構(gòu)進(jìn)行深度搜索,對于最基本的系統(tǒng)包(比如Glibc和Bash)一定要最先安裝。為了保證在安裝了所有的系統(tǒng)包之后,RPM數(shù)據(jù)庫運(yùn)行良好,還要在安裝過程中構(gòu)造正確的RPM數(shù)據(jù)庫。最后為了調(diào)試的方便,也便于用戶檢查安裝的系統(tǒng)包,還需要對包的安裝過程建立日志。
1 RPM包的基本概念
RPM(Redhat Package Management)是由RedHat開發(fā)的,Linux系統(tǒng)下的系統(tǒng)包管理工具。它的目標(biāo)是:使包的安裝和卸載過程更容易,能夠證實(shí)一個(gè)包是否已經(jīng)正確安裝了,簡化包的建立過程,可以從源代碼建立整個(gè)包,使它能用于不同的體系結(jié)構(gòu)。RPM系統(tǒng)已經(jīng)成為現(xiàn)在Linux系統(tǒng)下包管理工具事實(shí)上的標(biāo)準(zhǔn),并且它也移植到很多商業(yè)的unix系統(tǒng)之下。
RPM包由包標(biāo)簽標(biāo)識,它包含這樣幾個(gè)部分,軟件名,軟件版本,包的發(fā)行版本。在包的內(nèi)部還包含如下信息:包的建立時(shí)間,包的內(nèi)容描述,安裝包的所有文件的大小,數(shù)字簽名以證實(shí)包的完整性。RMP包還包含包內(nèi)的文件信息,其中包括:每個(gè)文件的文件名,每個(gè)文件的權(quán)限,文件的屬組和擁有者,每個(gè)文件的md5校驗(yàn)和,文件的內(nèi)容。
RPM的包管理系統(tǒng)提供了下列功能:安裝新的包,除去舊的包,將一個(gè)舊包升級為新的包,獲得已經(jīng)安裝包的信息。
常用的RPM命令:
rpm -i
使用此命令可以安裝一個(gè)rpm包。在安裝的過程中,此命令依次要進(jìn)行包依賴性檢測,包沖突檢測,完成安裝前必須執(zhí)行的任務(wù),處理相應(yīng)的配置文件,解開包中的文件并將其拷貝到正確的位置,完成安裝后必須執(zhí)行的任務(wù),對包進(jìn)行的處理進(jìn)行跟蹤記錄。
例如:
rpm -i bzip2-1.0.1-3.i586.rpm
//安裝bzip2包。
rpm -ivh bzip2-1.0.1-3.i586.rpm
//安裝bzip2包的同時(shí),顯示更多的文本提示信息,以及在屏幕上顯示連續(xù)的#號來表示的安裝進(jìn)度。
有時(shí)在安裝一個(gè)新包時(shí),根據(jù)依賴性檢查的結(jié)果,需要首先安裝其他的包。但可能這時(shí)系統(tǒng)中并沒有安裝所需要包的合適版本,這樣rpm會終止包的安裝。為了直接安裝這個(gè)包,您需要加入--nodeps選項(xiàng)。下例表示在安裝bzip2包時(shí),不進(jìn)行依賴性檢測。
rpm -ivh bzip2-1.0.1-3.i586.rpm --nodeps
rpm -ivh bzip2-1.0.1-3.i586.rpm --force
//強(qiáng)制安裝rpm包。這條命令實(shí)際上等價(jià)于
rpm -ivh bzip2-1.0.1-3.i586.rpm --replacepkgs --replacefiles
rpm -e
使用此命令可以刪除一個(gè)rpm包。刪除rpm包時(shí),此命令要完成如下工作:
檢查rpm數(shù)據(jù)庫確保沒有其他包依賴將要刪除的包。
如果包存在卸載前腳本,執(zhí)行此腳本。
檢測安裝包時(shí)是否對包配置文件進(jìn)行了修改。如果進(jìn)行了修改,則保存?zhèn)浞荨?
查找rpm數(shù)據(jù)庫中此RPM包所包含的文件。如果這些文件不屬于任何其它的包,則刪除它。
如果包存在卸載后腳本,執(zhí)行此腳本。
從rpm數(shù)據(jù)庫除去所有包跟蹤記錄。
例如:
rpm -e bzip2
//從系統(tǒng)中除去bzip2包。添加--nodeps選項(xiàng)可以在刪除包時(shí),禁止包的依賴性檢查。
rpm -U
這條命令完成rpm包的升級。它執(zhí)行的操作包括安裝合意的包,刪除所有存在的老版本的包。例如:
rpm -U bzip2
//升級包bzip2。
rpm -q
這條命令可以獲得rpm包的信息。通過這條命令可以查詢包的文件列表,包的版本,包的描述性信息。同樣的,你也可以通過這條命令查得一個(gè)文件屬于哪個(gè)rpm包。例如:
rpm -qf `which fdisk`
//檢查fdisk文件屬于哪個(gè)系統(tǒng)包。
rpm -qi bzip2
//獲得已安裝包bzip2的描述性信息。
rpm -ql bzip2
//獲得安裝包bzip2的文件列表。
rpm -qa
//獲得系統(tǒng)安裝的所有rpm包的列表。這條命令和grep命令一起使用,可以快速找到系統(tǒng)中包含的某個(gè)rpm包,例如:
rpm -qa | grep bzip2
2 RPM包建立過程
為了完成RPM包的建立過程,需要執(zhí)行以下步驟:
執(zhí)行Spec文件prep節(jié)的命令和宏。
檢查文件列表的內(nèi)容。
執(zhí)行Spec文件build節(jié)的命令和宏。
執(zhí)行Spec文件install節(jié)的命令和宏,同時(shí)也執(zhí)行文件列表中的宏。
創(chuàng)建二進(jìn)制包文件。
創(chuàng)建源碼包。
為了執(zhí)行打包的工作,RPM需要一系列目錄完成建立的工作。正常的目錄結(jié)構(gòu)通常由一個(gè)頂級目錄和五個(gè)子目錄構(gòu)成。這五個(gè)子目錄分別是:
SOURCES------包含原始的源文件、補(bǔ)丁和像標(biāo)文件。
SPECS--------包含控制建立過程的spec文件。
BUILD--------包含源碼解包和軟件建立的目錄。
RPMS---------包含建立過程創(chuàng)建的二進(jìn)制包文件。
SRPMS--------包含建立過程創(chuàng)建的源碼包文件。
除了上述這五個(gè)主要的目錄外,在RPMS或SRPMS目錄下通常還會有關(guān)于包目標(biāo)平臺的目錄。例如,i386、i586、i686等代表與Intel兼容cpu的平臺,noarch目錄下的包代表可以在任何平臺下執(zhí)行。
2.1 SPEC文件
Spec文件是整個(gè)RPM包建立過程的中心,它的作用就如同編譯程序時(shí)的Makefile文件。Spec文件包含建立一個(gè)rpm包必需的信息,包括哪些文件是包的一部分以及它們安裝在哪個(gè)目錄下。這個(gè)文件一般分為如下的幾節(jié):
Preamle(序言)
序言包含用戶請求包的信息時(shí)所顯示的內(nèi)容。它可以包含包的功能描述,包的軟件版本,版權(quán)信息,所屬的包組等。
Prep節(jié) Prep節(jié)進(jìn)行實(shí)際的打包準(zhǔn)備工作,它是使用節(jié)前綴%prep表示的。一般而言,這一節(jié)的主要工作是檢查標(biāo)簽語法是否正確,刪除舊的軟件源程序,對包含源程序的tar文件進(jìn)行解碼。如果包含補(bǔ)丁(patch)文件,將補(bǔ)丁文件應(yīng)用到解開的源碼中。它一般包含%setup與%patch兩個(gè)命令。% setup用于將軟件包打開,執(zhí)行%patch可將補(bǔ)丁文件加入解開的源程序中。
%setup
-n newdir---------將壓縮的軟件源程序在newdir目錄下解開。
-c ---------------在解開源程序之前先創(chuàng)建目錄。
-b num------------在包含多個(gè)源程序時(shí),將第num個(gè)源程序解壓縮。
-T----------------不使用缺省的解壓縮操作。
例如:
%setup -T -b 0
//解開第一個(gè)源程序文件。
%setup -c -n newdir
//創(chuàng)建目錄newdir,并在此目錄之下解開源程序。
%patch
%patchN----------這里N是數(shù)字,表示使用第N個(gè)補(bǔ)丁文件,等價(jià)于%patch -P N
-p0--------------指定使用第一個(gè)補(bǔ)丁文件,-p1指定使用第二個(gè)補(bǔ)丁文件。
-s---------------在使用補(bǔ)丁時(shí),不顯示任何信息。
-b name----------在加入補(bǔ)丁文件之前,將源文件名上加入name。若為指定此參數(shù),則缺省源文件加入.orig。
-T---------------將所有打補(bǔ)丁時(shí)產(chǎn)生的輸出文件刪除。
Build節(jié)
這一節(jié)主要用于編譯源碼,它是使用節(jié)前綴%build表示的。這一節(jié)一般由多個(gè)make命令組成。
Install節(jié)
這一節(jié)主要用于完成實(shí)際安裝軟件必須執(zhí)行的命令,它是使用節(jié)前綴%install表示的。這一節(jié)一般是由make install指令構(gòu)成,但是有時(shí)也會包含cp、mv、install等指令。
這一節(jié)還能指定在用戶安裝的系統(tǒng)上,包安裝時(shí)運(yùn)行的腳本。這樣的腳本稱為安裝(卸載)腳本。它可以指定包安裝前、包安裝后、包除去前、包除去后的系統(tǒng)必須運(yùn)行的外殼程序段。在用戶安裝的系統(tǒng)上,為了驗(yàn)證一個(gè)包是否已經(jīng)成功安裝的驗(yàn)證腳本也可由這一節(jié)指定。
Clean節(jié)
這一節(jié)所描述的內(nèi)容表示在完成包建立的工作之后,自動執(zhí)行此節(jié)下的腳本進(jìn)行附加的清除工作,它是使用節(jié)前綴%clean表示的。一般而言,這一節(jié)的內(nèi)容是簡單地使用rm -rf $RPM_BUILD_ROOT命令,不需要指定此節(jié)的其它內(nèi)容。
文件列表
這一節(jié)指定構(gòu)成包的文件的列表,它是使用節(jié)前綴%files表示的。此外,它還包含一系列宏控制安裝后的文件屬性和配置信息。
改動日志
這一節(jié)主要描述軟件的開發(fā)記錄,它是使用節(jié)前綴%changlog表示的。這個(gè)段的內(nèi)容是為了開發(fā)人員能詳細(xì)的了解該軟件的開發(fā)過程,對于包的維護(hù)極有好處。
2.2 建立rpm包
有時(shí)您可能只有一個(gè)tar.gz格式的源程序包,為了生成正確的rpm包,您可以使用autospec自動創(chuàng)建spec文件。
舉例來說,您有一個(gè)源程序文件some.tar.gz。為了定制rpm包,您要進(jìn)行如下操作:
解壓縮源程序包
tar xvzf some.tar.gz
手動編譯和安裝此源程序包
make; make install
自動生成spec文件
make -n install | autospec -i > some.spec
編譯生成rpm包
rpm -ba some.spec
在創(chuàng)建spec文件之前,必須成功編譯源程序包。否則autospec生成的spec文件將不會包含%build、%install、%file。對于一般的源程序包,您只需到SPEC目錄下,直接執(zhí)行上面操作的第四步就可以了。
3 定制系統(tǒng)安裝盤
定制系統(tǒng)安裝盤的過程主要是指對于用戶提供的一組RPM包,安裝程序能夠根據(jù)用戶提供的描述文件,自動進(jìn)行包的安裝。這個(gè)過程包括對包的依賴性進(jìn)行檢查、根據(jù)類別選擇需要安裝的包。
使用HappyLinux發(fā)布盤上附帶的RPM包管理工具可以定制系統(tǒng)發(fā)布盤,這些包管理工具保存在安裝盤的/misc目錄下。為了保證安裝程序能夠工作,必須在/HappyLinux/base目錄下提供以下的文件:
compss
在HappyLinux系統(tǒng)下,它是由系統(tǒng)命令gendistrib自動生成的。它可以根據(jù)包所屬的包組對包進(jìn)行分類。compss描述發(fā)布盤上所有包的一個(gè)列表。這個(gè)文件對于安裝過程沒有影響,它的存在只是為了與以前的版本兼容。
compssUsers
此文件的內(nèi)容是對rpmsrate文件所描述的包類別進(jìn)行進(jìn)一步的包裝,以形成更高層的包類表述。
depslist
depslist.ordered
這兩個(gè)文件是命令gendistrib生成的,它描述每個(gè)包所依賴的系統(tǒng)包。depslist.ordered使安裝程序?qū)嶋H使用的文件,它將依賴包的文本描述轉(zhuǎn)換為包的編號,這樣可以加快整個(gè)安裝的進(jìn)程。
happyinst_stage2.bz2
這個(gè)文件保存的是整個(gè)安裝環(huán)境(也就是安裝盤上/HappyLinux/happyinst目錄下的所有文件)的壓縮鏡像,它會在啟動過程中由系統(tǒng)加載程序調(diào)入內(nèi)存。這個(gè)文件是由命令make_happyinst_stage2生成的。
hdlists
這個(gè)文件是對安裝盤rpm包所在的目錄進(jìn)行描述。當(dāng)要進(jìn)行多盤安裝時(shí),此文件的每一行表示一張發(fā)布介質(zhì)的RPM包所在的目錄。這個(gè)文件必須由用戶手動生成。
hdlist.cz2
這是由命令gendistrib生成的,包頭信息的描述文件。安裝程序由此文件讀出每個(gè)包的大小、版本等信息。它的名字是由hdlists文件的對應(yīng)行表示的。
provides
這個(gè)文件也是命令gendistrib生成的,它將包沖突檢測的信息保存到此文件中。
rpmsrate
這個(gè)文件是由用戶自己定制的。它主要描述同類別包的編組。同時(shí),對包組的文件指定了優(yōu)先級,使得不同的場景可以自動安裝不同的包組。這些包類別將要寫入compssUsers文件中,構(gòu)成更大的包邏輯分組。
在發(fā)布盤上的任何包的發(fā)生改變時(shí),為了使此改變生效,必須運(yùn)行命令gendistrib,重新生成所有與包描述有關(guān)的文件。
gendistrib --distrib /export
根據(jù)目錄/export下保存的所有包信息生成相應(yīng)的描述文件。
在對包的安裝環(huán)境進(jìn)行了任何修改之后,必須重新運(yùn)行命令make_happyinst_stage2。
make_happyinst_stage2 /export/HappyLinux/happyinst /export/HappyLinux/base/happyinst_stage2.bz2
生成整個(gè)安裝程序的壓縮鏡像。
在進(jìn)行了上述步驟之后,您就可以使用mkisofs創(chuàng)建iso文件,以生成正確的光盤發(fā)布了。
4 rpmlib庫函數(shù)
rpmlib庫函數(shù)是包含在librpm.a庫中的。在c語言中,使用這些函數(shù)必須包含頭文件rpmlib.h,對于與header有關(guān)的函數(shù)還需要包含頭文件header.h。這些函數(shù)完成的功能包括從底層的rpm數(shù)據(jù)庫記錄遍歷到高層的包處理。
在安裝程序中,使用rpmlib中的函數(shù)完成包安裝的功能,具有以下的一些優(yōu)點(diǎn):
大大減小了整個(gè)安裝程序占用的內(nèi)存。這樣安裝程序就無需提供rpm系統(tǒng)文件了。
直接使用c語言的庫函數(shù)提高了包的安裝速度。
通過安裝回調(diào)函數(shù),可以在包的安裝過程中,實(shí)現(xiàn)一些特殊的效果,比如安裝過程中更換顯示圖片,顯示安裝進(jìn)度等。
下面包含的函數(shù)只是rpmlib中與安裝程序有關(guān)的部分函數(shù)接口。
4.1 錯誤處理
int rpmErrorCode(void);
這個(gè)函數(shù)返回最后一個(gè)失敗的rpmlib函數(shù)返回的錯誤碼。此函數(shù)僅用于rpmErrorSetCallBack()定義的錯誤回調(diào)函數(shù)中。
char *rpmErrorString(void);
這個(gè)函數(shù)返回最后一個(gè)失敗的rpmlib函數(shù)返回的錯誤串。此函數(shù)僅用于rpmErrorSetCallBack()定義的錯誤回調(diào)函數(shù)中。
rpmErrorCallBackType rpmErrorSetCallback(rpmErrorCallBackType);
這個(gè)函數(shù)設(shè)置當(dāng)前錯誤回調(diào)函數(shù),它返回以前設(shè)置的錯誤回調(diào)函數(shù)
4.2 獲得包信息
下列函數(shù)用于獲得包文件信息。返回的信息以Header結(jié)構(gòu)的形式保存。
int rpmReadPackageInfo(int fd, Header * signatures, Header * hdr);
使用給定fd(表示對應(yīng)的rpm包)讀入頭信息和標(biāo)記信息。如果指定signatures或hdr,則其對應(yīng)的信息不返回。
int rpmReadPackageHeader(int fd, Header * hdr, int * isSource, int * major, int * minor);
使用給定fd(表示對應(yīng)的rpm包)讀入頭信息,以及此包文件是否是源碼包,包的主、次版本號。
4.3 RPM數(shù)據(jù)庫處理
這一節(jié)的函數(shù)完成RPM數(shù)據(jù)庫的基本操作。這包括打開和關(guān)閉數(shù)據(jù)庫,以及在數(shù)據(jù)庫損壞時(shí)進(jìn)行重建。每個(gè)存取RPM數(shù)據(jù)庫的函數(shù)都要使用rpmdb結(jié)構(gòu),它是RPM數(shù)據(jù)庫的句柄。
int rpmdbOpen(char * root, rpmdb * dbp, int mode, int perms);
此過程打開環(huán)境變量RPMVAR_DBPATH指定的RPM數(shù)據(jù)庫,返回rpmdb結(jié)構(gòu)。
void rpmdbClose(rpmdb db);
此過程關(guān)閉rpmdb結(jié)構(gòu)指定的RPM數(shù)據(jù)庫。
int rpmdbInit(char * root, int perms);
此過程在環(huán)境變量RPMVAR_DBPATH指定的位置創(chuàng)建一個(gè)新的RPM數(shù)據(jù)庫。如果數(shù)據(jù)庫存在,此函數(shù)不做任何操作。
int rpmdbRebuild(char * root);
此過程在環(huán)境變量RPMVAR_DBPATH處重建RPM數(shù)據(jù)庫。
4.4 RPM數(shù)據(jù)庫遍歷
unsigned int rpmdbFirstRecNum(rpmdb db);
此過程返回db指定的數(shù)據(jù)庫第一個(gè)記錄的記錄編號。
unsigned int rpmdbNextRecNum(rpmdb db, unsigned int lastOffset);
此過程返回緊接著lastOffset的記錄的編號。
Header rpmdbGetRecord(rpmdb db, unsigned int offset);
此過程返回指定偏移處的記錄。
4.5 RPM數(shù)據(jù)庫查詢
這一節(jié)的函數(shù)查詢RPM數(shù)據(jù)庫,并且返回dbiIndexSet結(jié)構(gòu)。在不使用此返回值的時(shí)候,使用dbiFreeIndexRecord()函數(shù)釋放。
typedef struct {
dbiIndexRecord * recs;
int count;
} dbiIndexSet;
typedef struct {
unsigned int recOffset;
unsigned int fileNumber;
} dbiIndexRecord;
recOffset是記錄在數(shù)據(jù)庫中的偏移,fileNumber僅用于rpmdbFindByFile()。
int rpmdbFindByFile(rpmdb db, char * filespec, dbiIndexSet * matches);
此過程搜索db指定的RPM數(shù)據(jù)庫,找到擁有filespec指定文件的記錄,并保存在matches中。
int rpmdbFindByGroup(rpmdb db, char * group, dbiIndexSet * matches);
此過程搜索db指定的RPM數(shù)據(jù)庫,查找是group組指定的成員的包,返回值保存在matches中。
int rpmdbFindPackage(rpmdb db, char * name, dbiIndexSet * matches);
此過程搜索db指定的RPM數(shù)據(jù)庫,查找包名是name的包,返回值保存在matches中。
int rpmdbFindByProvides(rpmdb db, char * provides, dbiIndexSet * matches);
此過程搜索db指定的RPM數(shù)據(jù)庫,查找包的provides信息和指定的provides信息一致的包,返回值保存在matches中。
int rpmdbFindByRequiredBy(rpmdb db, char * requires, dbiIndexSet * matches);
此過程搜索db指定的RPM數(shù)據(jù)庫,查找包的requires信息和指定的requires信息一致的包,返回值保存在matches中。
int rpmdbFindByConflicts(rpmdb db, char * conflicts, dbiIndexSet * matches);
此過程搜索db指定的RPM數(shù)據(jù)庫,查找包的conflicts信息和指定的conflicts信息一致的包,返回值保存在matches中。
4.6 包處理
int rpmInstallSourcePackage(char * root, int fd, char ** specFile, rpmNotifyFunction notify, char * labelFormat);
此函數(shù)安裝fd指定的源碼包。如果指定了root字段則將包安裝到此目錄下。notify指定安裝過程中調(diào)用的進(jìn)程跟蹤過程。labelformat指定包標(biāo)簽的打印格式。
int rpmInstallPackage(char * rootdir,rpmdb db, int fd, char * prefix, int flags, rpmNotifyFunction notify, char * labelFormat, char * netsharedPath);
此過程安裝db指定的二進(jìn)制包,如果指定了rootdir,包安裝到此目錄下。flags參數(shù)控制包的安裝行為。
RPMINSTALL_REPLACEPKG------------即使包已經(jīng)安裝,仍繼續(xù)安裝。
RPMINSTALL_REPLACEFILES----------即使會替換另外一個(gè)包的文件也安裝此包。
RPMINSTALL_TEST------------------只進(jìn)行安裝時(shí)檢測,不安裝包。
RPMINSTALL_UPGRADE---------------安裝包并除去包的老版本。
RPMINSTALL_UPGRADETOOLD----------即使此包是老版本,仍進(jìn)行安裝。
RPMINSTALL_NODOCS----------------不安裝包的文檔文件。
RPMINSTALL_NOSCRIPTS-------------不執(zhí)行包的安裝和刪除腳本。
RPMINSTALL_NOARCH----------------不完成結(jié)構(gòu)兼容性測試。
RPMINSTALL_NOOS------------------不進(jìn)行操作系統(tǒng)兼容性測試。

Notify參數(shù)指定安裝過程中系統(tǒng)調(diào)用的進(jìn)度跟蹤過程。回調(diào)過程的函數(shù)指針定義:
typedef void (*rpmNotifyFunction)(const unsigned long amount, const unsigned long total);
amount指定已經(jīng)安裝的字節(jié)數(shù),total指定安裝的總字節(jié)數(shù)。此過程可用于在安裝過程中動態(tài)更新進(jìn)程條等信息。
LabelFormat指定包標(biāo)簽的格式。NetsharedPath參數(shù)指定和其它系統(tǒng)共享的本地文件系統(tǒng)部分。如果共享多個(gè)目錄,路徑要用冒號隔開。
int rpmRemovePackage(char * root, rpmdb db, unsigned int offset, int flags);
此過程除去在rpm數(shù)據(jù)庫中offset位置處的包。root指定包所在的根目錄,flags的值定義在rpmlib.h中,定義了如下標(biāo)志:
RPMUNINSTALL_TEST 進(jìn)行刪除檢測,但是不除去任何包
RPMUNINSTALL_NOSCRIPTS 不執(zhí)行包刪除腳本。
4.7 與包依賴性相關(guān)的操作
依賴性操作是完全和正常的基于包的操作隔離的。包的安裝和除去過程自身并不完成依賴性處理。因此依賴性處理函數(shù)和其他rpmlib函數(shù)不同。依賴性處理的中心是rpmDependencies數(shù)據(jù)結(jié)構(gòu)。這些函數(shù)只是隨著操作的不同管理此數(shù)據(jù)結(jié)構(gòu),它們并不對包進(jìn)行操作。在依賴性檢測過程發(fā)現(xiàn)存在包依賴性沖突問題時(shí),rpmDependencyConflict結(jié)構(gòu)返回依賴性沖突。
rpmDependencies rpmdepDependencies(rpmdb db);
此過程返回初始化了的rpmDependencies結(jié)構(gòu)。此時(shí)基于db參數(shù)指定的rpm數(shù)據(jù)庫進(jìn)行依賴性檢測。
void rpmdepAddPackage(rpmDependencies rpmdep, Header h);
此過程加入頭為h的包到rpmDependencies數(shù)據(jù)結(jié)構(gòu)rpmdep中。
void rpmdepUpgradePackage(rpmDependencies rpmdep, Header h);
此過程加入頭為h的包到rpmDependencies數(shù)據(jù)結(jié)構(gòu)rpmdep中,同時(shí)此過程除去包依賴的老版本。
void rpmdepRemovePackage(rpmDependencies rpmdep, int dboffset);
此過程在rpmdep依賴結(jié)構(gòu)中除去rpm數(shù)據(jù)庫中偏移為dboffset的包。
void rpmdepAvailablePackage(rpmDependencies rpmdep, Header h, void * key);
此過程加入頭為h的包到rpmDependencies數(shù)據(jù)結(jié)構(gòu)rpmdep中。Key用于標(biāo)識加入的包,這個(gè)參數(shù)會作為rpmdepCheck()函數(shù)返回的rpmDependencyConflict結(jié)構(gòu)的一部分。
int rpmdepCheck(rpmDependencies rpmdep, struct rpmDependencyConflict ** conflicts, int * numConflicts);
此函數(shù)在rpmDependencies類型的結(jié)構(gòu)變量rpmdep上進(jìn)行依賴性檢測。它返回一個(gè)numConflicts大小的數(shù)組,數(shù)組保存在conflicts指針中。
void rpmdepFreeConflicts(struct rpmDependencyConflict * conflicts, int numConflicts);
此函數(shù)釋放conflicts指針指向的依賴性沖突結(jié)構(gòu)。
void rpmdepDone(rpmDependencies rpmdep);
此函數(shù)釋放rpmDependencies類型的結(jié)構(gòu)變量rpmdep。
4.8 頭信息處理
rpm頭信息是為最小化的rpm數(shù)據(jù)庫服務(wù)的,在這個(gè)數(shù)據(jù)庫中可以進(jìn)行特定信息的檢索。
Header headerRead(int fd, int magicp);
此函數(shù)從文件fd中讀入header并且轉(zhuǎn)換網(wǎng)絡(luò)字節(jié)序?yàn)橹鳈C(jī)系統(tǒng)字節(jié)序。如果magicp定義為HEADER_MAGIC_YES,此函數(shù)期望接收頭幻數(shù)。如果magicp定義為HEADER_MAGIC_NO,此函數(shù)不包含頭幻數(shù)。
void headerWrite(int fd, Header h, int magicp);
此函數(shù)將頭h寫入文件fd中并且轉(zhuǎn)換主機(jī)系統(tǒng)字節(jié)序?yàn)榫W(wǎng)絡(luò)字節(jié)序。如果magicp定義為HEADER_MAGIC_YES,此函數(shù)在寫入的頭信息中加入適當(dāng)?shù)幕脭?shù)。如果magicp定義為HEADER_MAGIC_NO,此函數(shù)寫入的信息不包含幻數(shù)。
Header headerCopy(Header h);
此函數(shù)返回頭h的副本。
unsigned int headerSizeof(Header h, int magicp);
此函數(shù)返回頭h占用的字節(jié)數(shù)。
Header headerNew(void);
此函數(shù)返回一個(gè)新的頭。
void headerFree(Header h);
此函數(shù)釋放h所指定的頭信息。
void headerDump(Header h, FILE *f, int flags);
此函數(shù)打印頭h結(jié)構(gòu)到文件f中。若flags標(biāo)志為HEADER_DUMP_INLINE,頭數(shù)據(jù)也被打印。
4.9 包頭信息處理
此節(jié)的這些函數(shù)提供處理頭入口的基本操作,包含下列頭入口類型:
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無法處理的二進(jìn)制數(shù)據(jù)。
RPM_STRING_ARRAY_TYPE - 此入口包含字符數(shù)組。
int headerGetEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c);
此過程從h中取出與tag標(biāo)志匹配的入口。入口類型由type返回,數(shù)據(jù)指針由p返回,數(shù)據(jù)大小由c返回。
int headerAddEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c);
此函數(shù)向頭h中加入新的入口, tag參數(shù)指定入口的標(biāo)志,type指定入口的類型。P指定入口數(shù)據(jù),c是此數(shù)據(jù)的大小。
4.10 頭重復(fù)器支持
rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag, const void * key, size_t keylen);
此函數(shù)返回新創(chuàng)建的重復(fù)器。
Header rpmdbNextIterator(rpmdbMatchIterator iter);
此函數(shù)獲得下一個(gè)頭信息。
void rpmdbFreeIterator(rpmdbMatchIterator iter);
此函數(shù)釋放重復(fù)器iter占用的資源。
5 RPM包管理過程實(shí)現(xiàn)
在安裝程序中與包管理有關(guān)的函數(shù)基本上都保存在文件pkgs.pm中。這個(gè)模塊中包含的主要函數(shù)如下:
getDeps
讀入文件depslist.ordered,通過它形成所有包所依賴rpm包的描述結(jié)構(gòu)。
getProvides
讀入所有rpm文件的頭信息描述結(jié)構(gòu),通過provides字段形成包的Provides結(jié)構(gòu)。
init_db,rebuild_db_open_for_traversal,clean_old_rpm_db
初始化(重建、清除)包的rpm配置數(shù)據(jù)庫。
install
安裝指定的rpm包。
packageByXXX
通過對讀入的包頭信息的解釋,生成相應(yīng)的包內(nèi)部信息。比如讀出包的大小,版本號,描述,名字等。
psUsingHdlists
讀入hdlists,由它每一行保存的信息,讀入對應(yīng)的hdlist文件。
psUsingHdlist
由psUsingHdlists函數(shù)調(diào)用,讀入所有的rpm包的文件頭描述信息。
remove
刪除指定的rpm包。
read_rpmsrate
讀入rpmsrate,形成最基本的包類描述。
readCompssUsers
讀入高層的包類描述信息,返回compssUsers結(jié)構(gòu)。每選中一個(gè)compssUsers類,對應(yīng)多個(gè)rpmsrate文件描述的子類。
selectPackage
選中指定的包。
unselectPackage
取消指定包的選中狀態(tài)。
包的處理過程的一般流程:

#- 下面的程序是RPM包安裝過程代碼,它是整個(gè)包安裝過程中作關(guān)鍵的部分。它使用了安裝回調(diào)的機(jī)
#- 制,使得包安裝過程中,系統(tǒng)可以隨時(shí)顯示安裝進(jìn)度并更換安裝插圖。
#- 下面的c::*方式調(diào)用的函數(shù)是對rpmlib.a中的庫函數(shù)進(jìn)行封裝之后生成的函數(shù),它的封裝保存在#- 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);
#- 根據(jù)數(shù)組toInstall中的內(nèi)容,選擇安裝包,同時(shí)計(jì)算安裝的大小。
foreach my $pkg (@$toInstall) {
$packages{packageName($pkg)} = $pkg;
$nb++;
$total += packageSize($pkg);
}
#- 將安裝信息寫入日志文件,在成功安裝系統(tǒng)之后,會形成安裝日志文件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");
#- 打開包所在文件的回調(diào)函數(shù)。
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;
};
#- 刪除包所在的描述結(jié)構(gòu),并設(shè)置包的安裝標(biāo)志
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 {
#- 如果需要,改變安裝介質(zhì)(光盤、硬盤)
if ($i > $media->{$medium}{max}) {
#- 尋找包含指定安裝包的介質(zhì)
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";
#- 檢查使用的安裝介質(zhì),比如是光盤安裝還是別的方式
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);
}
#- 在任何介質(zhì)都沒有選擇的包時(shí),退出安裝。
if ($nb == 0 && scalar(@transToInstall) == 0) {
cleanHeaders($prefix);
loopback::save_boot($loop_boot);
return;
}
extractHeaders($prefix, \@transToInstall, $media->{$medium});
#- 重設(shè)文件描述符
if ($media->{$medium}{method} eq 'cdrom') {
install_any::getFile(packageFile($transToInstall[0]), $transToInstall[0][$MEDIUM]{descr});
}
install_any::getFile('XXX');
my $retry = 3;
#- 為了保證安裝過程具有更好的并發(fā)性和健壯性,在啟動安裝時(shí),創(chuàng)建了一個(gè)子進(jìn)程進(jìn)行安裝。
#- 同時(shí),在父進(jìn)程中對整個(gè)輸出過程進(jìn)行了改向。
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 {
#- 子進(jìn)程執(zhí)行所有的安裝操作
$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);
}

熱詞搜索:

上一篇:構(gòu)造Linux的圖形化安裝程序(3)
下一篇:RedHat Linux9.0安裝實(shí)例

分享到: 收藏