Terminating a process - is this code best practice?

Hi Folks -

I am building a process to kill a list of services. Sometimes, there's a service that hangs therefore I need to add an additionla peice of code to kill all instances of a service if it exists.

Here is that portion of code:

echo ---------------------------------------------------------
echo "Terminate Hung Services"                               
echo ---------------------------------------------------------
_SERVICE=ESSSVR
  
ps -ef | grep ${_SERVICE} | awk '{print $2}' | xargs kill -9

My goal with the above piece of code is to kill any instances of ESSSVR.

Is that a correct way of going about it?

Thanks!

Using signal 9 (= SIGKILL) directly is very bad.

Signals are a way of communicating with processes. And Signal 9 is a kind of non-communicating with which the processes get stopped immediately.

Most Software implement a proper cleanup when beeing told to terminate. All that cleanup can not take place here, because SIGKILL means the processes are stopped immediately by the operating system. That can cause a lot of trouble in your application, because Junk is not cleaned up(temporary files, temporary tables, proper setting some values in database done at a clean shutdown,...). Maybe your application will not start after you killed it that way once or some more times. Maybe your applications data will be corrupted badly over time.

A better way of doing what you want is send a signal 15 ( = SIGTERM), then give the task enought time time (10-60 Seconds depending on how much your application usually needs, to avoid trouble I would suggest you rather choose the 60 seconds). If the process is still alive afterwards send a signal 2 ( = SIGINT) and wait for another 10-60 seconds. If the process is still alive afterwards, finally kill it with signal 9 (SIGKILL).

Here's one of my scripts(could be improved though), doing this:

#!/bin/bash

function trim {

        arg="$*"
        shopt -s extglob
        arg="${arg#*( )}"
        arg="${arg%*( )}"
        echo "$arg"

}

function kill_soft_then_hard {

        PIDS="$*"

        PID_REGEX_PATTERN="^\s*("
        for pid in $PIDS;do
                PID_REGEX_PATTERN="$PID_REGEX_PATTERN|$pid"
        done
        PID_REGEX_PATTERN="$PID_REGEX_PATTERN)"

        # Here's the chain of signals being sent, when the processes refuse to terminate
        for SIGNAL in 15 1 3 7 9 ;do 
                kill -$SIGNAL $PIDS &>/dev/null
                for((w=1;$w<=30;w++)) ; do
                  if ps ax | grep -qE $PID_REGEX_PATTERN ; then
                          continue
                  else
                          break 2
                  fi
                  sleep 1
                done
        done

}

function kill_by_pattern {

        pattern="$1"
        PIDS="$(ps ax | grep "$pattern" | grep -v grep | awk '{print $1}')"
        PIDS="$(trim $PIDS)"

        [ -n "$PIDS" ] && kill_soft_then_hard $PIDS

}

kill_by_pattern ESSSVR

The programs pgrep and pkill may be useful too.

Have a look at the manpage signal(7) for a detailed explanation of the standard signals.

1 Like

Did you consider the killall command?

Wow, thank you for that reply!

Is this the portion I add ESSSVR or is this just getting all processes then it find the pattern later?

PID_REGEX_PATTERN="^\s*("

No. I'm grepping the process list for multiple PIDs that may match the given pattern.

For normal use, you may just change the very last line of the script:

kill_by_pattern ESSSVR

If you like to tweak the script, the most liked improvement will be to do not wait the full 30 second period if the process is already terminated.

I recommend you to test the script in a testing environment before using it in production, since you do not know anything about my environment and vice versa for me.

1 Like

Noted - thank you!

I will adjust and add your code to my script. And this would loop through and kill all instances of ESSSVR or just the first one it encounters?

It'll kill all Instances that have ESSSVR in its name in the process list.

1 Like

Awesome - thank you Stomp. Have a great day!

Hi,

I changed the script a little, so it does not wait 30 seconds for the process which may actually be shut down a lot faster.

Nice day 2u2.

kill -0 is often convenient, if checking when it went away. Works just as normal signals do, but does not
have any effect in the receiving process.

Juha

Can anyone condense it, having the KILL in the loop but without the trailing sleep...

daemons="ppp gps"

for sig in TERM 0 0 0 0
do
        killall -q -u0 -$sig $daemons || exit
        sleep 1
done

killall -q -u0 -KILL $daemons

(FreeBSD's killall, quiet, uid 0 processes only)

1 Like

Nice One.