Stop python program when ssh pipe is broken

I am writing a python script with an infinite while loop that I run on top of ssh. I would like the script to terminate when someone killed ssh. For instance:

script (script.py):

while True: # do something 

Will run as:

 ssh foo ./script.py 

When I kill the ssh process, I would like the script on the other end to stop working.

I tried looking for a closed scene:

 while not sys.stdout.closed: # do something 

but it didn’t work.

How do I achieve this?

Edit

A remote computer is a Mac that opens a program in csh:

 502 29352 ?? 0:00.01 tcsh -c python test.py 502 29354 ?? 0:00.04 python test.py 

I open the ssh process from a python script as follows:

 p = Popen(['ssh','foo','./script.py'],stdout=PIPE) while True: line = p.stdout.readline() # etc 

EDIT

Suggested solutions:

  • Run the script with while os.getppid() != 1

This is similar to working with Linux systems, but does not work when OSX is running on the remote computer. The problem is that the command runs in csh (see above), so csh has its parent process id set to 1, but not a script.

  • Log in to stderr periodically

This works, but the script also runs locally, and I don't want to print a heartbeat before stderr .

  • Run the script in pseduo tty using ssh -tt .

This works, but has some strange effects. Consider the following:

remote_script:

 #!/usr/bin/env python import os import time import sys while True: print time.time() sys.stdout.flush() time.sleep(1) 

local_script:

 #!/usr/bin/env python from subprocess import Popen, PIPE import time p = Popen(['ssh','-tt',' user@foo ','remote_script'],stdout=PIPE) while True: line = p.stdout.readline().strip() if line: print line else: break time.sleep(10) 

First of all, the conclusion is really strange, it seems that it adds tabs or something else:

 [ user@local ~]$ local_script 1393608642.7 1393608643.71 1393608644.71 Connection to foo closed. 

Secondly, the program does not exit the first time when it receives SIGINT , i.e. I need to press Ctrl-C twice to kill local_script.

+6
source share
5 answers

I suggest registering periodically with stderr.

This will throw an exception if you no longer have a stderr entry.

+2
source

Ok i have a solution for you

When the ssh connection closes, the parent process ID will change from pid ssh-deamon (the fork that handles your connection) to 1.

So the next solution to your problem.

 #!/usr/local/bin/python from time import sleep import os #os.getppid() returns parent pid while (os.getppid() != 1): sleep(1) pass 

Can you confirm that this also works in the end :)

change

I saw how you updated.

This has not been tested, but for this idea to work on OSX, you may find that the csh process has changed. The code below only illustrates the idea and has not been tested. However, I think this will work, but that would not be the most elegant solution. If a cross-platform solution using signals , it would be preferable.

 def do_stuff(): sleep(1) if sys.platform == 'darwin': tcsh_pid = os.getppid() sshfork_pid = psutil.Process(tcsh_pid).ppid while (sshfork_pid == psutil.Process(tcsh_pid).ppid) do_stuff() elif sys.platform == 'linux': while (os.getppid() != 1): sleep(1) else: raise Exception("platform not supported") sys.exit(1) 
+6
source

You tried

 ssh -tt foo ./script.py 
+3
source

When the terminal connection is lost, the application should receive a SIGHUP signal , so you only need to register a special handler using the signal module .

 import signal def MyHandler(self, signum, stackFrame): errorMessage = "I was stopped by %s" % signum raise Exception(errorMessage) # somewhere in the beginning of the __main__: # registering the handler signal.signal(signal.SIGHUP, MyHandler) 

Please note that most likely you will have to process some other signals. You can do it exactly the same.

+3
source

Running the script is the child pid of the terminal session. If you close the SSH session correctly, it will complete the process. But another way to do this is to associate the while loop with another factor and disconnect it from the SSH session.

You may have your cron managed script for regular execution. The while loop has a counter. You may have a sleep command in a loop to control execution. Virtually nothing, except that it is associated with your SSH session, really.

For this, you can use exec and disable instances from your loop.

0
source

All Articles