Send signals to remote process with Python

2019-03-06 03:19发布

问题:

There are two machines, where one has a script wait_for_signal.sh, and the second one has a script named controller.py. The code of each script is shown below.

The purpose of the controller.py is to spawn a subprocess that calls the wait_for_signal.sh script through ssh. When the controller needs to exit it needs to send an interrupt to the remote process that runs wait_for_signal.sh.

wait_for_signal.sh

#!/bin/bash
trap 'break' SIGINT
trap 'break' SIGHUP

echo "Start loop"

while true; do
  sleep 1
done

echo "Script done"

controller.py

import os
import signal
import subprocess

remote_machine = user@ip
remote_path = path/to/script/

remote_proc = subprocess.Popen(['ssh', '-T', remote_machine, 
                               './' + remote_path + 'wait_for_signal.sh'], 
                               shell=False, stdout=subprocess.PIPE, 
                               stderr=subprocess.PIPE)

# do other stuff

os.kill(remote_proc.pid, signal.SIGINT)

Currently, the signal send is to the process that started the ssh connection on the local machine and not the remote machine. This causes the local process to stop but the remote process continues to execute.

How does ssh work and what type of signals does it send to the remote machine when it is stopped? How can I send the appropriate signal to the remote process, which was started by the ssh connection?

回答1:

You're invoking ssh with the -T option, meaning that it won't allocate a PTY (pseudo-TTY) for the remote session. In this case, there's no way to signal the remote process through that ssh session.

The SSH protocol has a message to send a signal to the remote process. However, you're probably using OpenSSH for either the client or the server or both, and as far as I can tell, OpenSSH doesn't implement the signal message. So the OpenSSH client can't send the message, and the OpenSSH server won't act on it.

There is an SSH extension to send a "break" message which is supported by OpenSSH. In an interactive session, the OpenSSH client has an escape sequence that you can type to send a break to the server. The OpenSSH server handles break messages by sending a break to the PTY for the remote session, and unix PTYs will normally treat a break as a SIGINT. However, breaks are fundamentally a TTY concept, and none of this will work for remote sessions which don't have a PTY.

I can think of two ways to do what you want:

  1. Invoke ssh with the -tt parameter instead of -T. This will cause ssh to request a TTY for the remote session. Running the remote process through a TTY will make it act like it's running interactively. Killing the local ssh process should cause the remote process to receive a SIGHUP. Writing a Ctrl-C to the local ssh process's standard input should cause the remote process to receive a SIGINT.

  2. Open another ssh session to the remote host and use killall or some other command to signal the process that you want to signal.