tags:

views:

93

answers:

2

I want to start a couple of jobs on different machines using ssh. If the user then interrupts the main script I want to shut down all the jobs gracefully.

Here is a short example of what I'm trying to do:

#!/bin/bash
trap "aborted" SIGINT SIGTERM
aborted() {
    kill -SIGTERM $bash2_pid
    exit
}

ssh -t remote_machine /foo/bar.sh &
bash2_pid=$!
wait

However the bar.sh process is still running the remote machine. If I do the same commands in a terminal window it shuts down the process on the remote host.

Is there an easy way to make this happen when I run the bash script? Or do I need to make it log on to the remote machine, find the right process and kill it that way?

edit: Seems like I have to go with option B, killing the remotescript through another ssh connection

So no I want to know how do I get the remotepid? I've tried a something along the lines of :

remote_pid=$(ssh remote_machine '{ /foo/bar.sh & } ; echo $!')

This doesn't work since it blocks.

How do I wait for a variable to print and then "release" a subprocess?

A: 

You may want to consider mounting the remote file system and run the script from the master box. For instance, if your kernel is compiled with fuse (can check with the following):

/sbin/lsmod | grep -i fuse

You can then mount the remote file system with the following command:

sshfs user@remote_system: mount_point

Now just run your script on the file located in mount_point.

MattyV
the purpose of the real script is to distribute the work across a handful of slave machines, so this wouldn't work for me..
getekha
+1  A: 

It would definitely be preferable to keep your cleanup managed by the ssh that starts the process rather than moving in for the kill with a second ssh session later on.

When ssh is attached to your terminal; it behaves quite well. However, detach it from your terminal and it becomes (as you've noticed) a pain to signal or manage remote processes. You can shut down the link, but not the remote processes.

That leaves you with one option: Use the link as a way for the remote process to get notified that it needs to shut down. The cleanest way to do this is by using blocking I/O. Make the remote read input from ssh and when you want the process to shut down; send it some data so that the remote's reading operation unblocks and it can proceed with the cleanup:

command & read; kill $!

This is what we would want to run on the remote. We invoke our command that we want to run remotely; we read a line of text (blocks until we receive one) and when we're done, signal the command to terminate.

To send the signal from our local script to the remote, all we need to do now is send it a line of text. Unfortunately, Bash does not give you a lot of good options, here. At least, not if you want to be compatible with bash < 4.0.

With bash 4 we can use co-processes:

coproc ssh user@host 'command & read; kill $!'
trap 'echo >&"${COPROC[1]}"' EXIT
...

Now, when the local script exits (don't trap on INT, TERM, etc. Just EXIT) it sends a new line to the file in the second element of the COPROC array. That file is a pipe which is connected to ssh's stdin, effectively routing our line to ssh. The remote command reads the line, ends the read and kills the command.

Before bash 4 things get a bit harder since we don't have co-processes. In that case, we need to do the piping ourselves:

mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT

This should work in pretty much any bash version.

lhunath
This should work. The script need to run on bash 3.2 so I'll have to make my own piping. The normal case is that the remote process ends normally so I can't rely on the trap to do the clean up of the fifo. (it could if I had them all in a list and did the clean up at the end of the program, but I'd rather remove them as soon as I'm done with them).
getekha