RPM 是一種廣泛用于交付 Linux 軟件的工具; 用戶可以輕松地安裝用 RPM 打包的產(chǎn)品。在本文(該系列文章的第 2 篇)中,Dan 說明了在不具備 root 權(quán)限的情況下如何對軟件進(jìn)行打包,在不做更改的情況下如何處理不在 Linux 上構(gòu)建的軟件,以及如何分發(fā)您的工作結(jié)果。 當(dāng)在用戶機(jī)器上安裝或卸載程序時,能夠執(zhí)行命令將是很有用的。 例如,您可能需要編輯一個系統(tǒng)配置文件以啟用新的服務(wù),或者需要定義一個新用戶以擁有正在安裝的程序的所有權(quán)。 為了最有效地使用本文中的建議和示例,請先閱讀本系列文章的 第 1 部分和 第 2 部分, 它們分別向您演示了如何使用 RPM 以及如何分發(fā)您的產(chǎn)品。 安裝和卸載腳本的工作原理 安裝和卸載腳本看起來很簡單,但它們工作原理中的一些意外可能會引起大問題。 這里是一些基本信息。可以將下列四節(jié)中的任意一個添加到 .spec 文件, 它列出了在您的包安裝期間各個點(diǎn)上運(yùn)行的 shell 腳本: - %pre
- 在安裝包之前運(yùn)行
- %post
- 在安裝包之后運(yùn)行
- %preun
- 在卸載包之前運(yùn)行
- %postun
- 在卸載包之后運(yùn)行
尤其要注意 %install 與這些節(jié)之間的差異。構(gòu)建 RPM 時, %install 在開發(fā)機(jī)器上運(yùn)行;它應(yīng)該將產(chǎn)品安裝在開發(fā)機(jī)器上或安裝到一個構(gòu)建根目錄中。 另一方面,這些節(jié)指定當(dāng)用戶正在安裝或卸載您的 RPM 包時將在 用戶的機(jī)器上運(yùn)行什么。 這里有一個示例,是在前文基礎(chǔ)上建立的。我們要使用 install-info 將 GNU indent 的 info 文件添加到目錄中。 清單 1. indent-4.spec # Simplistic example of install scripts - do not use Summary: GNU indent Name: indent Version: 2.2.6 Release: 4 Source0: %{name}-%{version}.tar.gz License: GPL Group: Development/Tools BuildRoot: %{_builddir}/%{name}-root %description The GNU indent program reformats C code to any of a variety of formatting standards, or you can define your own. %prep %setup -q %build ./configure make %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %post if [ -x /sbin/install-info ]; then /sbin/install-info /usr/local/info/indent.info /usr/local/info/dir fi %preun if [ -x /sbin/install-info ]; then /sbin/install-info --delete /usr/local/info/indent.info /usr/local/info/dir fi %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) /usr/local/bin/indent %doc /usr/local/info/indent.info %doc %attr(0444,root,root) /usr/local/man/man1/indent.1 %doc COPYING AUTHORS README NEWS | 請注意,在嘗試使用 install-info 工具之前,首先對它進(jìn)行檢查。我們不希望只是因?yàn)槲覀儾荒芴峁┲廉a(chǎn)品文檔的鏈接而使安裝失敗。 但也有可能在某種情況下您希望安裝過程失敗。一種好的技術(shù)是使用 %pre 腳本來檢查安裝前提條件,它們比 RPM 可以直接支持的更復(fù)雜。 如果不符合前提條件,那么腳本以非零狀態(tài)退出,而且 RPM 不會繼續(xù)安裝。 另外請注意,我們必須小心地使用卸載腳本來撤銷安裝腳本。
沒有那么簡單:升級使每件事情都變得復(fù)雜 現(xiàn)在,讓我們著手升級。如果用戶只安裝和刪除您自己的包,那么前面節(jié)中的指令將正常工作;但在升級期間,它們會完全失效。 以下是 RPM 如何執(zhí)行升級: - 運(yùn)行新包的 %pre
- 安裝新文件
- 運(yùn)行新包的 %post
- 運(yùn)行舊包的 %preun
- 刪除新文件未覆蓋的所有舊文件
- 運(yùn)行舊包的 %postun
如果我們使用前面的示例來升級,那么 RPM 最后將運(yùn)行 %postun 腳本, 它將除去我們在安裝腳本中所做的所有工作!使用 RPM 的一般開發(fā)人員可能不會想到這一點(diǎn)。 我不會嘗試解釋其原因,只是解釋您必須為此做點(diǎn)什么。 相當(dāng)幸運(yùn)的是,在一定程度上,腳本有一種方法可以告之是否正在安裝、刪除或升級包。每個腳本都被傳遞單一命令行參數(shù) ― 一個數(shù)字。 這應(yīng)該告訴腳本 在當(dāng)前包完成安裝或卸載之后將安裝多少個包的副本。 只查看在各種情況下傳遞的值或許更容易,而不是嘗試計(jì)算它。 這里是在安裝期間傳遞的實(shí)際值: - 運(yùn)行新包的 %pre (1)
- 安裝新文件
- 運(yùn)行新包的 %post (1)
這里是在升級期間傳遞的值: - 運(yùn)行新包的 %pre (2)
- 安裝新文件
- 運(yùn)行新包的 %post (2)
- 運(yùn)行舊包的 %preun (1)
- 刪除新文件未覆蓋的任何舊文件
- 運(yùn)行舊包的 %postun (1)
這里是在刪除期間傳遞的值: - 運(yùn)行舊包的 %preun (0)
- 刪除文件
- 運(yùn)行舊包的 %postun (0)
可以通過將類似下例的一些東西添加到您的包中來自己測試它。然后創(chuàng)建一個帶稍高發(fā)行版號的新包,安裝第一個,然后升級到第二個,最后卸載它,以查看所有可能性。當(dāng)然,在信任的公共社區(qū)上發(fā)布任何 RPM 之前,您總是要對它進(jìn)行幾次這樣的嘗試。 清單 2. 腳本執(zhí)行的測試順序和參數(shù) %pre echo This is pre for %{version}-%{release}: arg=$1 %post echo This is post for %{version}-%{release}: arg=$1 %preun echo This is preun for %{version}-%{release}: arg=$1 %postun echo This is postun for %{version}-%{release}: arg=$1 | 這里是另一個示例,這次正確地處理升級過程: 清單 3. indent-5.spec Summary: GNU indent Name: indent Version: 2.2.6 Release: 5 Source0: %{name}-%{version}.tar.gz License: GPL Group: Development/Tools BuildRoot: %{_builddir}/%{name}-root %description The GNU indent program reformats C code to any of a variety of formatting standards, or you can define your own. %prep %setup -q %build ./configure make %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %post if [ "$1" = "1" ] ; then # first install if [ -x /sbin/install-info ]; then /sbin/install-info /usr/local/info/indent.info /usr/local/info/dir fi fi %preun if [ "$1" = "0" ] ; then # last uninstall if [ -x /sbin/install-info ]; then /sbin/install-info --delete /usr/local/info/indent.info /usr/local/info/dir fi fi %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) /usr/local/bin/indent %doc /usr/local/info/indent.info %doc %attr(0444,root,root) /usr/local/man/man1/indent.1 %doc COPYING AUTHORS README NEWS | 現(xiàn)在,僅當(dāng)完全刪除這個包時才會除去 info 鏈接。
觸發(fā)器 — 在安裝或卸載其它包時運(yùn)行腳本 假設(shè)在安裝或卸載 其它包時要運(yùn)行您包中的一些代碼。 可以用 觸發(fā)器(trigger)腳本來完成這一任務(wù)。 您為什么要這樣做?通常是因?yàn)槟陌褂靡粋€或多個其它包的服務(wù),或者提供服務(wù)給一個或多個其它包。 這里有一個示例。假設(shè)您正在為 Emacs 和 Xemacs 編輯器打包一個極好的附加工具。它可以與其中任何一個或兩個編輯器一起工作,但根據(jù)所安裝的編輯器,需要做一些少量的配置。 安裝時,可以對 Emacs 和 Xemacs 進(jìn)行測試,并配置您的工具以使可用編輯器可以訪問它。 但是,如果用戶稍后安裝 Xemacs,那么會發(fā)生什么情況呢?您的工具在 Xemacs 中不可用,除非用戶卸載并重新安裝您的工具。 如果您的包可以告訴 RPM,“讓我知道是否安裝了 Xemacs”,這是否會更好呢? 這是觸發(fā)器腳本的思想??梢詫⑺砑拥?.spec 文件中: 清單 4. 觸發(fā)器示例 %triggerin -- emacs # Insert code here to run if your package is already installed, # then emacs is installed, # OR if emacs is already installed, then your package is installed %triggerin -- xemacs # Insert code here to run if your package is already installed, # then xemacs is installed, # OR if xemacs is already installed, then your package is installed %triggerun -- emacs # insert code here to run if your package is already installed, # then emacs is uninstalled %triggerun -- xemacs # insert code here to run if your package is already installed, # then xemacs is uninstalled %postun # Insert code here to run if your package is uninstalled | 觸發(fā)器腳本被傳遞了 兩個參數(shù)。第一個參數(shù)是當(dāng)觸發(fā)器腳本完成運(yùn)行時將安裝的 您的包的實(shí)例數(shù)。第二個參數(shù)是當(dāng)觸發(fā)器腳本完成運(yùn)行時將安裝的 要觸發(fā)的包的實(shí)例數(shù)。 這里是 RPM 升級期間腳本執(zhí)行和文件安裝及卸載的完整順序,它來自 RPM 分發(fā)版中的 triggers 文件: 清單 5. 腳本順序 new-%pre for new version of package being installed ... (all new files are installed) new-%post for new version of package being installed any-%triggerin (%triggerin from other packages set off by new install) new-%triggerin old-%triggerun any-%triggerun (%triggerun from other packages set off by old uninstall) old-%preun for old version of package being removed ... (all old files are removed) old-%postun for old version of package being removed old-%triggerpostun any-%triggerpostun (%triggerpostun from other packages set off by old un install) |
高級腳本編制 備用解釋器 通常,所有安裝時腳本和觸發(fā)器腳本都是使用 /bin/sh shell 程序運(yùn)行的。如果您更喜歡另一個腳本語言,比方說 Perl,那么可以通過將 -p interpreter 添加到腳本行來告訴 RPM 應(yīng)該使用另一種解釋器運(yùn)行您的腳本。例如: 清單 6. 備用解釋器示例 %post -p /usr/bin/perl # Perl script here %triggerun -p /usr/bin/perl -- xemacs # Another Perl script here | 請注意,這 不適用于 RPM 的構(gòu)建時腳本,如 %install 。 RPM 變量 RPM 在將 RPM 變量存儲到 RPM 包文件之前先在您的腳本中擴(kuò)充它們,有時候這是有用的。 例如,可以在 .spec 文件頂部附近定義您自己的參數(shù),然后在整個 .spec 文件 ― 甚至在您的腳本中使用 %{variable_name} 引用它們: 清單 7. RPM 變量示例 ... %define foo_dir /usr/lib/foo ... %install cp install.time.message $RPM_BUILD_ROOT/%{foo_dir} %files %{foo_dir}/install.time.message %post /bin/cat %{foo_dir}/install.time.message |
要避免的事情 您可能會在安裝時試圖做一些事情,但結(jié)果會證明這是一個壞主意。 例如,與用戶交互的任何嘗試或許不能很好地工作。RPM 被設(shè)計(jì)成在無需用戶出現(xiàn)的情況下允許進(jìn)行批處理安裝。 如果在安裝期間 RPM 包停下來并提出問題,而沒有人看到這個問題,那么安裝將一直掛起。 您可能要避免的另一件事情是啟動任何服務(wù)。在完整安裝期間,您不能確定程序所需的每樣?xùn)|西是否已經(jīng)在那里(例如,可能還沒有任何網(wǎng)絡(luò));另外,如果在完整操作系統(tǒng)安裝期間每個 RPM 服務(wù)都嘗試啟動,那么整個安裝過程大概會花很長時間。這種情況下您可以做的就是打印消息,告訴用戶有關(guān)任何所需配置或需要啟動的服務(wù)的信息。 如果用戶正在手工安裝您的 RPM 包,那么他或她將看到這些消息;如果它是較大批處理安裝的一部分,那么它不會損害任何東西,機(jī)器幾乎肯定在結(jié)束時重新引導(dǎo),啟動您的服務(wù)。
要記住的事情 如果您的包安裝了 init 腳本,則可以使用 chkconfig 來安排將在適當(dāng)運(yùn)行級別上啟動和停止的服務(wù)。雖然可以通過將必需的符號鏈接直接安裝為包的一部分來實(shí)現(xiàn)同一件事情, 但要使它們恢復(fù)正常會有很多麻煩,您可能寧愿使用 chkconfig 。 為了安全起見,許多服務(wù)在一個特定的用戶標(biāo)識下運(yùn)行;如果您的服務(wù)是這樣的話,當(dāng)系統(tǒng)上不存在該用戶時,您需要在系統(tǒng)上創(chuàng)建它。 如果您的包安裝了任何 GNU info 文件,那么在 Info 目錄中將看不到它們,除非在安裝時使用 install-info 工具添加它們。 當(dāng)然,在卸載之前,必須試圖停止您的包可能正在運(yùn)行的任何服務(wù)(但如果服務(wù)不在運(yùn)行,請確保卸載不會失?。?/p> 當(dāng)然,在卸載時,應(yīng)該將您在安裝時可能對系統(tǒng)做的大多數(shù)更改恢復(fù)成原來狀態(tài)。 但稍微考慮一下您的操作;例如,卸載 RPM 包時不應(yīng)該意外地刪除任何用戶創(chuàng)建的文件。 所以,請不要嘗試除去用戶標(biāo)識或刪除整個目錄樹,這樣可能會好一些。
|