Novell Home

How To Create RPMs and Init Scripts That Are Compatible On Both SUSE Linux and Red Hat Linux

Novell Cool Solutions: Feature
By Arun Singh, Bart Whiteley, Paul MacKay

Digg This - Slashdot This

Posted: 16 Nov 2004
 

SUSE by default is LSB compliant. Red Hat is LSB compliant if the lsb.rpm is installed (which is not by default). The best solution is to create LSB compliant rpms and init scripts and require lsb.rpm to be pre-installed on Red Hat systems. However, for various reasons sometimes this requirement cannot be enforced. To create rpms and init scripts that can be installed on default Red Hat and SUSE (LSB) systems requires knowing how to support both environments. This document outlines how to create such init scripts and rpms.

Overview

The Linux Standard Base (LSB) provides an extensive set of standards which promote increased application binary compatibility among LSB compliant Linux distributions. The LSB specification also details compatibility conventions for software package installation and system services management.

If you follow the LSB specification while developing your rpms and init scripts, your work to support SUSE(LSB) and LSB compliant systems is simplified and you only need to worry about whether or not you want to support SUSE specific features (e.g. YaST etc.). IBM and others have created an excellent guide that outlines the steps necessary for developing LSB compliant applications.

SUSE by default is LSB compliant. Red Hat is also LSB compliant if lsb.rpm is installed. However, by default Red Hat does not install lsb.rpm. Sometimes the requirement to have lsb.rpm installed on Red Hat systems cannot be enforced.

To create rpms and init scripts that can be used on default Red Hat (without lsb.rpm being installed) and SUSE (LSB) requires knowing how to support both environments. This document outlines how to create such init scripts and rpms.

Init Scripts

Red Hat's specific handling of init scripts is different than how SUSE (LSB) handles them. It is possible to code for both the Red Hat's (using chkconfig) way of doing things as well as to follow the LSB specification.

First: The Comment Block

Near the top of the init script, you need to include comment blocks such as the following:

# chkconfig: 345 85 60
# description: Novell Thingy is a fly-trap server.
# processname: lengine

### BEGIN INIT INFO
# Provides: lengine
# Required-Start: $local_fs $network $syslog
# Should-Start: nthd
# Required-Stop:
# Default-Start: 3 4 5
# Default-Stop: 0 1 2 6
# Short-Description: Novell Thingy
# Description: Novell Thingy Secure Fly Trap Server
### END INIT INFO

Refer to the following links for detailed information on chkconfig and INIT INFO:

or for LSB 1.3

For LSB 1.3 compliant systems you should use the X-UnitedLinux-Should-Start tag instead of the new LSB 2.0 Should-Start. Basically both keywords mean that nthd should start before novell-thingy if nthd is present on the system. If nthd is not on the system, the init script will still continue.

The item(s) that are provided via the Provides directive must be unique. It is wise to prefix them with a LANANA approved name (i.e. "novell-") to ensure uniqueness.

Second: Init Script Location

According to the LSB specification init scripts go in /etc/init.d, not the old, deprecated location /etc/rc.d/init.d. Linux Standard Base Project Specification

Next: The init Start/Stop Order

On Red Hat and SUSE systems, the symbolic links, in the given run level directory, determine the order by which services are started and stopped. For example:

/etc/rc.d/rc5.d/K06novell-thingy -> /etc/init.d/novell-thingy
   /etc/rc.d/rc5.d/S17novell-thingy -> /etc/init.d/novell-thingy

The symbolic link is named using the following convention:

{S,K}[0-9][0-9]<name>

The 'K' means that the script is executed with a "stop" argument when leaving the runlevel. A 'S' means the script is executed with a "start" argument when entering the runlevel. The numbers determine the order in which the scripts are executed. The number field (e.g the 17 in S17novell-thingy) doesn't mean that the init script is the nth script invoked, it just means that the init script starts after any lower number specified init script and before any higher numbered init script.

On Red Hat systems using chkconfig you have explicit control over these numbers. You are responsible to make sure that you use a higher start number than the services you require started before yours (and vice-a-versa for stopping services).

With SUSE (LSB) you do not have explicit control. Instead you must list your dependencies using the Required-Start and Required-Stop keywords (Should-Start and Should-Stop can also be used in LSB 2.0 systems). When SUSE init script tools (insserv, etc.) are run they handle the numerical ordering of the symbolic links for you based on the dependency information you provide in the init script.

