Сокат и богатый терминал снова

542
ilya

Я начинаю socatна терминале, выполнивsocat - UNIX-listen:/tmp/sock

Затем я иду в другой терминал и запускаю программу (с помощью python) таким образом, что она запускается в первом терминале, вот пример:

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect("/tmp/sock") proc=subprocess.Popen(['prg'], stdin=s, stdout=s, stderr=s, shell=False) proc.wait() 

Программы с простым вводом / выводом, например «cat», работают нормально, но более сложные (например, «vim» или «bash»), очевидно, не очень счастливы. Кроме того, когда я нажимаю Cntrl-Cна первый терминал, socatон убивается, в то время как я предпочел бы, чтобы прерывание клавиатуры доставлялось в prgпрограмму (началось с кода Python выше).

Мой вопрос: если это возможно, и как сказать ему, чтобы он socatвел себя как prgвладелец терминала, а не socatсам.

PS. Может показаться, что этот вопрос является дубликатом этого один, но это не так, потому что в этом вопросе prgвызывается непосредственно socat, так что можно использовать EXECварианты.

2

1 ответ на вопрос

2
grawity

Сигналы или собственность владельца не могут быть «волшебным образом» перенесены через сокет.

За AF_UNIX, вы можете отправить терминал дескриптор файла непосредственно ( с помощью sendmsg и recvmsg) и есть принимающая сторона использовать (а не сокет) в качестве подпроцесса стандартный вывод / STDERR. Это не может быть сделано с простым socat, но этот метод используется внутри, например, инструментом tmux.


С простым socat самое лучшее, что вы можете сделать, это заставить его отправлять буквальный байт 0x03 (^ C) при нажатии Ctrl + C (это то, что делают telnet и ssh). Вы можете проверить это, нажав Ctrl+VCtrl+Cодин раз, чтобы отправить его, или запустив stty rawперед socat постоянный переход в «сырой режим».

Однако этого будет недостаточно, чтобы принимающая сторона действительно поняла нажатие клавиши. Самый простой способ - проверять каждый байт вручную - отправлять SIGINT, когда он видит 0x03; в противном случае запишите это в подпроцесс 'stdin.

Однако telnetd и sshd добавляют псевдо-tty между сокетом и подпроцессом - в Python вы можете создать его, используя os.openpty(); сторона 'slave' должна быть указана как подпроцесс 'stdout / stderr, а ваш сценарий должен выполняться в цикле, копируя данные между сокетом и стороной' master '.

Это все еще не вполне достаточно - полнофункциональный терминал протокола Логин, такие как SSH или Telnet, также есть сообщения, чтобы обновить размеры в PTy, и реагировать на termios устанавливающих изменения (например, установка «локальное эхо» и другие видели в stty).

Было бы полезно прочитать исходный код клиента telnet и сервера telnetd, например, из GNU inetutils, поскольку они делают именно это.

Спасибо за ответ! Увы, это не так просто, как я надеялся. Может ли быть так, что мою проблему будет легче решить, если я сформулирую ее в менее общем стиле? Чего я хочу добиться, так это: у меня есть «основная» программа на Python, которая открывает новое окно в данном сеансе tmux и запускает другую программу «xxx» в этом новом окне, но основной процесс Python должен иметь полный контроль над ней (таким образом, «xxx» должен быть экземпляром «подпроцесса» или подобным). Поэтому сначала я прошу libtmux открыть новое окно с помощью window_shell = "socat - UNIX-listen: / tmp / sock", а затем подключаюсь, как описано в вопросе выше. Это поможет? ilya 7 лет назад 0
Не могли бы вы попросить libtmux просто дать pty имя окна или fd напрямую? grawity 7 лет назад 0
Да, после того, как я создал tmux ** window **, я могу видеть в `window.panes [0] ['pane_tty']` псевдо-tty имя устройства, например, `/ dev / pts / 57` ilya 7 лет назад 0
Не могли бы вы немного уточнить ваш ответ: после того, как я вызову `(master, slave) = os.openpty ()` и затем вызову `subprocess.Popen (stdout = slave, stderr = slave ....)` что я должен дать как параметр `stdin`? Это хорошая идея, чтобы открыть `/ dev / pts / XXX` напрямую и передать дескриптор как` stdin` для `subprocess.Popen (...)`? И второй вопрос: что вы имеете в виду, копируя данные между сокетом и «главной» стороной? Откуда именно мне читать и куда писать? ilya 7 лет назад 0
Pty Slave используется как входной _and_ выход для подпроцесса. Аналогично, мастер pty должен быть скопирован из _and_ в сокет. (Вам может понадобиться select.poll () или похожий цикл событий.) grawity 7 лет назад 0
Еще раз спасибо за разъяснения. Я написал [этот пример сценария] (http://pastebin.com/gfD3gGtw), используя ваши предложения. На терминальной стороне я запускаю `socat` с помощью следующей команды:` socat file: $ (tty), raw, echo = 0 UNIX-listen: / tmp / blah-socket`, а на стороне python я просто запускаю скрипт с аргументы, говорящие о том, какая программа должна быть запущена. Я пробовал `cat` и` vim`, и оба работают нормально. Если у вас есть какие-либо комментарии к сценарию, пожалуйста, не стесняйтесь комментировать. ilya 7 лет назад 0

Похожие вопросы