openSUSE:Cron rename

Jump to: navigation, search

Intro

When I tried rename package cron to cronie I meet with few serious issues and I didn't found docs how to resolve them (If you got it please correct me). All I have found is http://en.opensuse.org/Package_Dependencies#Renaming_a_package : I should use classical Provides: and Obsoletes:. But after little investigation I found, that this solution doesn't cover case when package contain init script with daemon.

Example of proper package update

Firstly lets start with normal update (e.g. update from cron 4.1 to cron 4.2). Common way in (openSUSE) how to deal with update process in package which contain init script (and sysconfig) is using macros in %post %preun and %postun in spec file. These macros restart/stop/insert service if needed, clean up services or fill up syconfig files. Common and quick way how to check content of macro, is using rpm --eval (e.g. :rpm --eval %stop_on_removal). But I recommend using vim /usr/lib/rpm/suse_macros and check here SUSE specific macro (or use other file from /usr/lib/rpm/ dir). rpm --eval could expand macro, which is realy sometimes confusing.

...
%post
%{fillup_and_insserv -y cron}
...
%preun
%stop_on_removal cron

%postun
%restart_on_update cron
%insserv_cleanup
...

In example above (during update process) %postun and %preun scriptlets are called from older version, %post and %preun are called from new version of cron. Check this link to see scriptlet ordering :http://en.opensuse.org/Packaging/Scriptlet_Snippets#Scriptlet_Ordering But now lets see how update process works:

update : one process when scriptlets could interact between packages
Scriptlet from
cron 4.2 package
1, install package cron 4.2
2, %post
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab

insserv from fillup_and_insserv doesn't insert service (we are doing update so FIRST_ARG=2), so rc states of cron stayed untouched

Scriptlets from
cron 4.1 package
3, %preun
%stop_on_removal cron

we doesn't stop process inside stop_on_removal because we are doing update

4, uninstall package cron 4.1
5, %postun
%restart_on_update cron 
%{insserv_cleanup}

we call /etc/init.d/cron try-restart in %restart_on_update, so daemon is restarted

Package rename

As I mention you could hit few issues during rename process because rename is not update ! All macros which check FIRST_ARG ($1) doesn't do the job (e.g. %stop_on_removal cron), see more info about FIRST_ARG : http://en.opensuse.org/Packaging/Scriptlet_Snippets#Syntax This leads to :

  • rc states are not in proper states after install
  • process (daemon) state is not in proper states after install

Why the same spec doesn't do the job

Lets see what happen when we try to run "zypper dup","zypper in" or "rpm -U" cronie-1.4.4xx.rpm (which obsoletes cron 4.1) and we use standard Provides/Obsoletes way:

Name: cronie 
Version: 1.4.4
...
Provides: cron = 4.2
Obsoletes: cron <= 4.1
rename : one process when scriptlets could interact between packages
Scriptlet from
cronie 1.4.4 package
1, install package cronie 1.4.4
2, %post
%{fillup_and_insserv -y cron}
%run_permissions
%verifyscript
%verify_permissions -e /etc/crontab -e /usr/bin/crontab

we are not doing update, only one package with name "cronie" will left after finishing this action, so FIRST_ARG=1 and so inside of %fillup_and_insserv we call "insserv /etc/init.d/cron" which enable and install init script by reading the comment header of this init script (see man insserv)

Scriptlets from
cron 4.1 package
3, %preun
%stop_on_removal cron

we are doing uninstall so FIRST_ARG" = "0" and we call /etc/init.d/cron stop, so daemon will be stopped

4, uninstall package cron 4.1
5, %postun
%restart_on_update cron 
%{insserv_cleanup}

practically this macro do nothing, package cron was removed

So at the end we finish always with stopped daemon and services turned on

Modified spec to cover all issues

So how to deal with rename ? There is a lot of solution and probably this bellow isn't the best, but could be helpful. The main idea is using main package cronie with subpackage cron, Subpackage cron have newer version than old cron so it will do update. So basically we will do :
update cron 4.1 -> 4.2 + install cronie
In update cron process we will save configs and we will use them during install phase of cronie.
Check also useful info about config files and update process.

Enlarging the spec file

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

Different behavior of rpm -U and zypper in

Lets imagine : you have installed cron-4.1 on system and you used snippets of spec above to build cronie-1.4.4 and supackage cron-4.2, then you call

  • rpm -U cron-4.2xx.rpm cronie-1.4.4.rpm check the install steps
  • zypper in cron-4.2xx.rpm cronie-1.4.4.rpm check the install steps (same behavior also for zypper dup -r reponum), in this case zypper split process to two parts using rpm -U, this could be annoying because scriptlets are not executed in the same order, like in case of using pure rpm -U mentioned in previous point (to check what is zypper doing (during "zypper in cron-4.2xx.rpm cronie-1.4.4.rpm") I used 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'

rename process with modified spec using zypper in cron*.rpm

first process : zypper update cron from 4.1 to 4.2 using rpm -U --force --nodeps cron-4.2-xxx.rpm
Scriptlet from
cron 4.2 package
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, install package cron 4.2

Scriptlets from
cron 4.1 package
3, %preun
%preun
# stop_on_removal macro do nothing, we are doing update
%stop_on_removal cron
4, uninstall package cron 4.1

cron 4.2 contain only readme so after uninstallin cron 4.1 we lost all cron files (not really true in case of configs)

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
second process : zypper install cronie package using rpm -U --force --nodeps cronie-1.4.4-xxx.rpm
Scriptlets from
cronie 1.4.4 package
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, install package 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

rename process with modified spec using rpm -U cron*.rpm

rpm -U runs in one process
Scriptlet from
cron 4.2 package
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, install package cron 4.2
Scriptlet from
cronie 1.4.4 package
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, install pacakge 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
Scriptlets from
cron 4.1 package
6, %preun
%preun
# stop_on_removal macro do nothing, we are doing update
%stop_on_removal cron
7, uninstall package 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
Scriptlet from
cronie 1.4.4 package
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