12

I need to install a program as a service in Red Hat. It doesn't background itself, manage its PID file, or manage its own logs. It just runs and prints to STDOUT and STDERR.

Using the standard init scripts as guides, I've developed the following:

#!/bin/bash
#
#   /etc/rc.d/init.d/someprog
#
# Starts the someprog daemon
#
# chkconfig: 345 80 20
# description: the someprog daemon
# processname: someprog
# config: /etc/someprog.conf

# Source function library.
. /etc/rc.d/init.d/functions

prog="someprog"
exec="/usr/local/bin/$prog"
[ -e "/etc/sysconfig/$prog" ] && . "/etc/sysconfig/$prog"
lockfile="/var/lock/subsys/$prog"
RETVAL=0

check() {
    [ `id -u` = 0 ] || exit 4
    test -x "$exec" || exit 5
}

start() {
    check
    if [ ! -f "$lockfile" ]; then
        echo -n $"Starting $prog: " 
        daemon --user someproguser "$exec"
        RETVAL=$?
        [ $RETVAL -eq 0 ] && touch "$lockfile"
        echo
    fi
    return $RETVAL
}

stop() {
    check
    echo -n $"Stopping $prog: "
    killproc "exec"
    RETVAL=$?
    [ $RETVAL -eq 0 ] && rm -f "$lockfile"
    echo
    return $RETVAL
}

restart() {
    stop
    start
}   

case "$1" in
start)
    start
    ;;
stop)
    stop
    ;;
restart)
    restart
    ;;
status)
    status "$prog"
    RETVAL=$?
    ;;
*)
    echo $"Usage: $0 {start|stop|restart|status}"
    RETVAL=2
esac

exit $RETVAL

It may be that my mistake was to copy-paste and modify some of the existing scripts in /etc/init.d. In any case, the resulting service behaves strangely:

  • when I start it with service someprog start the program prints to the terminal and the command doesn't complete.
  • if I CTRL-C, it prints "Session terminated, killing shell... ...killed. FAILED". I have to do this to get my shell prompt back again.
  • now when I run service someprog status it says it's running and lists its PID. I can see it in ps so it is running.
  • now when I run service someprog stop it fails to stop. I can verify that it's still running with ps.

What do I need to change so that someprog is sent to the background and managed as a service?

Edit: I have now found a couple of related questions, neither of them with an actual answer other than "do something else":

Edit: this answer on double-forking might have solved my problem, but now my program itself double-forks and that works: https://stackoverflow.com/a/9646251/898699

  • Are you starting the program with the "daemon" utility provided by libslack. http://libslack.org/daemon/#documentation In this case the program can be stopped as daemon -n name --stop. Also, try redirecting the output(when starting the program) to a file or /dev/null and check. – Ankit Mar 28 '13 at 04:45
  • 2
    Dependant on your version of redhat, you can just make a simple wrapper for it in upstart and call it in upstart directly. Then upstart will manage the service for you. This is an EL6 thing though. – Matthew Ife Mar 30 '13 at 20:54

2 Answers2

3

The command "doesn't complete" because the daemon function does not run your application in the background for you. You will need to add an & to the end of your daemon command like so:

daemon --user someproguser $exec &

If someprog doesn't handle SIGHUP, you should run the command with nohup to ensure that your process won't receive SIGHUP which tells your process to exit when the parent shell exits. That would look like this:

daemon --user someproguser "nohup $exec" &

In your stop function, killproc "exec" isn't doing anything to stop your program. It should read like so:

killproc $exec

killproc requires the full path to your application to stop it properly. I've had some trouble with killproc in the past, so you can also just kill the PID in the PIDFILE you should be writing someprog's PID to with something like this:

cat $pidfile | xargs kill

You can write the PIDFILE like this:

ps aux | grep $exec | grep -v grep | tr -s " " | cut -d " " -f2 > $pidfile

where $pidfile points to /var/run/someprog.pid.

If you want [OK] or [FAILED] on your stop function, you should use the success and failure functions from /etc/rc.d/init.d/functions. You don't need these in the start function because daemon calls the appropriate one for you.

You also only need quotes around strings with spaces. It's a style choice, though, so it's up to you.

All these changes look like this:

#!/bin/bash
#
#   /etc/rc.d/init.d/someprog
#
# Starts the someprog daemon
#
# chkconfig: 345 80 20
# description: the someprog daemon
# processname: someprog
# config: /etc/someprog.conf

# Source function library.
. /etc/rc.d/init.d/functions

prog=someprog
exec=/usr/local/bin/$prog
[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
lockfile=/var/lock/subsys/$prog
pidfile=/var/run/$prog
RETVAL=0

check() {
    [ `id -u` = 0 ] || exit 4
    test -x $exec || exit 5
}

start() {
    check
    if [ ! -f $lockfile ]; then
        echo -n $"Starting $prog: " 
        daemon --user someproguser "nohup $exec" &
        RETVAL=$?
        if [ $RETVAL -eq 0 ]; then
          touch $lockfile
          ps aux | grep $exec | grep -v grep | tr -s " " | cut -d " " -f2 > $pidfile
        fi
        echo
    fi
    return $RETVAL
}

stop() {
    check
    echo -n $"Stopping $prog: "
    killproc $exec && cat $pidfile | kill
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
      rm -f $lockfile
      rm -f $pidfile
      success; echo
    else
      failure; echo
    fi
    echo
    return $RETVAL
}

restart() {
    stop
    start
}   

case "$1" in
start)
    start
    ;;
stop)
    stop
    ;;
restart)
    restart
    ;;
status)
    status $prog
    RETVAL=$?
    ;;
*)
    echo $"Usage: $0 {start|stop|restart|status}"
    RETVAL=2
esac

exit $RETVAL
Stuporman
  • 131
  • 6
-1

If this is your program, please write it as a proper daemon. Especially if its for redistribution. :)

You might try monit. Or maybe something like runit or daemontools. Those mighty not have readily available packages. Daemontools is from DJB, if that influences your decision (in either direction.)

toppledwagon
  • 4,215
  • 24
  • 15