简介
当我尝试将 cron 包重命名为 cronie 时,遇到了一些严重的问题,并且没有找到解决这些问题的文档(如果您知道,请更正我)。我找到的所有内容是 https://en.opensuse.net.cn/Package_Dependencies#Renaming_a_package :我应该使用经典的 Provides: 和 Obsoletes:。但是经过一些调查后,我发现此解决方案无法涵盖软件包包含 init 脚本和守护进程的情况。
正确软件包更新示例
首先,让我们从正常的更新开始(例如,从 cron 4.1 更新到 cron 4.2)。在 (openSUSE) 中处理包含 init 脚本(和 sysconfig)的软件包的更新过程的常用方法是在 spec 文件中的 %post %preun 和 %postun 中使用宏。这些宏会在需要时重启/停止/插入服务,清理服务或填充 syconfig 文件。检查宏内容的快速方法是使用 rpm --eval(例如:rpm --eval %stop_on_removal)。但我建议使用 vim /usr/lib/rpm/suse_macros 并在此处检查 SUSE 特定的宏(或使用 /usr/lib/rpm/ 目录中的其他文件)。rpm --eval 可能会扩展宏,这有时会令人困惑。
...
%post
%{fillup_and_insserv -y cron}
...
%preun
%stop_on_removal cron
%postun
%restart_on_update cron
%insserv_cleanup
...
在上面的示例中(在更新过程中),旧版本的 %postun 和 %preun 脚本会被调用,新版本的 %post 和 %preun 脚本会被调用。请查看此链接以了解脚本执行顺序 :https://en.opensuse.net.cn/Packaging/Scriptlet_Snippets#Scriptlet_Ordering 但现在让我们看看更新过程是如何工作的
|
|
更新:一个过程,脚本可以相互交互
|
来自 cron 4.2 包 |
| 1、安装 cron 4.2 包 |
|
| 2、%post |
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab
fillup_and_insserv 中的 insserv 不会插入服务(我们正在进行更新,所以 FIRST_ARG=2),因此 cron 的 rc 状态保持不变
|
|
来自 cron 4.1 包 |
| 3、%preun |
%stop_on_removal cron
我们不在 stop_on_removal 内部停止进程,因为我们正在进行更新
|
| 4、卸载 cron 4.1 包 |
|
| 5、%postun |
%restart_on_update cron
%{insserv_cleanup}
我们在 %restart_on_update 中调用 /etc/init.d/cron try-restart,因此守护进程将被重启
|
|
软件包重命名
正如我提到的,您在重命名过程中可能会遇到一些问题,因为重命名不是更新!所有检查 FIRST_ARG ($1) 的宏都不起作用(例如 %stop_on_removal cron),有关 FIRST_ARG 的更多信息,请参见: https://en.opensuse.net.cn/Packaging/Scriptlet_Snippets#Syntax 这会导致
- rc 状态在安装后不处于正确状态
- 进程(守护进程)状态在安装后不处于正确状态
为什么相同的 spec 文件不起作用
让我们看看当我们尝试运行 "zypper dup"、"zypper in" 或 "rpm -U" cronie-1.4.4xx.rpm(它过时了 cron 4.1)并使用标准的 Provides/Obsoletes 方法时会发生什么
Name: cronie
Version: 1.4.4
...
Provides: cron = 4.2
Obsoletes: cron <= 4.1
|
|
重命名:一个过程,脚本可以相互交互
|
来自 cronie 1.4.4 包 |
| 1、安装 cronie 1.4.4 包 |
|
| 2、%post |
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab
我们不是在进行更新,只有名为 "cronie" 的一个包会在完成此操作后保留,所以 FIRST_ARG=1,因此在 %fillup_and_insserv 内部,我们调用 "insserv /etc/init.d/cron",它通过读取此 init 脚本的注释头来启用和安装 init 脚本(请参阅 man insserv)
|
|
来自 cron 4.1 包 |
| 3、%preun |
%stop_on_removal cron
我们正在卸载,所以 FIRST_ARG" = "0",我们调用 /etc/init.d/cron stop,因此守护进程将被停止
|
| 4、卸载 cron 4.1 包 |
|
| 5、%postun |
%restart_on_update cron
%{insserv_cleanup}
实际上,此宏什么也不做,cron 包已被删除
|
|
所以最后,我们总是以停止的守护进程和开启的服务结束
修改后的 spec 文件以涵盖所有问题
那么如何处理重命名?有很多解决方案,可能下面的不是最好的,但可能会有所帮助。主要思想是使用主包 cronie 和子包 cron,子包 cron 具有比旧 cron 更新的版本,因此它将执行更新。所以基本上我们将执行
更新 cron 4.1 -> 4.2 + 安装 cronie
在更新 cron 过程中,我们将保存配置并在 cronie 的安装阶段使用它们。
另请查看 有用 的有关配置文件和更新过程的信息。
扩充 spec 文件
Name: cronie
Version: 1.4.4
PreReq: cron
Conflicts: cron <= 4.1
..
%package -n cron
Version: 4.2
Requires: %{name} = %{cronie_version}-%{release}
%pre -n cron
# check if we are doing "ugly" update from old 4.1 vixie-cron
check_cron_mail_feature=`/usr/sbin/cron --help 2>&1 | /usr/bin/grep mail`
# vixie-cron 4.1 doesn't contain mail fature
if [ -e /usr/sbin/cron -a "${check_cron_mail_feature}" == "" ]; then
# save configs for cronie post-install phase
touch /var/run/update_from_old_cron
for conf in %{cron_configs}
do
%__mv "$conf" "$conf.bk" ||:
done
fi
%pre
if [ -e /var/run/update_from_old_cron ]; then
# restore configs
for conf in %{cron_configs}
do
%__mv "$conf.bk" "$conf" ||:
done
fi
%post
echo "post-install of cronie package"
# when we are doing rename then we pretend update with set 2
if [ -e /var/run/update_from_old_cron ]; then
set 2
%restart_on_update cron
# in %postun restart_on_update call try-restart but we don't have init script in this phase when
# we are doing "ugly" update, but don't panic, it produces only warning to stderr
echo "Please ignore message about missing init script(from postun) - when occurs, we will ....."
%__mv /etc/init.d/cron /etc/init.d/cron.bk ||:
%__ln_s /bin/true /etc/init.d/cron
fi
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab
%preun
%stop_on_removal cron
%postun
%restart_on_update cron
%insserv_cleanup
%posttrans
echo "posttrans of cronie"
if [ -e /var/run/update_from_old_cron ]; then
%{__rm} /var/run/update_from_old_cron ||:
%__mv /etc/init.d/cron.bk /etc/init.d/cron ||:
fi
rpm -U 和 zypper 的不同行为
假设:您已经在系统上安装了 cron-4.1,并且使用了 上面的 spec 代码片段来构建 cronie-1.4.4 和子包 cron-4.2,然后您调用
rpm -U cron-4.2xx.rpm cronie-1.4.4.rpm 查看 安装步骤
zypper in cron-4.2xx.rpm cronie-1.4.4.rpm 查看 安装步骤(也适用于 zypper dup -r reponum 的相同行为),在这种情况下,zypper 使用 rpm -U 将过程拆分为两部分,这可能会令人烦恼,因为脚本不会像在前面提到的使用纯 rpm -U 的情况下那样按相同的顺序执行(要检查 zypper 在执行 "zypper in cron-4.2xx.rpm cronie-1.4.4.rpm" 时在做什么,我使用了 tail -f /var/log/zypper.log | grep "Executing 'rpm'")
..(start_program):221 Executing 'rpm' '--root' '/' '--dbpath' '/var/lib/rpm' '-U' '--percent' '--force' '--nodeps' ... cron-4.2-242.1.x86_64.rpm'
..(start_program):221 Executing 'rpm' '--root' '/' '--dbpath' '/var/lib/rpm' '-U' '--percent' '--force' '--nodeps' ... cronie-1.4.4-242.1.x86_64.rpm'
使用 zypper in cron*.rpm 修改后的 spec 文件进行重命名过程
|
|
第一个过程:zypper update cron 从 4.1 更新到 4.2 使用 rpm -U --force --nodeps cron-4.2-xxx.rpm
|
来自 cron 4.2 包 |
| 1、%pre |
%pre -n cron
# check if we are doing "ugly" update from old 4.1 vixie-cron
check_cron_mail_feature=`/usr/sbin/cron --help 2>&1 | /usr/bin/grep mail`
# vixie-cron 4.1 doesn't contain mail fature
if [ -e /usr/sbin/cron -a "${check_cron_mail_feature}" == "" ]; then
# save information about update from old cron for other scriptles
touch /var/run/update_from_old_cron
# save configs defined by %cron_cofigs
for conf in %{cron_configs}
do
%__mv "$conf" "$conf.bk" ||:
done
fi
|
| 2、安装 cron 4.2 包 |
|
|
来自 cron 4.1 包 |
| 3、%preun |
%preun
# stop_on_removal macro do nothing, we are doing update
%stop_on_removal cron
|
| 4、卸载 cron 4.1 包 |
cron 4.2 仅包含 readme,因此在卸载 cron 4.1 后,我们丢失了所有 cron 文件(在配置的情况下并非如此)
|
| 5、%postun |
%postun
# restart on update call /etc/init.d/cron try-restart, but we lost init script so we got some message to stderr
# but this doesn't break update
%restart_on_update cron
%insserv_cleanup
|
|
|
|
第二个过程:zypper install cronie 包使用 rpm -U --force --nodeps cronie-1.4.4-xxx.rpm
|
来自 cronie 1.4.4 包 |
| 6、%pre |
%pre
# check if we are doing update from old cron, we touch /var/run/update_from_old_cron in %pre scriptlet of cron
4.2
if [ -e /var/run/update_from_old_cron ]; then
# if yes, restore configs before installing cronie
for conf in %{cron_configs}
do
%__mv "$conf.bk" "$conf" ||:
done
fi
|
| 7、安装 cronie 1.4.4 包 |
|
| 8、%post |
%post
# when we are doing rename from old cron
if [ -e /var/run/update_from_old_cron ]; then
#then we pretend update with "set 2", %restart_on_update and %fillup_and_insserv macros will do
#FIRST_ARG=2
set 2
#we doesn't do %restart_on_update in postun of old cron 4.1 because we lost init script so
#we call this macro here
%restart_on_update cron
#some message according to err message rom postun of old cron 4.1
echo "Please ignore message about missing init script(from postun) - when occurs, we will install ..."
#this is useless for case when we are using zypper, but prevent restart cron daemon twice when we
#are instrad of zypper in using pure rpm -U
%__mv /etc/init.d/cron /etc/init.d/cron.bk ||:
%__ln_s /bin/true /etc/init.d/cron
fi
#few lines above we call set 2 so we pretend update ($1=2)
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab
|
| 9、%posttrans |
%posttrans
echo "posttrans of cronie"
if [ -e /var/run/update_from_old_cron ]; then
#clean up temporary file
%{__rm} /var/run/update_from_old_cron ||:
#again this is useless, in case of using zypper, but this lines are needed when we are using rpm -U to
#prevent restart cron twice
%__mv /etc/init.d/cron.bk /etc/init.d/cron ||:
fi
|
|
使用 rpm -U 修改后的 spec 文件进行重命名过程
|
|
rpm -U 在一个过程中运行
|
来自 cron 4.2 包 |
| 1、%pre |
%pre -n cron
# check if we are doing "ugly" update from old 4.1 vixie-cron
check_cron_mail_feature=`/usr/sbin/cron --help 2>&1 | /usr/bin/grep mail`
# vixie-cron 4.1 doesn't contain mail fature
if [ -e /usr/sbin/cron -a "${check_cron_mail_feature}" == "" ]; then
# save information about update from old cron for other scriptles
touch /var/run/update_from_old_cron
# save configs defined by %cron_cofigs
for conf in %{cron_configs}
do
%__mv "$conf" "$conf.bk" ||:
done
fi
|
| 2、安装 cron 4.2 包 |
|
|
来自 cronie 1.4.4 包 |
| 3、pre |
%pre
# check if we are doing update from old cron, we already made touch /var/run/update_from_old_cron in
# %pre scriptlet of cron 4.2
if [ -e /var/run/update_from_old_cron ]; then
# if yes, restore configs before installing cronie
for conf in %{cron_configs}
do
%__mv "$conf.bk" "$conf" ||:
done
fi
|
| 4、安装 cronie 1.4.4 包 |
|
| 5、%post |
%post
# when we are doing rename from old cron
if [ -e /var/run/update_from_old_cron ]; then
#we pretend update with "set 2", %restart_on_update and %fillup_and_insserv macros
#will use FIRST_ARG=2 then
set 2
#we call restart_on_update here because if we used "zypper in", macro %restart_on_update called
#from %postun of old cron can't use init script (init script doesn't exist) see and %postun scriptlet
%restart_on_update cron
#this echo is useful when we use "zypper in"
echo "Please ignore message about missing init script(from postun) - when occurs, ..."
#we restart cron daemon on few lines above, so we have to prevent executing /etc/init.d/cron in
#%postun scriptlet of old cron 4.1 (see step 8)
%__mv /etc/init.d/cron /etc/init.d/cron.bk ||:
%__ln_s /bin/true /etc/init.d/cron
fi
#few lines above we call set 2 so we pretend update ($1=2)
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab
|
|
来自 cron 4.1 包 |
| 6、%preun |
%preun
# stop_on_removal macro do nothing, we are doing update
%stop_on_removal cron
|
| 7、卸载 cron 4.1 包 |
|
| 8、%postun |
%postun
#restart on update call /etc/init.d/cron try-restart, /etc/init.d/cron is only link to /bin/true so
#practically we do nothing, we called restart_on_update in %post of cronie (see step 5)
%restart_on_update cron
%insserv_cleanup
|
|
来自 cronie 1.4.4 包 |
| 9、posttrans |
%posttrans
#we are doing clen up here
if [ -e /var/run/update_from_old_cron ]; then
#drop temporary file
%{__rm} /var/run/update_from_old_cron ||:
#and move init script back
%__mv /etc/init.d/cron.bk /etc/init.d/cron ||:
fi
|
|