How to Properly Daemonize a Non-Daemon Program in Linux Init Scripts


2 views

When dealing with programs that weren't designed as daemons (those that don't background themselves, manage PID files, or handle their own logging), we face several technical hurdles in init scripts:

  • Process doesn't detach from the controlling terminal
  • Output continues to stream to STDOUT/STDERR
  • Lack of proper PID management makes service control unreliable

The key problems in the provided init script:

# Main issues in the original approach:
1. Using daemon function with a non-detaching process
2. killproc tries to kill "exec" literally instead of $prog
3. No output redirection handling

Here's a properly modified version that handles the daemonization properly:

#!/bin/bash
#
#   /etc/rc.d/init.d/someprog
#
# chkconfig: 345 80 20
# description: someprog service wrapper
. /etc/rc.d/init.d/functions

prog="someprog"
exec="/usr/local/bin/$prog"
user="someproguser"
logfile="/var/log/${prog}.log"
pidfile="/var/run/${prog}.pid"

start() {
    echo -n $"Starting $prog: "
    # Key modifications:
    daemon --user $user --pidfile $pidfile \
        "{ $exec &>> $logfile & }; echo \$! > $pidfile"
    RETVAL=$?
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
    echo
    return $RETVAL
}

stop() {
    echo -n $"Stopping $prog: "
    killproc -p "$pidfile" "$exec"
    RETVAL=$?
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
    echo
    return $RETVAL
}

The solution works because:

  1. We force backgrounding with &
  2. Redirect all output to a log file with &>>
  3. Properly capture and store the PID
  4. Use the correct process name with killproc

For programs that resist even the above method:

start() {
    echo -n $"Starting $prog: "
    nohup $exec &>> $logfile &
    echo $! > $pidfile
    RETVAL=$?
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
    echo
}

For enterprise environments, consider adding:

  • Log rotation configuration
  • Resource limits (ulimit)
  • Startup dependency management
  • Pre-start validation checks

When dealing with programs that don't background themselves, the main issue lies in their interaction with the controlling terminal. The standard daemon function from /etc/rc.d/init.d/functions might not properly handle processes that:

  • Maintain STDOUT/STDERR connections
  • Don't create their own PID files
  • Don't detach from the terminal session

The behavior you're seeing occurs because:

daemon --user someproguser "$exec"

This command doesn't fully daemonize the process when:

  1. The program maintains terminal connections
  2. No proper PID file management exists
  3. The process isn't properly disowned from the shell

Here's a modified version that properly handles foreground processes:

start() {
    check
    if [ ! -f "$lockfile" ]; then
        echo -n $"Starting $prog: "
        # Use nohup with output redirection
        nohup daemon --user someproguser "$exec" > /var/log/someprog.log 2>&1 &
        # Store the PID properly
        echo $! > /var/run/someprog.pid
        RETVAL=$?
        [ $RETVAL -eq 0 ] && touch "$lockfile"
        echo
    fi
    return $RETVAL
}

stop() {
    check
    echo -n $"Stopping $prog: "
    # Read PID from our custom file
    [ -f /var/run/someprog.pid ] && kill -9 $(cat /var/run/someprog.pid)
    RETVAL=$?
    [ $RETVAL -eq 0 ] && rm -f "$lockfile" /var/run/someprog.pid
    echo
    return $RETVAL
}

For a production-ready solution:

#!/bin/bash
#
#   /etc/rc.d/init.d/someprog
#
# chkconfig: 345 80 20
# description: Custom daemon management for foreground processes

. /etc/rc.d/init.d/functions

prog="someprog"
exec="/usr/local/bin/$prog"
pidfile="/var/run/$prog.pid"
logfile="/var/log/$prog.log"
lockfile="/var/lock/subsys/$prog"
[ -e "/etc/sysconfig/$prog" ] && . "/etc/sysconfig/$prog"

start() {
    [ -x $exec ] || exit 5
    echo -n $"Starting $prog: "
    # Full daemonization with output redirection
    nohup $exec >> $logfile 2>&1 &
    retval=$?
    pid=$!
    [ $retval -eq 0 ] && {
        echo $pid > $pidfile
        touch $lockfile
    }
    echo
    return $retval
}

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

case "$1" in
    start)  start ;;
    stop)   stop ;;
    restart)
        stop
        start
        ;;
    status)
        status -p $pidfile $prog
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|status}"
        exit 2
esac
exit $?

For more complex scenarios, consider:

  • Using start-stop-daemon if available
  • Implementing proper log rotation
  • Adding resource limit controls
  • Setting process priority with nice

After implementing changes:

  1. chkconfig --add someprog
  2. service someprog start
  3. Verify with ps aux | grep someprog
  4. Check log output in /var/log/someprog.log