stop python program when ssh pipe is broken

时光怂恿深爱的人放手 提交于 2021-02-07 13:35:40

问题


I'm writing a python script with an infinite while loop that I am running over ssh. I would like the script to terminate when someone kills ssh. For example:

The script (script.py):

while True:
    # do something

Will be run as:

ssh foo ./script.py

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

I have tried looking for a closed stdout:

while not sys.stdout.closed:
    # do something

but this didn't work.

How do I achieve this?

Edit:

The remote machine is a Mac which opens the program in a csh:

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

I'm opening the ssh process from a python script like so:

p = Popen(['ssh','foo','./script.py'],stdout=PIPE)

while True:
    line = p.stdout.readline()
    # etc

EDIT

Proposed Solutions:

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

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

  1. Periodically log to stderr

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

  1. Run the script in a pseduo tty with ssh -tt.

This does work, but has some weird consequences. 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 output is really weird, it seems to keep adding tabs or something:

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

Second of all, the program does not quit the first time it receives a SIGINT, i.e. I have to hit Ctrl-C twice in order to kill the local_script.


回答1:


I'd suggest periodically logging to stderr.

This will cause an exception to occur when you no longer have a stderr to write to.




回答2:


Okay, I have a solution for you

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

Thus the following solves 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 this is working in your end too :)

edit

I saw you update.

This is not tested, but to get this idea working on OSX, you may be able to detect if the process of the csh changes. The code below only illustrates an idea and has not been tested. That said i think it would work, but it would not be the most elegant solution. If a cross platform solution using signals could be found, it would be preferred.

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)



回答3:


Have you tried

ssh -tt foo ./script.py



回答4:


When the terminal connection is lost, the application is supposed to receive SIGHUP signal, so all you have to do is to register a special handler using 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)

Note that most likely you'll have to handle some other signals. You can do it in absolutely the same way.




回答5:


The running script is a child pid of the terminal session. If you close the SSH session properly it will terminate the process. But, another method of going about this is to connect your while loop to another factor and disconnect it from your SSH session.

You can have your script controlled by cron to execute regularly. You can have the while loop have a counter. You can have a sleep command in the loop to control execution. Pretty much anything other than having it connected to your SSH session is valid.

To do this you could use exec & to disconnect instances from your loop.



来源:https://stackoverflow.com/questions/21731267/stop-python-program-when-ssh-pipe-is-broken

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!