Как получить полностью интерактивный bash-терминал в C с клавишами shift + ctrl? - PullRequest
0 голосов
/ 01 марта 2019

Я хочу знать, как создать полностью интерактивный терминал.Я создаю интерактивный терминал Bash, как это:

fds = open(ptsname(fdm), O_RDWR);

if (fork())
{
     ....
}
else
{
    ...

    ioctl(0, TIOCSCTTY, 1);

    // Execution of the program
    {

            char *child_av[] = {"/bin/sh", "-i", NULL};


            rc = execvp(child_av[0], child_av);
    }

    // if Error...
    return -1;
}

Затем я могу использовать read () / write () для отправки команд и получения вывода.Моя проблема в том, как мне автоматизировать клавиши CTRL и SHIFT?Подпроцесс Python делает это, так что это определенно возможно.

  1. Как отправить Ctrl-C, чтобы убить процесс переднего плана?В Python это будет следующим, и я хочу знать, как это выглядит в C:
process = subprocess.Popen(..) ...
process.send_signal(signal.SIGINT)
Как отправить клавиши shift, ctrl, esc?Например, как открыть nano, затем отправить ctrl-a-esc?В Python это будет следующим, и я хочу знать, как это выглядит в C:
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)`

Ответы [ 2 ]

0 голосов
/ 02 марта 2019

Здесь есть четыре отдельных вопроса:

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
$
0 голосов
/ 01 марта 2019

Похоже, у вас есть большая часть необходимой инфраструктуры:

  • создайте псевдо-терминал
  • fork
    • в дочернем элементе, dup2 подчиненного pty в stdin/ stdout / stderr
    • exec оболочка
  • в родительском объекте, взаимодействовать с оболочкой через мастер pty

Подробнее огде вы идете не так, вам нужно предоставить Минимальный, Полный и Поддающийся проверке пример того, что вы на самом деле пытаетесь сделать, а не только смутное описание.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...