在openpty之后ncurses newterm [英] ncurses newterm following openpty

查看:131
本文介绍了在openpty之后ncurses newterm的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图弄清楚如何做:

  1. 创建一个新的伪终端

  2. 打开在(从属)伪终端内运行的ncurses屏幕

  3. 叉子

  4. A)从正在(重击)运行程序的终端向新的(从属)终端转发I/O或

    B)退出,使ncurses程序在新的pty中运行.

任何人都可以提供指向我可能做错的事情的指针,或者这会使其中的一些有意义,甚至更好地使用带有 posix_openpt()、openpty() 或 forkpty() 的 newterm() 的示例程序.

我的代码大致是(细节被简化或省略了):

  openpty(master,slave,NULL,NULL,NULL);pid_t res = fork();if(res == -1)std :: exit(1);if(res == 0)//孩子{FILE * scrIn = open(从站,O_RDWR | O_NONBLOCK);FILE * scrOut = open(从站,O_RDWR | O_NONBLOCK);SCREEN * scr = newterm(NULL,scrIn,scrOut);}其他//父母{如果(!optionA)退出(0);//但让孩子运行并使用奴隶为了(;;){//将IO转发到从站fd_set read_fd;fd_set write_fd;fd_setexcept_fd;FD_ZERO(& read_fd);FD_ZERO(& write_fd);FD_ZERO(& except_fd);FD_SET(masterTty,& read_fd);FD_SET(STDIN_FILENO,& read_fd);选择(masterTty + 1,& read_fd,& write_fd,& except_fd,NULL);字符输入[2];字符输出[2];输入[1] = 0;输出[1] = 0;如果(FD_ISSET(masterTty,& read_fd)){if(read(masterTty,& output,1)!= -1){写(STDOUT_FILENO,& output,1);}}如果(FD_ISSET(STDIN_FILENO,& read_fd)){读取(STDIN_FILENO,& input,1);写入(masterTty,& input,1);}}}} 

我有各种调试例程,可将结果从父级和子级记录到文件中.

我不了解与终端相关的几件事.我看到了几种我不理解的行为,具体取决于我尝试的变化.

我不明白的事情:

  • 如果我指示父进程退出,则子进程终止,而子进程不会记录任何有趣的事情.

  • 如果我尝试关闭标准输入、标准输出并使用 dup() 或 dup2() 使 pty 成为替换标准输入curses窗口使用原始的stdin和stdout,并使用原始的pty而不是基于ptsname()输出的新的pty.(父进程成功地与子进程执行了IO,但在终端中不是从新的pty启动的)

  • 如果我使用open()打开新的pty,则会在ncurses newterm()调用中出现段错误,如下所示:

程序以信号11终止,分段错误.来自/lib64/libc.so.6的fileno_unlocked()中的#0 0x00007fbd0ff580a0缺少单独的debuginfo,请使用:debuginfo-install glibc-2.17-317.el7.x86_64 ncurses-libs-5.9-14.20130511.el7_4.x86_64(gdb)在哪里来自/lib64/libc.so.6的fileno_unlocked()中的#0 0x00007fbd0ff580a0来自/lib64/libncurses.so.5的newterm()中的#1 0x00007fbd106eced9...现在在我的程序中...

我试图了解此处的pty系统调用.使用 screen tmux 之类的程序无助于此(同样,注释来源也不足以填补我的理解空白).

一些其他基准:

我仍然不清楚:

  • login_tty/grantpt实际执行的操作.

    如果您自己打开pty,为什么还没有正确的功能?

  • 为什么我可能更喜欢openpty而不是posix_openpt或反之亦然.


注意:这与解决方案

Glärbo的答案已经帮助我充分理解了问题,经过一些试验,我相信我可以直接回答其余的问题.

要点是:

pty的主端必须保持打开状态

我:如果我指示父进程退出,则子进程终止,而子进程不会记录任何有趣的事情."

Glärbo:在没有主服务器的情况下,并且没有一个管理主服务器端的进程,实际上没有伪终端对:当主服务器关闭时,内核也会强行删除从服务器,从而使从服务器向服务器打开的文件描述符无效.伪终端对的从属端."

从站的文件描述符必须以与最初创建时相同的模式打开.

我不正确的伪代码(对于fork的子代):

  FILE * scrIn = open(从站,O_RDWR | O_NONBLOCK);FILE * scrOut = open(从站,O_RDWR | O_NONBLOCK);SCREEN * scr = newterm(NULL,scrIn,scrOut); 

