persistent process supervision
perpetrate(5)           persistent process supervision           perpetrate(5)

       perpetrate - conventions for runscripts

       perpd(8)  operates  on  a  pair  of ``runscripts'' that are executed to
       start and reset a service and an optional logger.

       The runscripts recognized by perpd(8) are:

              Optional.  Controls the logger for the main service.

              Required.  Controls the main service.

       The arguments, structure, and conventions for rc.log  and  rc.main  are
       identical.  The illustrations below will usually be shown with rc.main,
       but are equally applicable to rc.log unless noted otherwise.

       perpd(8) will invoke service runscripts from within the service defini-
       tion directory.  For example, given some service definition directory


       perpd(8)  will switch into foo before invoking a runscript.  This means
       that foo will be the current working  directory  within  the  runscript

       Runscripts  are  normally implemented as executable shell scripts, pre-
       pared and installed by the system administrator.  A runscript  will  be
       executed at least twice during the life cycle of a service:

              o   to start the service

              o   after the service exits

       A  runscript  will  accordingly be structured to handle either of these

       Runscripts are invoked in the general form:

              ./rc.main target svname [ args...  ]

       The argument target will be set to one of two literal  strings,  either
       ``start''  or  ``reset'',  depending on whether perpd(8) is starting or
       resetting the service.  The svname argument will be set to the basename
       of  the  service  definition directory, such as ``foo'' for the service
       directory foo.  Any additional arguments depend on the target.

       When using a ``start'' target, perpd(8) will invoke the runscript  like

              ./rc.main start svname

       This  follows  the  general form with no additional arguments.  Given a
       ``start'' target, a runscript process should  perform  any  initializa-
       tions  the  service  requires,  and then proceed to replace itself (its
       process ID) with the desired service  running  in  the  foreground.   A
       Bourne-compatible script such as sh(1) will replace itself by using the
       exec command to start the service.  This  provides  perpd(8)  with  the
       process ID it needs for the actual running service, so that the service
       may be properly monitored and controlled.

       Normally the ``start'' target will result in a  persistent  process,  a
       long-running  program  that  starts  at system boot and continues until
       system shutdown.  A runscript called with  a  ``start''  target  should
       generally  not return or exit, unless some error is encountered in ini-
       tializing or starting the service.

       As long-running as a service may be, whenever it  does  exit,  for  any
       reason, perpd(8) will call the runscript again with a ``reset'' target.
       A ``reset'' target will invoke the runscript with additional  arguments
       in  either  one  of  two forms, depending on whether the service exited
       normally, or was terminated by a signal:

              ./rc.main reset svname exit exitcode
              ./rc.main reset svname signal signum signame

       In the first case, where a service has terminated normally,  the  addi-
       tional  arguments  include  the  literal string ``exit'', followed by a
       string representation of the numeric exit code returned by the process.

       In  the  second  case,  where a service was terminated by a signal, the
       additional arguments include the literal string ``signal'', followed by
       a  string  representation of the signal number that killed the process,
       followed by the symbolic name for the signal, such as SIGTERM,  and  as
       may be found listed in signal(7).

       When  called  with  a ``reset'' target, a runscript may be used for any
       number of purposes:

              o   post-service logging and cleanup

              o   sysadmin notification

              o   shutdown of dependent services

       On the other hand, a runscript doesn't  have  to  do  anything  with  a
       ``reset''  target  at  all.   There  are  no  particular  conditions or
       requirements for a resetting service, except that the runscript  should
       normally  return/exit  as  promptly as possible.  A resetting runscript
       will usually do its job quickly so that perpd(8) may start the  service
       again as soon as possible.

       Assume  that perpd(8) is supervising some service defined in the subdi-
       rectory foo.  The simplest barebones rc.main runscript will act only on
       the ``start'' target to exec into the foo service:

              if test ${1} = 'start' ; then
                exec /usr/bin/foo -f

              exit 0

       This example performs no initializations, does not prepare for logging,
       and responds only to a ``start'' target.   It  simply  execs  into  the
       /usr/bin/foo  program,  here  called  with  an  -f argument, presumably
       required to run foo in the foreground.  On any other target other  than
       ``start'', this runscript will exit 0.

       Here  is  another example of a simple foo service, this time with a bit
       of logging added:

              exec 2>&1

              if test ${1} = 'start' ; then
                echo "starting ${2}..."
                exec /usr/bin/foo -f

              exit 0

       This runscript starts by redirecting stderr to stdout, so that all out-
       put  will be captured by the associated logging service.  The runscript
       also emits a startup message that will be  picked  up  by  the  logger,
       showing the use of the svname argument given as the second parameter to
       the runscript.

       A simple rc.log runscript for this service might look like this:

              if test ${1} = 'start' ; then
                exec tinylog -k 5 -t /var/log/${2}

              exit 0

       As with the rc.main runscript example, this runscript only responds  to
       a  ``start''  target.  It execs tinylog(8) to maintain a set of up to 5
       rotated and timestamped log files in the directory  /var/log/foo,  sup-
       plying  the final path element to the logging directory from the svname
       given as the second parameter to the runscript.

       With a logger setup for the foo service, some post-service logging  may
       now be added for a ``reset'' target in rc.main:

              exec 2>&1

              if test ${1} = 'start' ; then
                echo "starting ${2}..."
                exec /usr/bin/foo -f

              if test ${1} = 'reset' ; then
                case ${3} in
                  'exit')   echo "service ${2} exited with exitcode ${4}" ;;
                  'signal') echo "service ${2} terminated on signal ${5}" ;;

              exit 0

       Now  whenever  this  service  exits,  and  the  runscript is run with a
       ``reset'' target, the logger will pick up a timestamped record  of  the
       event as well as the cause/type of termination.

       Runscripts may use whatever branching idioms are provided by the script
       interpreter.  A couple of obvious possibilities in  sh(1)  include  the
       case and eval statements.  Here is an example runscript using eval:

              exec 2>&1


              start() {
                echo "starting ${SVNAME}..."
                exec /usr/bin/foo -f

              reset() {
                echo "resetting ${SVNAME}..."
                exit 0

              eval ${TARGET} "$@"

       The  runscripts  shown above are admittedly simplistic.  Runscripts may
       be, and often are, embellished considerably beyond the simple  examples
       shown here.  In particular, runtools(8) are often used in the exec com-
       mand of a runscript ``start'' target  to  implement  resource  control,
       privilege  drops,  and other manipulations of the service process envi-

       Nevertheless, it is generally preferable to keep runscripts  as  simple
       as  possible,  while  still  starting  the service safely and reliably.
       Simpler runscripts run faster, are easier  to  maintain  and  diagnose,
       have  fewer  unexpected  side  effects, and are generally more portable
       among different host installations.

       In addition to the positional arguments supplied to the runscript, cer-
       tain other variables are also defined within the runscript environment.

       The PERP_BASE variable is defined for both ``start'' and ``reset'' tar-
       gets.  This variable provides the base directory of the service instal-
       lation, normally /etc/perp, and as described in perpd(8).  The  defini-
       tion  of  PERP_BASE permits the use of such perp utilities as perpok(8)
       and perpctl(8) directly within runscripts, where they may  then  easily
       reference any other service definitions as necessary.

       The  PERP_SVPID  variable  is  defined for both ``start'' and ``reset''
       targets.  For the ``start'' target, PERP_SVPID gives the process ID  of
       the service that will be started.  For the ``reset'' target, PERP_SVPID
       gives the process ID of the service that  has  just  terminated.   Run-
       scripts  may  choose  to use the PERP_SVPID variable to generate output
       that cleanly brackets the complete life-cycle of a service within  ser-
       vice logs, or for any other purpose of reporting and notification.

       The  PERP_SVSECS variable is defined only for the ``reset'' target.  It
       gives the total wallclock uptime, in seconds, of the service  that  has
       just  terminated.   Runscripts  running  ``reset''  may  choose  to use
       PERP_SVSECS for logging the uptime of a service, or for any other  pur-
       pose of reporting and notification.

       For  users familiar with the daemontools package, the runscript conven-
       tions described here are not directly  interchangeable  with  those  of
       supervise(8).   The  main difference is that the run scripts of daemon-
       tools are designed to perform only on startup of a  service,  and  will
       have no facility for properly handling a ``reset'' argument.

       Nevertheless,  it  is  trivial to provide compatibility to perpd(8) for
       any pre-existing daemontools run scripts.  Just install copies  of  the
       following rc.main into any daemontools service definition directory:

              if test ${1} = 'start' ; then
                exec ./run

       Likewise, this rc.log:

              if test ${1} = 'start' ; then
                cd ./log && exec ./run

       The perpd(8) daemon formerly used multiple instances of a perpetrate(8)
       executable, running one instance for each  service  under  supervision.
       Under that architecture, the perpetrate(8) executable performed all the
       supervisory operations on the service definition as described  in  this

       Beginning  with  version  2.0, the operations of the perpetrate(8) exe-
       cutable were internally coalesced with perpd(8) itself,  and  the  need
       for perpetrate(8) was eliminated.  Meanwhile, all the runscript conven-
       tions have otherwise remained the same, and the  name  of  this  manual
       page has been retained to describe them.

       Wayne Marshall,

       perp_intro(8),    perpboot(8),    perpctl(8),   perpd(8),   perphup(8),
       perpls(8), perpok(8), perpstat(8), sissylog(8), tinylog(8)

perp-2.07                        January 2013                    perpetrate(5)