=================================================== RPM 包裝檔案管理程式 -- 參與 GNU Linux 開發的踏腳石 =================================================== marr@iis.sinica.edu.tw 2005 Oct 13 在 GNU Linux 的世界裡,RPM 指的就是 RPM Package Manager (以前指的是 Red Hat Package Manager),我們稱之為「包裝檔案管理程式」。一套良好的管理程式,能夠成為程式寫作人員與一般使用人員之間的工具橋樑,有助於 GNU Linux 應用環境的推廣。長期以來,RPM 已廣被業界與一般使用者接受,成為最受歡迎的包裝檔案格式。本文的用意,在於介紹 RPM 系統內部運作的入門資訊,協助 GNU Linux 玩家獲得製作 RPM 包裝檔案的必備知識。 包裝檔案的重要性 ================ 曾經在 GNU Linux 環境上編譯過軟體的朋友,很可能都吃過這樣的苦頭:雖然軟體說明檔裡會告訴你該準備好哪些函式庫,但是說明文件很可能因為過於簡略,甚至內含過時、錯誤的資訊,導致整個編譯過程猶如摸石過河,充滿挫折或黑魔術般的驚奇!面對函式庫相依關係的連環套,若不是得花時間逐一破解、過關斬將,就是得碰運氣求助於「大師們」(guru)的經驗。如果能把這些過程當成修練或遊戲,倒也算是自得其樂,但是,並非所有人都想如此辛苦地學習 GNU Linux,能讓不同需求的使用者都各取所好,該是美事一椿。 包裝檔案提供了一個架構機制,讓軟體的開發者、測試者、使用者,彼此的角色分工得以進一步區隔,讓更多人可以參與軟體改善的整體工作,成為流程環節裡的貢獻者;包裝檔案也是一項以簡御繁的工具,協助使用者管理系統的應用軟體,藉以改善系統的效能及安全性。透過包裝檔案,軟體的建置與散佈,變得更為模組化與標準化,昇級或移除軟體的過程,變得更為簡便而可控制。 當前流行的 GNU Linux 散佈版本 (distribution),都有獨具特色的包裝檔案管理系統及運作機制,散佈版本的維護工作,有賴開發者與測試者的密切合作,才能帶給使用者更多更新更穩定的驅動程式和應用軟體。可見的未來裡,將有越來越多的軟體採用線上昇級方式來散佈,在此趨勢下,包裝檔案所扮演的角色,也將日益重要。 包裝檔案管理系統的運作原理 ========================== 包裝檔案管理系統 (package management system) 絕不是 Red Hat Linux 獨有,當然也不是 Linux 獨有,諸如 FreeBSD 或其他商用 Unix 系統上,類似的東西不但行之多年,而且也是相當成熟。舉例來說,Debian GNU/Linux 的愛用者可以利用 dpkg、apt 工具程式來管理 deb 包裝檔案,FreeBSD 環境上的 ports 提供類似的管理機制,Gentoo Linux 即採用 ports 機制來實作包裝檔案管理。值得一提的是,由 Red Hat Linux 帶頭發展出來的 rpm 機制,也被其他 distributor (例如 SuSE、Mandriva) 廣泛使用,甚至和 Debian 的 apt 能夠互通。 通常,包裝檔案管理系統都會存在一個小型資料庫,用以集中管理所有的包裝檔案訊息,以及各式軟體之間的相依關係。在此,相依關係 (dependency) 指的是,某一個軟體的正常運作,需要另一個軟體事先成功安裝,便稱這兩個軟體之間有著相依關係,舉例來說,MS Windows 使用者在安裝特定應用程式時,需要搭配 Direct X 軟體,就是一個「需要滿足相依關係」的類似情況。而且,這還包括「軟體之間適當的版本搭配」,例如「python >= 1.3」這樣的資訊,表示需要事先安裝好 1.3 版本以上的 Python 程式。 搭配小型資料庫的運作,包裝檔案管理系統可以回答或處理類似下列的問題: * 我的 Linux 機器裡倒底裝了哪些軟體或應用程式?可以減肥嗎? * 我想安裝測試一些下載自網站的軟體,測試後可以解除安裝嗎? * 我想將部份軟體以包裝系統來昇級與管理,該如何進行呢? * 我成功地安裝了 Apache 及 PHP 的 rpm 包裝檔案,想再加入 MySQL 應用的支援,該如何擴充呢? 初學者在進階學習 Linux 系統時,通常就會遇到上述的問題,透過包裝檔案管理系統的協助,它能提供相關的軟體資訊,諸如檔案版本、檔案安裝路徑、相依關係、執行與設定檔案的位置等。 為了讓軟體的編譯、包裝、測試、整合過程更順暢完整,包裝檔案管理系統會使用一個稱為 SPEC 的描述檔案,再搭配必要的 patch 檔案,程式人員就可以系統化地製作包裝檔案,甚至讓不同平台 (architecture) 都能同步產生包裝檔案。產生的包裝檔案通常有兩種格式,一種是類似 .src.rpm 格式,一種是類似 .i586.rpm 格式,分別代表「原始碼包裝檔案」及編譯過的「二進位元包裝檔案」。 .src.rpm 這類的檔案,可以讓其他程式人員進行驗證或重新包裝的工作,而 .i586.rpm 這類的檔案,裡頭通常包含了執行檔、說明文件、設定檔、安裝圖示,就可以讓一般使用者直接安裝。 包裝檔案管理系統的先備知識 ========================== 在自己動手做包裝檔案前,必須先檢查是否擁有足夠的先備知識,它們將是過關斬將時的隨身武器,要是缺了下列任何一項,很抱歉,請務必補齊: □ 一個順手的編輯器,編寫程式用的: 如果你想多花點時間在 Linux 上,特別是打算成為程式人員,那麼學好一個編輯器,重要性是很大的。我個人偏好使用 vi 編輯器,不過,emacs 也是很好的工具。兩者相比,vi 具有輕巧、普遍存在的優勢,實務上,多數 Linux 環境附的是 vi 改良版本 -- vim,它在 MS Windows 環境上也有移植。學習文件可以參考李果正所撰寫的《大家來學 Vim》一文。 □ 熟悉 shell script 語法 (特別是 bash): bash 是 Linux 環境上最常見的 shell 程式,其重要性真的難以言喻,其線上說明文件,可由 man bash 或 info bash 閱讀,有意進階的朋友,還可以考慮直接購買市面上的專書。 □ 熟悉工具程式的使用,多多益善 Linux 環境上與程式語言相關的工具程式,諸如 grep、sed、awk、install、ldconfig、tar、gzip/bzip,大多可以在 /bin 或是 /usr/bin 目錄下找到,同樣地,建議至少先瀏覽過線上說明文件。 □ 熟悉包裝檔案管理程式之基本功能: 一些實用的基本功能,諸如安裝(install)、昇級(upgrade)、查詢(query)、移除(erase)、驗證(verify) 等,在本文後面將提供更詳細說明。 □ 基本 C 語言編譯技巧: 至少包括 Makefile 寫作概念,diff、patch、gcc 的使用等。究竟該具備多少 C 語言的相關技巧?這點我也說不上來,嚴格地講,在下也是程式白痴一個。所以,視情況而定吧。如果您希望製作難度高的包裝檔案,那麼高超的編譯除錯技巧、精緻的參數選項設定,勢必需要。如果原始碼的作者,早就寫好一份完美的 Makefile,那麼直接 configure、make、make install (這通常是 GNU 軟體的標準編譯流程),倒也全不費功夫。 另外,或許有人專精 Perl、Python、Tk/Tcl、Java 之類的語言,這原本也是好事,特別是在製作這類與「另類語言」相關的包裝檔案時,您應該會倍感親切 (例如 Perl 程式的編譯流程,通常是 perl Makefile.PL、make、make install)。不過,再次強調,shell script 是多數包裝檔案管理系統的基本語言。而 C 則是其常態語言,像 Perl、Python、Tk/Tcl、Java 雖然也能解決幾乎所有問題,但目前還未全面被應用在系統底層。而且,別小看 grep、sed、awk 這些小型工具,它們與 bash 搭配的威力,應該可以滿足所有包裝檔案製作時的需要。 以上是入門包裝檔案管理系統的通用知識,以下則將以 rpm 為例,介紹這個 Linux 環境上最被熟知的包裝檔案格式。 RPM 之一般資訊 ============== 在 RPM 的文件方面,網路上已有不少,而且目前也有專書解說,列舉如下: □ RPM Guide http://fedora.redhat.com/docs/drafts/rpm-guide-en/ Eric Foster-Johnson 所撰寫的文件,詳細說明 RPM 的運作原理及操作方式。 □ Maximum RPM http://www.rpm.org/max-rpm-snapshot/ Maximum RPM 是一本 RPM 專書的內容,由 SAMS 所出版,市面上可以購得。作者 Edward C. Bailey 除了在網站上有 Postscript 檔案 http://www.rpm.org/local/maximum-rpm.ps.gz 讓讀者下載列印,還提供了 LaTeX 格式檔案。值得注意的是,這本書的內容正在改寫中,最新內容可從 CVS 伺服器上取得。 □ Mandrakelinux Rpmdrake and URPMI HOWTO http://www.mandrakehelp.com/RPM-HOWTO.html Eskild Hustvedt 針對 Mandiva Linux 所撰寫的文件。 □ RPM WebSite http://www.rpm.org/ RPM 計畫主網站,資訊集中站。這裡提供通信論壇資料記錄,最新的 RPM 程式發展,應該考慮在通信論壇 http://www.rpm.org/mailing_list/ 上取得。 □ RPM HOWTO http://www.linux.org.tw/CLDP/RPM-HOWTO.html 一篇概況式的中譯文件,不過資料已呈老舊,而且本身實用性並不高。 □ RPM 包裝檔案管理程式 -- Linux 探險的必備工具 http://www.study-area.org/tips/rpm.htm 原文刊於 PC2000 三月號,算是還有些入門參考價值的中文簡介資料。 RPM 目前可以在多種平台上運作,包括 Linux、FreeBSD、cygwin、Solaris、AIX 等。而取得 RPM 最好的方法,就是安裝任何一個使用 RPM 為系統基礎的散佈套件。如果你不想要這麼做的話,當然還是可以取得及使用 RPM。請自 ftp://ftp.rpm.org/pub/rpm/dist/ 這個位置取得程式源碼。若是要從原始碼來建立 RPM (例如使用 Slackware Linux 或是 FreeBSD 的朋友),你還必須準備如 C compiler、make 等編譯環境的工具。 RPM 之基本功能 ============== RPM 管理系統裡,使用的檔案格式是 .rpm 檔案,也就是類似 apache-1.3.12-2.i386.rpm 這樣的檔案命名格式。名稱格式之間以「減號」相隔,顧名思義,apache 是包裝檔案的名稱 (software's name),1.3.12 是版本號碼 (softeware's version),而 2 是包裝號碼 (package's release,通常指的是第幾次的包裝),到此為止的 apache-1.3.12-2 稱為「package label」。i386 指的是編譯的平台環境,類似的有 i586、i686,或是像 alpha、sparc、ppc 等,以代表不同的架構環境(architecture),不過,它們都是屬於「二進位元包裝檔案」(binary rpm package)。有時候會有包裝者的資訊,如 CLE、mdk (mandrake) 等,或是包裝檔案的特色資訊等,如 kernel-2.2.16-3.ext3.ide-i386.rpm (加入 ext3 檔案系統支援的設定項目)。noarch (no architecture) 指的是「與架構環境無關」,諸如以文件資料、Perl 模組程式為素材所製作的包裝檔案。至於 src.rpm 指的是「源碼包裝檔案」(source rpm package),內含規格設定檔案等資訊,可用來製作出上述的 i386.rpm 或 noarch.rpm 等。 值得一提的是,在檔案系統裡的「包裝檔案名稱」,並未必一定要和「package label」一致,例如 apache-1.3.12-2.i386.rpm,若改變檔案成為 apache.rpm,並不會影響其「package label」內容(仍同為 apache-1.3.12-2)。 包裝檔案的名稱,通常會盡可能清楚描述檔案本身的功能,例如 python-devel 指的是 Python 語言的程式開發函式檔案組,python-docs 指的是額外包裝的文件資料。當然,任何時候還是可以直接以 rpm -qi pythonlib 這類的指令語法,取得詳細的包裝檔案資訊。 以下則將以 Mandriva 10.2 for i586 的環境為例,介紹幾個簡單而實用的指令範例,僅管 Mandriva rpm 的範例未必能完全適用於其他 Linux distribution,但是運作機制的實務參考價值還是很高。簡單地看,rpm 程式提供四大基本功能: □ 查詢 (query) 查詢是以 -q 為指令參數前導字元,可以搭配其他子參數,下列是幾個有用的範例: example$ rpm -qa | grep -i mysql MySQL-client-4.0.15-1mdk MySQL-common-4.0.15-1mdk libmysql12-4.0.15-1mdk MySQL-4.0.15-1mdk MySQL-python-0.9.2-2mdk -qa 表示 query all 之意,輸出結果是所有已安裝的包裝檔案名稱。利用 | (pipeline) 及 grep 就可以把輸出結果進一步過濾。搭配 grep 的 -i 參數,表示 ignore-case 之意,可以查詢含有 mysql (包括 MySQL、mysql 不分大小寫) 字樣的包裝檔案名稱。而下列的範例則顯示更詳細的查詢結果: example$ rpm -qa --queryformat '%15{NAME} %30{GROUP}\n' | grep -i mysql 利用 --queryformat 可以自訂查詢格式,諸如 %{NAME}、%{GROUP}、%{VERSION} 都是查詢的標籤範例,而 15、30 數字則是用來指定顯示的欄位長度。 example$ rpm -qf `which startx` -f 可以用來查詢特定檔案所屬的包裝檔案,語法上是接一個檔案名稱,這裡我們利用「反括號」(`) 的 shell 技巧,先讓 `which startx` 產生 /usr/X11R6/bin/startx 的輸出結果,再餵給 -qf 參數,善用此技巧,可以節省打字與尋找檔案的時間。 example$ rpm -q --provides gtk+2.0 example$ rpm -q --provides glibc example$ rpm -q --provides mozilla-firefox --provides 選項可以用來查詢某個包裝檔案所提供的「虛擬包裝名稱」(virtual package)。例如 gtk+2.0 提供了 gtk+2 及 gtk+2.0 這兩個虛擬包裝名稱;glibc 提供了 ld.so 及 glibc-localedata 等名稱,在此例中,即是所謂的動態連結函式庫名稱;而 mozilla-firefox 提供的是 webclient、mozilla-firebird 等虛擬包裝名稱,如果你查詢 lynx 的話,也會發現它同樣有提供 webclient 虛擬包裝名稱。若是有某個包裝檔案需要相依於 webclient 的話 (例如 urlview),那麼必須至少有一個以上的包裝檔案提供該虛擬包裝名稱,才能把相依關係滿足。和 --provide 相對的是 --requires 選項,進階的操作可以類似下列例子: example$ rpm -q --whatrequires python 可以查詢出有哪些包裝檔案需要滿足 python 的相依關係。 example$ rpm -qip apache-1.3.12-2.i386.rpm 這是許多新手容易搞混的地方,-q 所接之查詢參數,通常是 software's name 或是 package label,所以 rpm -q apache-1.3.12-2.i386.rpm 會出現「package apache-1.3.12-2.i386.rpm is not installed」的錯誤訊息。想要查詢類似 .i386.rpm 或 .src.rpm 這樣的包裝檔案,記得要使用 -p 選項。 □ 安裝 (install) 及 昇級 (upgrade) 安裝是以 -i 為指令參數前導子,可以搭配其他子參數,下列是一個有用的範例: example$ rpm -ivvh --test \ ftp://mdk.linux.org.tw/pub/mandrake/2006.0/i586/media/main/gcin-1.0.3-3mdk.i586.rpm -v 是報告 RPM 「內部運作」詳細資訊 (verbose) 的意思,在 rpm 指令選項中,幾乎可以隨時任意搭配使用,譬如,此處的兩個 v 就表示「更加詳細地」顯示資訊。最後,我們指定 --test 選項,表示只打算測試安裝的狀況,而非真正執行安裝動作。 圖(rpm-i-test.png):可以透過FTP模式直接上網安裝包裝檔案 安裝包裝檔案時,可能會遇到「衝突」(conflict) 的情況,例如你想替系統安裝一套 postfix 郵件伺服程式,卻忘了留意原本留在系統裡的 sendmail 郵件程式,此時 RPM 便會通告發生衝突狀況。由於 sendmail 與 postfix 都提供 smtpdaemon 這個虛擬包裝檔案名稱,在沒有適當做好設定之前,這樣的衝突狀況會導致安裝或執行的失敗。 圖(rpm-conflict.png):RPM偵測到衝突狀況發生 昇級則是以 -U 為指令參數前導子,和「安裝」的差別在於:它會先將準備要裝進系統的包裝檔案安裝好,然後再執行移除舊版包裝檔案的動作。 想要聰明地讓系統順利昇級,其實裡頭牽涉到不少複雜的細節,特別是觸及系統核心及函式庫的場合,或是許多系統設定檔的內容已被更改。RPM 採用 MD5 checksum 的檢查機制,包裝檔案裡若含有設定檔案 (config files),則在第一次安裝時會記錄其 MD5 值,日後用以比對設定檔的內容是否已被更動。 假設原始設定檔案的 MD5 值為 X,而現存設定檔案的 MD5 值為 Y,如果準備要昇級的包裝檔案裡,所含的設定檔案 MD5 值為 Z,則意謂「設定檔案內容已被更動」,而且「很可能新的設定檔裡已加入新的特色功能」。此時昇級過程中,RPM 會印出警告訊息,說明該設定檔已被更名,通常是檔案後面加上 .rpmsave 之副檔名。 □ 移除 (erase) 移除是以 -e 為指令參數前導子,可以搭配其他子參數,下列是一個有用的範例: example$ rpm -e --nodeps python 由於在真正執行移除動作之前,RPM 會進行相依關係的確認檢查,如果準備要移除的包裝檔案與其他檔案「緊密糾纏」,那麼在移除之前最好要三思再行。特別是和系統核心或函式庫相關的項目,若是一場災難於焉發生,屆時可就呼天不應了。原則上,越是屬於系統底層的包裝檔案,它的相依關係越是複雜,道理不難體會,你可以想像整個 Linux 系統如同由許多積木不斷堆疊起來,地基部份的關鍵積木若是不小心被移除,則整個系統會有頓時癱瘓的風險。 圖(rpm-e-py.png):移除時因相依關係檢查所造成的警告訊息。 雖然存在上述的風險,我還是非常鼓勵讀者盡量透過 RPM 來測試與探究 Linux 系統。當然,千萬不要拿具生產力的線上主機開玩笑,最好是自己能夠獨力管理,而且硬碟未存在重要資料的主機。在認識 Linux 系統的題目上,RPM 可以像是一個貼身而耐心的老師,隨時等著向你解說系統的許多秘密。 □ 驗證 (verify) 想像一下,在一個意外中,你不小心刪除了一些檔案,但是你也不確定你倒底刪除了什麼?如果你想要驗證 (verify) 整個系統,並且查看倒底缺少了什麼,你可以鍵入以下的指令: example$ rpm -Va 圖(rpm-va.png):RPM正在進行系統檔案的驗證工作 有人在一裝好系統後,就替系統檔案保存一份 MD5 紀錄,以供日後進行系統安全檢查,而上述這個指令也做了類似的動作。前面提過 RPM 管理著一個包裝檔案的資料庫,進行驗證動作時,所參考的資訊來源即是此一資料庫,當然,RPM 資料庫也有錯誤掛點的風險,但是與系統檔案發生狀況的機會來比,總是小許多。 RPM 進行驗證時,會檢查下列九個屬性資料: □ S 是「檔案的大小」。 □ M 是「檔案的存取模式」。 □ 5 是「檔案的 MD5 值」。 □ D 是「檔案的主要與次要號碼(major/minor number)」。 □ L 是「檔案的符號連結內容」。 □ U 是「檔案的擁有者」。 □ G 是「檔案所屬的群組名稱」。 □ T 是「檔案的修改時間」。 □ c 的出現,代表其為「設定檔案」。 以 S.5....T c /etc/inetd.conf 這樣的結果為例,它表示 /etc/inetd.conf 這個檔案的「檔案大小」「MD5 值」「修改時間」都已經和原始安裝狀態不同,當然,如果某個檔案被誤刪了,那麼它會出現「missing」的訊息。 不過,倒也不是每一種檔案類型 (file type) 都適用上述的屬性資料,事實上 RPM 依不同的檔案類型來進行上述的檢查動作。 檔案類型 大小 存取模式 MD5 值 主號 次號 連結內容 擁有者 群組 修改時間 目錄檔案 X X X 連結檔案 X X X X FIFO檔案 X X X 設備檔案 X X X X X 一般檔案 X X X X X X 表:RPM 會依據不同檔案類型進行必要的屬性檢查 example$ rpm -Vvv rpm 我們可以使用 -Vvv 參數選項,在 RPM 進行驗證過程中,順便得知包裝檔案的詳細相依資訊。 其他實用的 RPM 功能 =================== 在對 RPM 的基本功能有所暸解之後,接下來就讓我們來看看在 RPM 中的一些實用功能: □ --rebuilddb 資料庫重建 RPM 的資料庫非常重要,一旦有所毀損,整個管理功能便會大受影響。若是真有不幸發生,還是可以嘗試以 rpm --rebuilddb 指令來重建整個資料庫。另外,如果系統經歷過大規模的昇級動作,重建整個資料庫也是有好處的。資料庫存放於 /var/lib/rpm 目錄底下,觀察一下,你可以看到諸如 Packages、Conflictname、Requirename、Providename 等資料庫檔案。 □ --showrc 除了 RPM 的基本功能外,還有一個非常重要的資訊來源,是由 --showrc 選項所提供。執行 rpm --showrc 後的顯示內容,主要分為「平台與作業系統」資訊,與「RPMRC 設定值」資訊,讓使用者一目了然地查閱 RPM 系統運作時的系統變數值。其中的 RPMRC 資訊區段,還可細分有巨集設定內容 (macro)、編譯選項內容 (compile options)、規格資訊內容 (specs options)、系統工具清單內容 (tool options) 等。 □ rpm2cpio 有時候,二進位包裝檔案裡所包含的眾多檔案,你只想解出其中某個特定檔案,此時 rpm2cpio 便派上用場,範例如下: example$ rpm2cpio postfix-2.2.5-7mdk.i586.rpm | cpio -t cpio 是 rpm 所內附使用的檔案壓縮工具,rpm2cpio 指的就是「從 rpm 包裝檔案中還原解讀出 cpio 格式的檔案」,如上例,我們透過 pipeline (|) 過濾 cpio 檔案的輸出內容,給 cpio -t 來列出所有的檔案名稱。 有了 cpio 檔案名稱列表,就可以指定解出某個特定的檔案,如下例: example$ rpm2cpio postfix-2.2.5-7mdk.i586.rpm | cpio -ivd ./usr/share/man/man5/master.5.bz2 分享 DIY 的快樂 =============== 想要了解一個文化,最快的方式就是直接參與其中,自由軟體與開放源碼世界的「黑客文化」,當然也不例外。對黑客文化有興趣的朋友,不妨逛一下「鮮肉站」(http://freshmeat.net/)、「源碼爐」(http://sourceforge.net/),很容易便發現,每天都有好幾打的自由軟體與開放源碼程式問世及更新。想要自娛娛人、或是練功精進的朋友,應該可以找到好的切入點,再到 http://rpmfind.net/ 網站找尋源碼包裝檔案來研究,進一步了解更多系統運作的秘密。 Use the Force, Read the Source! May the Source Be with You!