In the previous comment block example, the line:

Required-Start: $local_fs $network $syslog

indicates, by start order, that local_fs, network and syslog services must be running before the current service is started. The Required-Stop keyword indicates what services must still be running during the shutdown of the service (however, this is currently ignored in SUSE; the reverse order of the Required-Start is used instead).

RPM Issues

To ensure that an rpm behaves the same in Red Hat and SUSE (LSB) there are a couple of changes that need to be made.

First: RPM %post scripts

In the rpm %post script you can enable services to be started automatically when the OS boots.

In the %post script, do something like:

if [ -x /usr/lib/lsb/install_initd ]; then
  /usr/lib/lsb/install_initd /etc/init.d/novell-httpd
elif [ -x /sbin/chkconfig ]; then
  /sbin/chkconfig --add novell-httpd
else
   for i in 2 3 4 5; do
        ln -sf /etc/init.d/novell-httpd /etc/rc.d/rc${i}.d/S90novell-httpd
   done
   for i in 1 6; do
        ln -sf /etc/init.d/novell-httpd /etc/rc.d/rc${i}.d/K10novell-httpd
   done
fi

Second: RPM %preun Scripts

To disable services when the package is being removed you do this in the %preun section of your rpm.

In the %preun script,do something like:

#only on uninstall, not on upgrades.
if [ $1 = 0 ]; then
  /etc/init.d/novell-httpd stop  > /dev/null 2>&1
  if [ -x /usr/lib/lsb/remove_initd ]; then
    /usr/lib/lsb/install_initd /etc/init.d/novell-httpd
  elif [ -x /sbin/chkconfig ]; then
    /sbin/chkconfig --del novell-httpd
  else
    rm -f /etc/rc.d/rc?.d/???novell-httpd
  fi
fi
Points to Remember

Point 1:

On SUSE you must use their utilities to install and remove the init scripts. If you don't use them, the system won't know that the service is enabled. For instance, if nthd is installed but the symlinks are created through means other than install_initd, SUSE won't know that nthd is enabled at boot-time. When an application is installed that has a service listed in Required-Start, install_initd will fail to enable the service at boot-time because it thinks that service isn't enabled yet.

Also note that you must include INIT INFO comments in your init script on SUSE. If you don't, the next time insserv (install_initd) is executed your symlinks will be re-numbered to S01.

Point 2:

Avoid the following in your init scripts:

  1. Don't include the symlinks in /etc/rc.d/rc?.d in the %files section of the spec file.
  2. Don't install an init script on SUSE that doesn't have INIT INFO comments.
  3. Don't create symlinks in /etc/rc.d/rc?.d on SUSE by any means other then /usr/lib/lsb/install_initd.

Point 3:

Naming conventions and return codes for init scripts:

  1. There are certain actions that should be supported by init scripts (see the LSB specification).
  2. The return codes for those actions are also specified in the LSB specification.
  3. Use LSB/LANANA naming for init scripts to avoid collisions.

Point 4:

    There are also bash functions specified in the LSB specification that are available to be used in init scripts. It is also useful to have a common set of function names that can be used in init scripts that can be used on either platform.
# sample:Not all cases are tested. 
MyStatus()
{
  ps wt? | grep "$DAEMON" 2>&1 > /dev/null
  if [ "x$?" = "x0" ]; then
    RVAL=0
    echo "Apache is running"
  else
    RVAL=3
    echo "Apache is not running"
  fi
}

if [ -f /lib/lsb/init-functions ]; then
  . /lib/lsb/init-functions
  alias START_DAEMON=start_daemon
  alias STATUS=MyStatus
  alias LOG_SUCCESS=log_success_msg
  alias LOG_FAILURE=log_failure_msg
  alias LOG_WARNING=log_warning_msg
elif [ -f /etc/init.d/functions ]; then
  . /etc/init.d/functions
  alias START_DAEMON=daemon
  alias STATUS=status
  alias LOG_SUCCESS=success
  alias LOG_FAILURE=failure
  alias LOG_WARNING=passed
else
  echo "Error: your platform is not supported by $0" > /dev/stderr
  exit 1
fi
Conclusion

Adhering to the LSB specifications is the best way to create init scripts and rpms. However, when LSB system compliance cannot be assured, the information contained in this document can be used to mitigate the differences between Red Hat specific conventions and SUSE (LSB) when creating init scripts and rpms.

Resources


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell