2

I have a very complex python application. It has it's own init script, but when I execute:

sudo service my_service_daemon stop

It prints "Stopping my_service_daemon [OK]", but in real subprocesses still alive, until they finish their work (they caught signal 15, but have to finish some job). So, I want that message "[OK]" will be printed only when there are no subprocesses at all. Here is part of init script (it runs on CentOS 6)

...

. /etc/init.d/functions 

...

stop(){
    echo -n $"Stopping $prog: "
    if [ -a $pidfile ]; then
    group_id=$(ps -o pgid= $(cat $pidfile) | grep -o [0-9]*)
    if [ ! -z $group_id ]; then
            kill -- -$group_id
        success
    fi
    else
        failure
    fi
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && rm -f $lockfile $pidfile
}
Sergius
  • 813

3 Answers3

3

Use systemd's handling of processes, or use a cgroup by hand to control the zoo of decendents. Fooling around with ps(1) output and PID files is race-prone, and has been known to lead to early baldness due to hair-pulling.

vonbrand
  • 18,253
  • 2
    systemd? but it has sysvinit as init system, also it's a /etc/init.d/my_service_daemon script... or I misunderstand something? – Sergius Oct 21 '15 at 07:50
1

After you do the kill -- -$group_id wait for all the processes to end. For example, I'd do something like

...
if [ ! -z "$group_id" ]; then
    kill -- -$group_id
    while pgrep -g $group_id &>/dev/null; do
        sleep 1
    done
    success
...

This will have pgrep search for all members of the process group (-g) $group_id. Normally it will print all those PIDs to the screen, but we don't really want to see them so I redirect all the output to /dev/null. pgrep will exit with success (0) if it finds at least 1 process matching its search criteria, which would keep the while loop going. Once it doesn't find any processes it will exit with a "false" (1) which will exit the loop and get to the success line. In order to not crush the system with a tight loop, I stuck a sleep 1 in there.

Eric Renouf
  • 18,431
1

Keep a file open in the Python application. That is, open it in the launcher script, and don't close it in the application.

This file can be the PID file. Proof of concept (with no handling of concurrency):

pidfile=/var/run/myapp.pid
logfile=/var/log/myapp.log
start () {
  sh -c 'echo $$ >"$1"; exec myapp <"$1" >/dev/null 2>"$2"' &
}
stop () {
  master_pid=$(cat "$pidfile")
  while
    case " $(fuser "$pidfile" 2>/dev/null) " in
      *" $master_pid "*)
        echo "Master process is alive, killing it"
        kill "$master_pid";;
      "  ") echo "Master process is dead and no subprocesses remain"; false;;
      *) echo "Master process is already dead but some processes remain";;
    esac
  do
    sleep 1
  done
  rm "$pidfile"
}