X

Cloud and Infrastructure Insights from the Systems Community in Germany

Postinstall and Postremove with Solaris 11 Packaging

Detlef Drewanz
Master Principal Sales Consultant

With the introduction of Oracle Solaris 11 and the Image Packaging System (IPS), the execution of scripts during the installation of software packages is no longer supported and possible. So the time for pre-/post-  install/-remove scripts has been over. Constantin discussed this already some years back in his Blog.

But still the discussion about executing scripts during package operations comes up from time to time. The question is what can be done to make changes to an Oracle Solaris system during the installation of a software package - especially, if changes have to be made to existing configuration files. ... and ... is the execution of such scripts really needed that much or is it just that we think we need to ? Typical examples are:

  1. Adding Apache Configuration file entries
  2. Adding entries to /etc/system
  3. Configuring the syslog.conf file
  4. Adding crontab entries

There are multiple options to handle these topics:

  • Decouple the package operations (place files into a filesystem) and the configuration task. Many do this by using a combination of IPS and puppet. Because puppet is included in Oracle Solaris 11.2, is should be easy to do for people how are knowledgeable about puppet. For new starters has Glynn written this nice HowTo about puppet and Oracle Solaris.
     
  • For the named examples 1.-3. there are ways to use the software self-assembly functionality, that exists for many services already in Solaris. In this case a collection of installed configuration snippets are used by software to build a working configuration when that system is booted, by the time the packaging operation completes, or at software runtime. The self-assembly directories are: For apache (1.) is this /etc/apache2/2.2/conf.d and for /etc/system (2.) is this /etc/system.d. For syslog there is the option to install the system/rsyslog package and then have /etc/rsyslog.d. For crontab there is no self-assembly, but since Oracle Solaris 11.3 there is the option to achieve the same result than crontab entries to just install a periodic SMF Service. So we see here that at least for the 4 examples we could solve the task without using system configuration scripts.
     
  • But finally there might be still examples, where puppet is not wanted or not in favor to be used or self-assembly is not possible and one is looking for a different general solution. In that case, running scripts after the installation or after the removal of packages is needed again. The following part of this Blog entry will show an example on how to do that with a combination of IPS and a SMF service.

Before diving into the details of such an IPS post service, there are some things to consider:

  • All changes to an installation should be able to be audited - so that one know and can record the changes.
  • All change operations need to be reversible, so it must be possible to "rewind" with a package removal.
  • What happens, if a script breaks or does only runs half ?
  • Package operations does not only happen in an active Boot Environment, but also in passive Boot Environments - like with "pkg -R /<some mountpoint> install <package with scripting>. In such a case ... while the package will be installed into /<some mountpoint> as root directory ... where does a post-script run against ?

The ips-duty Service

My suggestion is an ips-duty service, that I created as a global SMF Service. I provide here the examples. The service is responsible to run postinstall and postremove tasks, that are placed by several IPS packages. This SMF duty service checks after it's start or restart a directory (/var/lib/ips-duty/postaction) for postinstall-* and postremove-* executables. This is the way they are handled:

  • Install: A new postinstall-* is executed, if the package is installed into the active Boot Environment (BE). If the package has been installed into a passive BE, the postinstall-* is executed after the next reboot of the BE.
  • Install: All postinstall-* and postremove are copied into a cache area to keep track of changes, to check for duplicates and already executed operations, and to backup stuff that need to be executed, after the package has been already removed.
  • Uninstall: A postremove-* is executed from the cache, if the package that earlier installed it has been removed from the active BE.If the package that earlier installed the postremove has been removed from the passive BE, the postremove-* is executed after the next reboot of the BE
  • Uninstall: All postinstall-* and postremove-* are removed from the cache, if the counterpart does no longer exists in /var/lib/ips-duty/postaction.

With that, we can not only enable postinstall and postinstall in active BE's, but also in passive BE's. But it is also important to make sure to run every action only once and catch also package updates. In this case I use the bart(1M) utility in Oracle Solaris to identify news, updates and removals.

Ok. So which parts are needed ?

  1. A SMF manifest that will be used later to instantiate the service by restarting svc:/system/manifest-import.
  2. The SMF method script that is doing the real job.
  3. An IPS manifest to create the ips-duty package.
  4. A Test package to try things out.

1. The SMF Manifest:

<?xml version="1.0" ?>
<!DOCTYPE service_bundle
  SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type="manifest" name="site/ips-duty">
    <service version="1" type="service" name="site/ips-duty">
        <dependency restart_on="none" type="service"
            name="multi_user_dependency" grouping="require_all">
            <service_fmri value="svc:/milestone/multi-user"/>
        </dependency>
        <dependent restart_on="none"
            name="ips-duty_multi-user-server" grouping="optional_all">
            <service_fmri value="svc:/milestone/multi-user-server" />
        </dependent>
        <exec_method timeout_seconds="60" type="method" name="start"
                     exec="/opt/ips-duty/ips-duty.sh"/>
        <exec_method timeout_seconds="60" type="method" name="refresh"
                     exec="/opt/ips-duty/ips-duty.sh"/>
        <exec_method timeout_seconds="60" type="method" name="stop"
                     exec=":true"/>
        <property_group type="framework" name="startd">
                     <propval type="astring" name="duration" value="transient"/>
        </property_group>
        <instance enabled="true" name="default"> </instance>
    </service>
</service_bundle>

2. The SMF Method Script for the real Job

#!/bin/sh
# IPS Duty Script to detect and run postinstall-* and postremove-* scripts
# in /var/lib/ips-duty/postaction
###
# This is a prototype version. Support is not provided.
# Detlef.Drewanz@oracle.com, v0.1, 22.07.2017

