Paramiko и exec_command - убить удаленный процесс? - PullRequest
13 голосов
/ 12 октября 2011

Я использую Paramiko для tail -f файла на удаленном сервере.

Раньше мы запускали его через ssh -t, но это оказалось некорректно, и -t вызывал проблемы с нашимСистема удаленного планирования.

Мой вопрос: как убить хвост, когда скрипт ловит SIGINT?

Мой скрипт (на основе Долгосрочных команд ssh в модуле Python Paramiko (и какчтобы закончить их) )

#!/usr/bin/env python2
import paramiko
import select

client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
channel = transport.open_session()

channel.exec_command("tail -f /home/victorhooi/macbeth.txt")
while True:
    try:
        rl, wl, xl = select.select([channel],[],[],0.0)
        if len(rl) > 0:
            # Must be stdout
            print channel.recv(1024)
    except KeyboardInterrupt:
        print("Caught control-C")
        client.close()
        channel.close()
        exit(0)

Скрипт успешно ловит мой Ctrl-C и заканчивается.Однако он оставляет процесс tail -f запущенным в удаленной системе.

Ни client.close (), ни channel.close (), по-видимому, не завершают его.

Какую команду можно выполнитьв блоке исключений, чтобы убить его?

Удаленный сервер работает под управлением Solaris 10.

Ответы [ 8 ]

6 голосов
/ 25 июня 2012

Есть один способ сделать это. Работает как на оболочке

ssh -t commandname

Опция -t открывает псевдо pty, чтобы помочь ssh отследить, как долго должен длиться этот процесс. то же самое можно сделать через пормико через

channel.get_pty()

до execute_command (...). Это не откроет оболочку, как это происходит с channel.invoke_shell (), она просто запросит такой псевдоинтерфейс, чтобы связать все процессы. Эффект также можно увидеть, если на удаленной машине выдается ps aux, теперь процесс подключен к sshd с помощью интерфейса ptxXY.

5 голосов
/ 12 октября 2011

Вы должны использовать ssh keepalive ... проблема в том, что удаленная оболочка не может знать (по умолчанию), что ваша сессия ssh была убита. Keepalive позволит удаленной оболочке обнаружить, что вы прервали сеанс

client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
transport.set_keepalive(1)   # <------------------------------
# ... carry on as usual...

Установите значение keepalive настолько низким, насколько вам нужно (даже 1 секунду) ... через несколько секунд удаленная оболочка увидит, что ssh-логин умер, и завершит все процессы, которые были им вызваны.

4 голосов
/ 31 октября 2012

Я только что решил эту проблему и не смог выпустить pkill, чтобы завершить процесс в конце.

Лучшее решение - изменить команду, которую вы используете:

tail -f /path/to/file & { read ; kill %1; }

Это позволит вам запускать команду tail так долго, как вам нужно. Как только вы отправите новую строку удаленному процессу, kill% 1 выполнится и остановит команду tail, которую вы создали. (для справки:% 1 является спецификацией задания и используется для описания первого процесса, который был основан в вашем сеансе, т.е. команды tail)

1 голос
/ 04 января 2013

Вот способ получить идентификатор удаленного процесса:

def execute(channel, command):
    command = 'echo $$; exec ' + command
    stdin, stdout, stderr = channel.exec_command(command)
    pid = int(stdout.readline())
    return pid, stdin, stdout, stderr

А вот как его использовать (замените ... битами в исходном вопросе):

pid, _, _, _ = execute(channel, "tail -f /home/victorhooi/macbeth.txt")
while True:
    try:
        # ...
    except KeyboardInterrupt:
        client.exec_command("kill %d" % pid)
        # ...
0 голосов
/ 25 апреля 2017

Была такая же проблема с ssh -t.Есть библиотека под названием ближе - она ​​запускает удаленный процесс через ssh и автоматически закрывается для вас.Проверьте это.

0 голосов
/ 11 августа 2016

Вы можете использовать get_pty, как описано в https://stackoverflow.com/a/38883662/565212.

например. сценарий - когда вызывать client / channel.close ():
Шаг 1: Выполнить удаленную команду, которая записывает в файл журнала.
Step2: порождает поток, который выполняет команду tail и блокирует цикл readline
Шаг 3: В главном потоке, когда команда возвращается, вы знаете, что больше не будет логов, уничтожьте хвостовой поток.

0 голосов
/ 16 января 2013

Специально для 'tail' вы можете использовать аргумент --pid = PID и позволить tail позаботиться о нем:

  --pid=PID  with -f, terminate after process ID, PID dies
0 голосов
/ 12 октября 2011

Хотя это не самый эффективный метод, он должен работать. После вас CTRL + C ; В обработчике KeyboardInterrupt вы могли бы exec_command("killall -u %s tail" % uname) примерно так:

#!/usr/bin/env python2

import paramiko
import select

import time
ltime = time.time()

# Or use random:
# import random
# ltime = random.randint(0, 500)

uname = "victorhooi"
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username=uname, password='blahblah')
transport = client.get_transport()
channel = transport.open_session()

channel.exec_command("tail -%df /home/victorhooi/macbeth.txt" % ltime)
while True:
    try:
        rl, wl, xl = select.select([channel],[],[],0.0)
        if len(rl) > 0:
            # Must be stdout
            print channel.recv(1024)
    except KeyboardInterrupt:
        print("Caught control-C")
        channel.close()
        try:
            # open new socket and kill the proc..
            client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))
        except:
            pass
    
        client.close()
        exit(0)

Это уничтожит все открытые процессы с именем tail. Однако это может вызвать проблемы, если у вас есть tail открытых, которые вы не хотите закрывать, если в этом случае вы могли бы grep a ps, получить pid и kill -9 it.

Во-первых, задайте хвост для чтения n строк от конца файла до следующего. присвойте n уникальное число, например time.time(), поскольку tail не заботится о том, что это число больше числа строк в файле, большое число из time.time() не должно вызывать проблем и будет уникальным. Затем найдите этот уникальный номер в ps:

   client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))
...