如果替换为,则可以使用(省略错误检查):

  setsid();关闭(STDIN_FILENO);关闭(STDOUT_FILENO);const char * slave_pts = pstname(master);int slave = open(slave_pts,O_RDWR);ioctl(slave(TIOCTTY,0);关闭(主);dup2(从站,STDIN_FILENO);dup2(从站,STDOUT_FILENO);FILE * slaveFile = fdopen(slavefd,"r +");SCREEN * scr = newterm(NULL,slaveFile,slaveFile);(void)set_term(scr);printw("hello world \ n");//打印到curses窗口的内存中表示形式刷新();//将in mem rep复制到实际终端 

我认为一个错误的文件或文件描述符必须在未检查的情况下经过某个地方.这解释了fileno_unlocked()中的段错误.我也尝试过两次打开奴隶的实验.一次阅读,一次写作.该模式将与原始fd的模式发生冲突.

在子进程侧(没有从属pty)没有setsid()的情况下,子进程仍具有原始控制终端.

在使用newterm()而不是tha initscr()时,需要小心ncurses调用

许多ncurses函数具有隐式的"intscr".参数是指为控制终端STDIN和STDOUT创建的屏幕或窗口.除非用指定的WINDOW的等效ncurses()函数代替,否则它们将不起作用.您需要调用newwin()来创建一个窗口,newterm()只给您一个屏幕.

实际上,我仍在努力解决此类问题,例如对subwin()的调用在使用从属pty而不是与普通终端一起使用时失败.


还值得注意的是:

https://github.com/coreutils/gnulib/blob/master/lib/posix_openpt.c)

  • I don't have access to a copy of APUE though I have looked at the pty example.

  • Although the ncurses documentation for newterm() mentions talking to multiple terminals simultaneously I have not found an example program that does this.

  • I am still not clear on:

    • what login_tty / grantpt actually do.

      If you opened the pty yourself why wouldn't you already have the correct capabilities?

    • why I might prefer openpty to posix_openpt or visa-versa.


    Note: This is a different question to attach-a-terminal-to-a-process-running-as-a-daemon-to-run-an-ncurses-ui which describes a use case and looks for a solution where this question assumes a particular but incorrect/incomplete implementation for that use case.

    解决方案

    Glärbo's answers have helped me understand the problems enough that after some experimentation I believe I can answer my remaining questions directly.

    The important points are:

    • The master side of the pty must remain opened
    • The file descriptor for the slave must be opened in the same mode as originally created.
    • without setsid() on the slave it remains connected to the original controlling terminal.
    • You need to be careful with ncurses calls when using newterm rather tha initscr

    The master side of the pty must remain opened

    Me: "If I instruct the parent process exits the child terminates without anything interesting being logged by the child."

    Glärbo: "Without the master, and a process managing the master side, there is literally no pseudoterminal pair: when the master is closed, the kernel forcibly removes the slave too, invalidating the file descriptors the slave has open to the slave side of the pseudoterminal pair."

    The file descriptor for the slave must be opened in the same mode as originally created.

    My incorrect pseudo code (for the child side of the fork):

     FILE* scrIn = open(slave,O_RDWR|O_NONBLOCK);
     FILE* scrOut = open(slave,O_RDWR|O_NONBLOCK);
     SCREEN* scr = newterm(NULL,scrIn,scrOut);
    

    Works if replaced with (error checking omitted):

     setsid();
     close(STDIN_FILENO);
     close(STDOUT_FILENO);
     const char* slave_pts = pstname(master);
     int slave = open(slave_pts, O_RDWR);
     ioctl(slave(TIOCTTY,0);
     close(master);
     dup2(slave,STDIN_FILENO);
     dup2(slave,STDOUT_FILENO);
     FILE* slaveFile = fdopen(slavefd,"r+");
     SCREEN* scr = newterm(NULL,slaveFile,slaveFile);
     (void)set_term(scr);
     printw("hello world\n"); // print to the in memory represenation of the curses window
    refresh(); // copy the in mem rep to the actual terminal
    

    I think a bad file or file descriptor must have crept through somewhere without being checked. This explains the segfault inside fileno_unlocked(). Also I had tried in some experiments opening the slave twice. Once for reading and once for writing. The mode would have conflicted with the mode of the original fd.

    Without setsid() on the child side (with the slave pty) the child process still has the original controlling terminal.

    • setsid() makes the process a session leader. Only the session leader can change its controlling terminal.
    • ioctl(slave(TIOCTTY,0) - make slave the controlling terminal

    You need to be careful with ncurses calls when using newterm() rather tha initscr()

    Many ncurses functions have an implicit "intscr" argument which refers to a screen or window created for the controlling terminals STDIN and STDOUT. They doen't work unless replaced with the equivalent ncurses() functions for a specified WINDOW. You need to call newwin() to create a WINDOW, newterm() only gives you a screen.

    In fact I am still wrestling with this kind of issue such as a call to subwin() which fails when the slave pty is used but not with the normal terminal.


    It is also noteworthy that:

    • You need to handle SIGWINCH in the process connected to an actual terminal and pass that to the slave if it needs to knows the terminal size has changed.

    • You probably need a pipe to daemon to pass additional information.

    • I left stderr connect to the original terminal above for convenience of debugging. That would be closed in practice.

    attach a terminal to a process running as a daemon (to run an ncurses UI) does a better job of describing the use case than the specific issues troubleshooted here.

    这篇关于在openpty之后ncurses newterm的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