4

I have a shutdown script for Oracle in /etc/init.d dir on "stop" command it does:

su oracle -c "lsnrctl stop >/dev/null"
su oracle -c "sqlplus sys/passwd as sysdba @/usr/local/PLATEX/scripts/orastop.sql >/dev/null"

.. The problem is when lsnrctl or sqlplus are unresponsive - in this case this "stop" script just never ends and server cant shutdown. The only way - is to "kill - 9 " that.

I'd like to rewrite script so that after 5min (for example) if command is not finished - it should be terminated.

How I can achieve this? Could you give me an example? I'm under Linux RHEL 5.1 + bash.

codeforester
  • 39,467
  • 16
  • 112
  • 140
zmische
  • 809
  • 3
  • 13
  • 23
  • I wouldn't write that in bash; I'd look for a program which does that for you. We have an in-house tool for that (which is closed-source, unfortunately). – JesperE Oct 04 '09 at 13:56
  • Is it difficult to do in Bash? Why do you prefer you own tool? I could write a perl,java tool, but I think that doing in bash is more native and simple? – zmische Oct 04 '09 at 14:04
  • Well, it's _easiest_ to do in something where you have direct access to `waitpid`, which actually supports a timeout value on its own. Bash is fine too, though, and I've used it (literally for exactly this purpose) in the past. (Former employer, no continuing access to the scripts, and I've slept enough times since then that I'm unlikely to remember much that's domain-specific and useful). – Charles Duffy Oct 04 '09 at 14:11
  • 1
    accepted answer of this question did the trick for me: http://stackoverflow.com/questions/687948/timeout-a-command-in-bash-without-unnecessary-delay – P Shved Oct 04 '09 at 14:28
  • Pavel, they're very similar, but not exactly the same -- this one doesn't only kill the child process, but acts differently afterwards (needing to follow the `kill -9` codepath). – Charles Duffy Oct 04 '09 at 14:36

2 Answers2

9

If able to use 3rd-party tools, I'd leverage one of the 3rd-party, pre-written helpers you can call from your script (doalarm and timeout are both mentioned by the BashFAQ entry on the subject).

If writing such a thing myself without using such tools, I'd probably do something like the following:

function try_proper_shutdown() {
  su oracle -c "lsnrctl stop >/dev/null"
  su oracle -c "sqlplus sys/passwd as sysdba @/usr/local/PLATEX/scripts/orastop.sql >/dev/null"
}

function resort_to_harsh_shutdown() {
  for progname in ora_this ora_that ; do
    killall -9 $progname
  done
  # also need to do a bunch of cleanup with ipcs/ipcrm here
}

# here's where we start the proper shutdown approach in the background
try_proper_shutdown &
child_pid=$!

# rather than keeping a counter, we check against the actual clock each cycle
# this prevents the script from running too long if it gets delayed somewhere
# other than sleep (or if the sleep commands don't actually sleep only the
# requested time -- they don't guarantee that they will).
end_time=$(( $(date '+%s') + (60 * 5) ))
while (( $(date '+%s') < end_time )); do
  if kill -0 $child_pid 2>/dev/null; then
    exit 0
  fi
  sleep 1
done

# okay, we timed out; stop the background process that's trying to shut down nicely
# (note that alone, this won't necessarily kill its children, just the subshell we
# forked off) and then make things happen.    
kill $child_pid
resort_to_harsh_shutdown
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • AFAIR we could use WAITPID in bash? And what about waitpid + WHOHANG option in while-loop that measure time that past since exec and terminates then? (I think instead of "if [[ ! -e /proc/$child_pid ]] ; then" construction?) – zmische Oct 04 '09 at 14:22
  • You recall incorrectly; bash's wait construct accepts a pid as an argument, but nothing else (no timeout, so you lose much of the power of the actual waitpid syscall). – Charles Duffy Oct 04 '09 at 14:23
  • ...but yes, if we could do that, it would be *much* less evil. :) – Charles Duffy Oct 04 '09 at 14:26
  • Updated to remove the /proc/$child_pid reference. – Charles Duffy Oct 04 '09 at 14:31
  • I looked through my past projects and found, that I'd used waitpid in PERL when forking childs for multithreading of the script. So you were right - no waitpid with params in bash! – zmische Oct 04 '09 at 16:13
6

wow, that's a complex solution. here's something easier. You can track the PID and kill it later.

my command & #where my command is the command you want to run and the & sign backgrounds it.
PID=$! #PID = last run command.
sleep 120 && doProperShutdown || kill $PID #sleep for 120 seconds and kill the process properly, if that fails, then kill it manually..  this can be backgrounded too. 
AdamOutler
  • 870
  • 4
  • 13
  • 29