如何在C中使用shift + Ctrl键获得完全交互式的bash终端?

时间:2019-03-01 19:29:31

标签: c linux bash

我想知道如何创建一个完全交互式的终端。我正在创建一个这样的交互式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)
  1. 如何发送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 :(得分:0)

您似乎拥有所需的大多数框架:

  • 创建一个假终端
    • 在孩子中,将奴隶pup dup2设置为stdin / stdout / stderr
    • 执行外壳程序
  • 在父级中,通过主pty与shell交互

要详细了解问题出在哪里,您需要提供Minimal, Complete, and Verifiable example,而不是模糊的描述。

答案 1 :(得分:0)

这里有四个独立的问题:

1。如何在PTY上触发Ctrl-C

在接收到Ctrl-C作为输入时(在熟模式下),由终端的线路规程触发信号。您可以通过发送0x03来做到这一点,它等于清除了第7位的大写ASCII'C':

write(fdm, "\x03", 1);

2。 process.send_signal

的C等效项
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已经介绍了如何在简单的ascii字符上执行Ctrl键。

要进行移位,您应该只使用适当的字符串函数将要关注的字符大写。清除/设置位6的传统硬件逻辑不适用于Unicode字符,但是如果您愿意,可以使用'c' == 'C'+0x20

请注意,这不适用于箭头键之类的东西,在这种情况下,您需要发送与要模拟的终端相对应的不同ANSI转义代码。

为完整起见,Alt / Meta具有两种形式:传统上将bit 8设置(多数出于Unicode原因而弃用),或Meta-As-Escape,您只需将两个字节ESC x发送给{ {1}}。


4。与Alt+x

的管道的C等效项
xte

这将打开一个X11实用程序,该实用程序模拟X11按键序列。如果您在XTerm中运行该程序并且不切换焦点,则希望最终会在您启动的终端中结束,但这完全不能保证。绝对不像#3。

但是,如果您想这样做,则可以使用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)` ,方式几乎相同,但这并不能帮助您完成任何用文字描述的事情。


您可以在此处找到根据代码发送改编的popen的完整示例:

Ctrl+C

执行表明睡眠被中断,shell继续运行,最后退出:

#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