Здесь есть четыре отдельных вопроса:
1.Как вызвать Ctrl-C на PTY
. Сигнал запускается дисциплиной линии терминала при получении Ctrl-C в качестве ввода (в режиме приготовления).Вы можете сделать это, отправив 0x03, что эквивалентно прописному ASCII 'C' с очищенным 7-м битом:
write(fdm, "\x03", 1);
2.Эквивалент C process.send_signal
process = subprocess.Popen(..) ...
process.send_signal(signal.SIGINT)
Это просто fork
+ kill
, так что это не связано ни с чем, что вы сделаете, чтобы достичь # 1.Вы можете увидеть это в strace
:
$ cat foo.py
import signal
import subprocess
process = subprocess.Popen(["sleep", "10"])
process.send_signal(signal.SIGINT)
$ strace -f -eclone,kill,execve python foo.py
execve("/usr/bin/python", ["python", "foo.py"], 0x7ffe4d179458 /* 30 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa0f6e14a10) = 12917
strace: Process 12917 attached
[pid 12917] execve("/usr/bin/sleep", ["sleep", "10"], 0x7ffe6a45efc0 /* 30 vars */) = -1 ENOENT (No such file or directory)
[pid 12917] execve("/bin/sleep", ["sleep", "10"], 0x7ffe6a45efc0 /* 30 vars */ <unfinished ...>
[pid 12916] kill(12917, SIGINT) = 0
[pid 12917] <... execve resumed> ) = 0
[pid 12917] --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=12916, si_uid=1000} ---
[pid 12917] +++ killed by SIGINT +++
3.Как отправить Shift, Ctrl и Esc
Esc очень просто: вы просто отправляете его значение ASCII.Вот man ascii
:
033 27 1B ESC (escape)
Shift и Control - модификаторы, так что вы на самом деле их не отправляете.Вы просто изменяете персонажа, которого отправляете.# 1 уже рассказывалось о том, как это сделать для Ctrl на простых символах ascii.
Для сдвига вы должны просто прописать интересующий вас символ, используя соответствующие строковые функции.Традиционная аппаратная логика очистки / установки бита 6 не работает хорошо для символов Юникода, но 'c' == 'C'+0x20
, если вам это нравится.
Обратите внимание, что это не относится к таким вещам, как клавиши со стрелками, где вам нужнодля отправки другого управляющего кода ANSI, соответствующего терминалу, который вы пытаетесь эмулировать.
Для полноты Alt / Meta имеет две формы: Традиционно устанавливающий бит 8 (в основном не рекомендуется по причинам Unicode) или Meta-As-Бегите куда вы просто отправляете два байта ESC
x
для Alt+x
.
4.Эквивалент C для трубопровода xte
from subprocess import Popen, PIPE
shift_a_sequence = '''keydown Shift_L
key A
keyup Shift_L
'''
def keypress(sequence):
p = Popen(['xte'], stdin=PIPE)
p.communicate(input=sequence)`
Это открывает утилиту X11, которая имитирует последовательность клавиш X11.Если вы запускаете программу в XTerm и не переключаете фокус, мы надеемся, что это закончится в терминале, где вы начали, но это вовсе не гарантия.Это определенно не похоже на # 3.
Если вы хотите сделать это, вы можете использовать popen
из C почти таким же образом, но это не поможет вам сделать то, что вы описываете в тексте..
Полный пример отправки Ctrl+C
, адаптированный из вашего кода, можно найти здесь:
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define __USE_BSD
#include <termios.h>
#define guard(x) do { if((x)<0) { perror(#x); exit(1); } } while(0);
int main(void)
{
int fdm, fds, rc;
char input[150];
fdm = posix_openpt(O_RDWR);
guard(grantpt(fdm));
guard(unlockpt(fdm));
if (fork())
{
char* output = "sleep 60\n";
// Start a long sleep
write(fdm, output, strlen(output));
// Wait and send Ctrl-C to abort it after 1 second
sleep(1); write(fdm, "\x03", 1);
// Make sure shell is still alive
output = "echo 'Shell is still alive'\n";
write(fdm, output, strlen(output));
// Wait and send Ctrl-D to exit
sleep(1); write(fdm, "\x04", 1);
while((rc = read(fdm, input, sizeof(input)-1)) > 0) {
input[rc] = 0;
printf("From PTY:\n%s\n", input);
}
close(fdm);
wait(NULL);
}
else
{
setsid();
guard(fds = open(ptsname(fdm), O_RDWR));
close(fdm);
dup2(fds, 0);
dup2(fds, 1);
dup2(fds, 2);
close(fds);
execlp("sh", "sh", "-i", NULL);
}
return 0;
} // main
Выполнение показывает, что режим сна прерывается, оболочка продолжается и, наконец,выход:
$ ./foo
From PTY:
sleep 60
$ ^C
echo 'Shell is still alive'
$ Shell is still alive
$