SVC=ips-duty
DIR=/opt/${SVC}
LIBDIR=/var/lib/${SVC}
BART=/usr/bin/bart
# The directory, where the postinstall-* and postremove-* scripts are found
POSTACTION=${LIBDIR}/postaction
# The cache directory for executed postinstall and to be executed postremove
CACHE=${LIBDIR}/cache

# First create bart manifests of the postaction and the cache directory.
${BART} create -R ${POSTACTION} > ${LIBDIR}/postaction.manifest
${BART} create -R ${CACHE} > ${LIBDIR}/cache.manifest

# Now compare and check for todo's
${BART} compare -p -r ${DIR}/bart.rule ${LIBDIR}/cache.manifest ${LIBDIR}/postaction.manifest | \
while read filename todo;
do
  case "${todo}" in
    "add")
        # Ok, this entry is not in cache already
        # If it's a postinstall-*, do it now
        # Then copy the file always to cache
        INSTALL=`echo ${filename} | grep ^/postinstall-`
        if [ -n "${INSTALL}" ]; then
          ${POSTACTION}${filename}
        fi
        cp -p ${POSTACTION}${filename} ${CACHE}
        ;;
    "delete")
        # Ok, this entry is in cache, but no longer in postaction
        # If it's a postremove-*, do it now
        # Then remove the file always from cache
        REMOVE=`echo ${filename} | grep ^/postremove-`
        if [ -n "${REMOVE}" ]; then
          ${CACHE}${filename}
        fi
        rm ${CACHE}${filename}
        ;;
    *)
        # Ok, this entry is in cache already, but has been modified recently
        # So copy the file always to cache
        cp -p ${POSTACTION}${filename} ${CACHE}
        ;;
  esac
done
# Finally remove the bart manifests
rm ${LIBDIR}/postaction.manifest
rm ${LIBDIR}/cache.manifest

# Exit SMF
exit 0

The used ${DIR}/bart.rule file is very small and just contains one line "CHECK". That's it. So in general bart(1M) is here very helpful to identify new or removed stuff in postaction.

3. The IPS Manifest

set name=pkg.fmri value=ips-duty@1.0
set name=pkg.summary value="IPS Duty Service"
set name=pkg.description value="IPS Duty, Detlef.Drewanz@oracle.com"
set name=info.classification value="org.opensolaris.category.2008:System/Administration and Configuration"

depend type=require fmri=security/bart
file opt/ips-duty/site_manifest/ips-duty.xml path=lib/svc/manifest/site/ips-duty.xml owner=root group=bin mode=0644 \
     restart_fmri=svc:/system/manifest-import:default
dir  path=opt/ips-duty owner=root group=bin mode=0755
file opt/ips-duty/bart.rule path=opt/ips-duty/bart.rule owner=root group=bin mode=0644
file opt/ips-duty/ips-duty.sh path=opt/ips-duty/ips-duty.sh owner=root group=bin mode=0755
dir  path=var/lib/ips-duty owner=root group=bin mode=0755
dir  path=var/lib/ips-duty/cache owner=root group=bin mode=0755
dir  path=var/lib/ips-duty/postaction owner=root group=bin mode=0755
The IPS manifest shows how my IPS prototype directory looks like and I can create my package with the following commands:
pkgrepo -s /<my-site-pository-path> set publisher/prefix=<my-site-publisher-name>
pkgsend publish -s /<my-site-pository-path> \
                -d /<my-package-source-dir>/proto /<my-package-source-dir>/manifest.p5m

Due to the line with "restart_fmri=..." in the IPS manifest, the ips-duty service will be immediately available after the package has been installed and will be deleted, if the package has been removed.

Now we can use the ips-duty service for postinstall and postremove functionality from all of my created packages. All I need to do is to insert postinstall and postremove scripts into my packages, place them into the right directory and don't forget to use the "restart_fmri=..." actuator in my IPS manifest files. Those will make sure to restart the ips-duty service, after the files have been place into the right directory. Also make sure, that the post* files are named different in each package.

4. A Test package

Similar lines need to be added into the IPS manifest files of my packages to make use of ips-duty.

depend type=require fmri=ips-duty
file var-lib-ips-duty-postaction/postinstall-my-example \
    path=var/lib/ips-duty/postaction/postinstall-my-example owner=root \
    group=bin mode=0755 restart_fmri=svc:/site/ips-duty:default
file var-lib-ips-duty-postaction/postremove-my-example \
    path=var/lib/ips-duty/postaction/postremove-my-example owner=root \
    group=bin mode=0755 restart_fmri=svc:/site/ips-duty:default

Summary

This finally concludes this IPS-Duty example. As mentioned above, the first option is to use self-service assembly or puppet to configure systems together with package installations. But if someone is looking for a more general solution, this might be helpful.

Join the discussion

Comments ( 2 )
  • Pablo Thursday, May 24, 2018
    Hello, Detlef Drewanz,

    Is there no way to delete some files and kill some demons after the removal process without creating another service to do it?

    My problem is that I am creating a package that works perfectly during the installation process, but when I install it, it leaves some demons running and does not remove the user and the group I have created for the specific application.

    I would like to know if there is any way to remove them without creating a new service like ips-duty, it's a very smart solution, but I can't create more services on my customers' machines.

    Thank you in advance.

    Greetings,

    Pablo.
  • Detlef Drewanz Friday, May 25, 2018
    Hello Pablo,
    there is no way to run a script during "pkg uninstall" as this was with the former sysv package operations. ips operations are pure file operations in the file system + smf operations to activate, remove or restart services. To kill a daemon you would need to run a script. And that is the reason why I wrote this blog to get around this limitation. In your case you could create one ips-duty service (or call it as you wish) to take over the responsibility to run scripts that are place in a kind of execution queue. So no, I don't see an other way to do it.

    Regards
    Detlef
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.