維護者腳本是在套件安裝、升級和移除時,由套件管理系統執行的腳本。
這些腳本是控制資訊檔案 (control information file),包含 preinst、postinst、prerm、postrm。這四個檔案必須是可執行檔,若是以腳本傳寫 (建議),則必須以 #! 開頭。維護者腳本應該要是具備可讀性且可由任何人執行的,但不能是全局可寫的。
套件管理系統會檢查腳本的結束回傳值。當一個腳本發生錯誤時,他應該要回傳一個非零的狀態,如此一來套件管理系統才可以停止安裝流程。對於一個腳本來說,這代表者你在多數時候是需要使用 set -e 來強迫腳本在發生錯誤時停止運行,當然,在正常結束時回傳為零的狀態也是同等重要。
進一步的,若 postinst 腳本中有使用 debconf 與使用者互動,則應該於套件中多放置 config 腳本做為控制資訊檔案。
當一個套件升級時,新舊套件的腳本會在升級過程中被呼叫,如果套件腳本打算進行複雜的操作,這是一個需要特別注意的重點,你可能需要在腳本中檢查呼叫參數。
簡單來說,preinst 腳本會在套件被解封裝前呼叫,postinst 則是在解封裝之後;prerm 在(舊版本)套件被移除前呼叫,而 postrm 則是在被移除之後。
在撰寫腳本時,腳本不應該使用絕對路徑來呼叫程式。在安裝過程開始前,套件管理系統會透過 ldconfig、start-stop-daemon 和 update-rc.d 在路徑 (PATH) 環境變數中尋找可用程式,之後在腳本中便可以直接用名字呼叫程式。維護者腳本也不該重設 PATH 環境變數,但可以選在前 (prepend) 或後 (append) 增加 PATH 路徑,這個變動需要考慮會影響到所有的腳本。
一個維護者腳本的錯誤回復流程必須要是等幂(idempotency)的。這代表在腳本在安裝成功後再被安裝一次不應該造成任何的問題或傷害,而只是確定所有東西都符合預想。然而,當第一次執行失敗或在中途被終止時,第二次呼叫應僅是接著完成第一次呼叫尚未完成的部分,並在最後回傳正常結束。
維護者腳本的運行沒辦法總是保證有控制終端 (controlling terminal),也不保證總是有能力可以跟使用者互動。若腳本發現無法與使用者互動時,則應該要改採用非交互的方式繼續運作。腳本使用符合 Debian Configuration Management Specification 規範的程式與使用者交互時,可以假設這些程式皆有非交互的行為
有一些重要的資訊需要使用者輸入時,開發者可能沒有填寫預設的提示答案而導致腳本在沒有控制終端時被終止。這個狀況應該盡可能的避免,因為這在自動安裝或不參與的安裝過程中會被使用者視為套件的臭蟲。
每一個維護者腳本應該回傳零做為成功執行的結束狀態,或以非零值代表執行錯誤。套件管理系統會利用這個回傳結果來決定接下來的執行流程。
本節概述維護者腳本被執行的時機點以及可視的檔案狀態。腳本名稱前包含 new- 的代表是在安裝、升級或降級時的新版本套件腳本;腳本名稱前包含 old- 的則代表是在安裝、升級或降級時的舊版本套件腳本
preinst 腳本會以下列方式被呼叫
new-preinst install
new-preinst install old-version
new-preinst upgrade old-version
preinst 腳本是在套件上未解封裝 (unpack) 前被呼叫,因此不能依賴任何包含在套件內的檔案。此時只能假設 essential 套件和相依 (Pre-Depends) 的套件是可見的。相依套件只會被設定一次,但在執行 preinst 套件時,若相依套件的舊版本已經曾經設定過且未被移除過,則此時這些套件的狀態可能是 Unpacked 或 Half-Configured 狀態。
old-preinst abort-upgrade new-version
在處理升級錯誤時被呼叫,此時是新套件已經解封裝完成,但 postrm upgrade 操作發生錯誤。這個時候解封裝的套件可能部分來自於新本版,部分來自於舊版本套件,因此腳本不應該使用任何包含於套件內的檔案。此時的套件相依性也可能不可見,相依套件至少會處於 Unpacked 狀態,如同上述,為依不同的是,若升級相依失敗,則可能會處於 Half-Installed 狀態。
poinst 腳本會以下列方式被呼叫
postinst configure most-recently-configured-version
此時包含在套件內的所有檔案都已經被解封裝。所有套件相依至少都會處於 Unpacked 狀態,若沒有任何的循環相一問題,則所有相依套件都會被設定完成。
old-postinst abort-upgrade new-version
conflictor's-postinst abort-remove in-favour package new-version
postinst abort-remove deconfigured's-postinst abort-deconfigure in-favour failed-install-package version [removing conflicting-package version]
此時套件內的所有檔案都已經被解封裝。所有套件相依至少會在 Half-Installed 狀態,並且已經被設定好且沒有移除。在某些錯誤下有機會相依可能沒有被正確設定或是完整的解封裝,postinst 腳本應該仍嘗試執行相依套件提供的功能,雖然相依套件處於非正常狀態下,但考慮到這些錯誤可能由相關的錯誤處理修復成功。若在最後相依套件功能確定無法提供,則終止 postisnt 動作會是一個好方法。
prerm 腳本會以下列方式被呼叫
prerm remove
old-prerm upgrade new-version
conflictor's-prerm remove in-favour package new-version
deconfigured's-prerm deconfigure in-favour package-being-installed version [removing conflicting-package version]
當套件的 prerm 被呼叫時,套件至少會處於 Half-Installed 狀態。所有的套件相依至少會處於 Half-Installed,曾被設定過且沒有被移除。若沒有任何的錯誤,所有的相依會至少處於 Unpacked,但 prerm 可能在各種的錯誤狀態下被呼叫,而此時相依性只有 Half-Installed。
new-prerm failed-upgrade old-version
在 prerm upgrade 執行失敗的時候被呼叫,此時新套件上未被解封裝,狀態和 preinst upgrade 相同。
postrm 腳本會以以下方式被呼叫
postrm remove
postrm purge
old-postrm upgrade new-version
disappear's-postrm disappear overwriter overwriter-version
postrm 腳本會在套件檔案已經被移除或被覆蓋時呼叫。當套件的 postrm 被呼叫時,套檢已經被解除設定 (deconfigured) 且處於 Unpacked 狀態。此時的操作不考慮套件的相依關係,因此,postrm 的操作應該只使用 essential 套件並優雅跳過 (graceful skip) 任此時不存在的相依操作。
new-postrm failed-upgrade old-version
當 postrm upgrade 操作失敗的時候被呼叫。新的套件已經被解封裝,但由於錯誤,仍應該只依靠 essential 套件和相依套件的功能。相依套件可能處於 Unpacked 或 Half-Configured 狀態,但已經被設定好且不會被移除。
new-postrm abort-install
new-postrm abort-install old-version
new-postrm abort-upgrade old-version
在 preinst 失敗時被呼叫,可以假設和 preinst 處於相同的狀態。
安裝/升級/複寫/消失 (disapper) 流程如下。在每一個狀況中,若發生了未敘述的嚴重錯誤,一般來說則以反序並帶不同參數呼叫維護者腳本。 以下為例外狀況:
old-prerm upgrade new-version
new-prerm failed-upgrade old-version
old-postinst abort-upgrade new-version