pseudo-terminal 基础一

主要函数

NAME

openpty, login_tty, forkpty - tty utility functions

SYNOPSIS

      #include <pty.h>

      int openpty(int *amaster, int *aslave, char *name,
                  struct termios *termp, struct winsize *winp);

      pid_t forkpty(int *amaster, char *name, struct termios *termp,
                    struct winsize *winp);

      #include <utmp.h>

      int login_tty(int fd);

Link with -lutil.

DESCRIPTION

  • The openpty() function finds an available pseudo-terminal and returns file descriptors for the master and slave in amaster and aslave. If name is not NULL, the filename of the slave is returned in name. If termp is not NULL, the terminal parameters of the slave will be set to the values in termp. If winp is not NULL, the window size of the slave will be set to the values in winp.
  • The login_tty() function prepares for a login on the tty fd (which may be a real tty device, or the slave of a pseudo-terminal as returned by openpty()) by creating a new session, making fd the controlling terminal for the calling process, setting fd to be the standard input, output, and error streams of the current process, and closing fd.
  • The forkpty() function combines openpty(), fork(2), and login_tty() to create a new process operating in a pseudo-terminal. The file descriptor of the master side of the pseudo-terminal is returned in amaster, and the filename of the slave in name if it is not NULL. The termp and winp arguments, if not NULL, will determine the terminal attributes and window size of the slave side of the pseudo-terminal.

RETURN VALUE

If a call to openpty(), login_tty(), or forkpty() is not successful, -1 is returned and errno is set to indicate the error. Otherwise, openpty(), login_tty(), and the child process of forkpty() return 0, and the parent process of forkpty() returns the process ID of the child process.

ERRORS

  openpty() will fail if:
  • ENOENT There are no available ttys.
  • login_tty() will fail if ioctl(2) fails to set fd to the controlling terminal of the calling process.
  • forkpty() will fail if either openpty() or fork(2) fails.

CONFORMING TO

These are BSD functions, present in libc5 and glibc2.

用例

通常的例子像下面一样
图片.png

用例一

交互式

//
// Created by        :  Harris Zhu
// Filename          :  main.c
// Author            :  Harris Zhu
// Created On        :  2017-11-26 18:06:44
// Last Modified      :  2017-11-26 18:06:44
// Update Count      :  1
// Tags              :  
// Description        :  
// Conclusion        :  
//                      
//=======================================================================

#define _XOPEN_SOURCE 600 
#include <stdlib.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <string.h> 
# include <pty.h> /* for openpty and forkpty */
#include <termios.h> 

int main(void) 
{ 
    int fdm, fds, rc; 
    char input[150]; 
    char sptyname[20]={'\0'};
    int rtn;
    int i=0;

    rtn = openpty(&fdm, &fds, sptyname, NULL, NULL);
    printf("slave name is %s\n", sptyname);

    if (rtn != -1) 
    {
        if (fork()) 
        { 
            // parent

            close(fds); 
            while (i<50) 
            { 
                write(1, "Input : ", sizeof("Input : ")); 
                rc = read(0, input, sizeof(input)); 
                if (rc > 0) 
                {
                    // Write the input to the child process through PTY 
                    write(fdm, input, rc); 

                    // Read the child's answer through PTY 
                    rc = read(fdm, input, sizeof(input) - 1); 
                    if (rc > 0) 
                    { 
                        input[rc] = '\0'; 
                        printf("%s", input);
                    } 
                    else 
                    { 
                        break; 
                    } 
                } 
            } // end while 
        } 
        else 
        { 
            // Child
            close(fdm); 
            struct termios slave_orig_term_settings; // Saved terminal settings 
            struct termios new_term_settings; // Current terminal settings 

            // Save the default parameters of the slave side of the PTY 
            rc = tcgetattr(fds, &slave_orig_term_settings); 

            // Set raw mode on the slave side of the PTY
            new_term_settings = slave_orig_term_settings; 
            cfmakeraw (&new_term_settings); 
            tcsetattr (fds, TCSANOW, &new_term_settings); 

            // The slave side of the PTY becomes the standard input and outputs of the child process 
            close(0); // Close standard input (current terminal) 
            close(1); // Close standard output (current terminal) 
            close(2); // Close standard error (current terminal) 

            dup(fds); // PTY becomes standard input (0) 
            dup(fds); // PTY becomes standard output (1) 
            dup(fds); // PTY becomes standard error (2) 

            while (i++<50) 
            { 
                rc = read(fds, input, sizeof(input) - 1); 

                if (rc > 0) 
                { 
                    input[rc - 1] = '\0'; 
                    printf("Child received : '%s'\n", input); 
                } 
                else 
                { 
                    break; 
                } 
            } // End while 
        } 
    }
    return 0; 
}

gcc -o main main0.c -lutil

例子二

下面是非交互的例子

//
// Created by        :  Harris Zhu
// Filename          :  main.c
// Author            :  Harris Zhu
// Created On        :  2017-11-26 18:06:44
// Last Modified      :  2017-11-26 18:06:44
// Update Count      :  1
// Tags              :  
// Description        :  
// Conclusion        :  
//                      
//=======================================================================

#define _XOPEN_SOURCE 600 
#include <stdlib.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <string.h> 
# include <sys/types.h>
# include <linux/limits.h>
# include <pty.h> /* for openpty and forkpty */
#include <utmp.h> /* for login_tty */
#include <termios.h> 

int main(void) 
{ 
    int fdm, fds, rc; 
    char sptyname[20]={'\0'};
    char input[150]; 
    int rtn;
    int i=0;

    rtn = openpty(&fdm, &fds, sptyname, NULL,NULL);
    if(rtn != -1) 
    {
        printf("slave name is %s\n", sptyname);
        if (fork()) 
        { 
            // Parents
            close(fds); 
            while (i<50) 
            { 
                sprintf(input, "the time is %d\n", i++);
                write(fdm, input, strlen(input)); 

                rc = read(fdm, input, sizeof(input) - 1); 
                if (rc > 0) 
                { 
                    input[rc] = '\0'; 
                    printf("%s", input); 
                } 
                else 
                { 
                    break; 
                } 
            } // End while 
        } 
        else 
        { 
            // Child
            close(fdm); 
            struct termios slave_orig_term_settings; // Saved terminal settings 
            struct termios new_term_settings; // Current terminal settings 

            // Save the default parameters of the slave side of the PTY 
            rc = tcgetattr(fds, &slave_orig_term_settings); 

            // Set raw mode on the slave side of the PTY
            new_term_settings = slave_orig_term_settings; 
            cfmakeraw (&new_term_settings); 
            tcsetattr (fds, TCSANOW, &new_term_settings); 

            // The slave side of the PTY becomes the standard input and outputs of the child process 
            close(0); // Close standard input (current terminal) 
            close(1); // Close standard output (current terminal) 
            close(2); // Close standard error (current terminal) 

            dup(fds); // PTY becomes standard input (0) 
            dup(fds); // PTY becomes standard output (1) 
            dup(fds); // PTY becomes standard error (2) 

            int i=0;
            while (i++<50) 
            { 
                rc = read(fds, input, sizeof(input) - 1); 

                if (rc > 0) 
                { 
                    input[rc - 1] = '\0'; 
                    printf("Child received : '%s'\n", input); 
                } 
                else 
                { 
                    break; 
                } 
            } // End while 
        } 
    }
    return 0; 
}

上面如果在child里不创建新的term属性,那么在parent进程里,每次write到mpty里的字符会被下一行的read读到,然后输出就会是重复的字符
On the slave side we can note the calls to cfmakeraw() and tcsetattr() to reconfigure the slave side of the pty. This sets the raw mode to disable the echoing among other things.

$ ./main
slave name is /dev/pts/1
the time is 0
Child received : 'the time is 0'
the time is 1
Child received : 'the time is 1'
the time is 2
Child received : 'the time is 2'
the time is 3
Child received : 'the time is 3'
the time is 4
Child received : 'the time is 4'
the time is 5
Child received : 'the time is 5'
the time is 6
Child received : 'the time is 6'
the time is 7
Child received : 'the time is 7'
the time is 8

奇怪的是把上面的cfmakeraw()和tcsetattr()改成login_tty(fds), 还是有回显问题。

用例三

读取另一个程序的输入输出

//
// Created by        :  Harris Zhu
// Filename          :  main0.c
// Author            :  Harris Zhu
// Created On        :  2017-11-29 18:02:08
// Last Modified      :  2017-11-29 18:02:08
// Update Count      :  1
// Tags              :  
// Description        :  
// Conclusion        :  
//                      
//=======================================================================

#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <pty.h>
#include <utmp.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <string.h>


int main(int ac, char *av[])
{
    int fdm, fds;
    int rc;
    char input[150];
    char fdsname[150];

    // Check arguments
    if (ac <= 1)
    {
        fprintf(stderr, "Usage: %s program_name [parameters]\n", av[0]);
        exit(1);
    }

    if (openpty(&fdm, &fds, fdsname, NULL, NULL) == -1)
    {
        fprintf(stderr, "error open pty pairs\n");
        exit(1);
    } else {
        printf("the slave is %s\n", fdsname);
    }

    // Create the child process
    if (fork())
    {
        // FATHER
        // Close the slave side of the PTY
        close(fds);
        fd_set fd_in;

        while (1)
        {
            // Wait for data from standard input and master side of PTY
            FD_ZERO(&fd_in);
            FD_SET(0, &fd_in);
            FD_SET(fdm, &fd_in);

            rc = select(fdm + 1, &fd_in, NULL, NULL, NULL);
            switch(rc)
            {
            case -1 : 
                {
                    fprintf(stderr, "Error %d on select()\n", errno);
                    exit(1);
                }
            default :
                {
                    // If data on standard input
                    if (FD_ISSET(0, &fd_in))
                    {
                        rc = read(0, input, sizeof(input));
                        if (rc > 0)
                        {
                            // Send data on the master side of PTY
                            write(fdm, input, rc);
                        }
                        else
                        {
                            if (rc < 0)
                            {
                                fprintf(stderr, "Error %d on read standard input\n", errno);
                                exit(1);
                            }
                        }
                    }

                    // If data on master side of PTY
                    if (FD_ISSET(fdm, &fd_in))
                    {
                        rc = read(fdm, input, sizeof(input));
                        if (rc > 0)
                        {
                            // Send data on standard output
                            write(1, input, rc);
                        }
                        else
                        {
                            if (rc < 0)
                            {
                                fprintf(stderr, "Error %d on read master PTY\n", errno);
                                exit(1);
                            }
                        }
                    }
                }
            } // End switch
        } // End while
    }
    else
    {
        // CHILD
        // Close the master side of the PTY
        close(fdm);
        struct termios slave_orig_term_settings; // Saved terminal settings
        struct termios new_term_settings; // Current terminal settings

        // Save the defaults parameters of the slave side of the PTY
        rc = tcgetattr(fds, &slave_orig_term_settings);

        // Set RAW mode on slave side of PTY
        new_term_settings = slave_orig_term_settings;
        cfmakeraw (&new_term_settings);
        tcsetattr (fds, TCSANOW, &new_term_settings);

        // The slave side of the PTY becomes the standard input and outputs of the child process
        close(0); // Close standard input (current terminal)
        close(1); // Close standard output (current terminal)
        close(2); // Close standard error (current terminal)

        dup(fds); // PTY becomes standard input (0)
        dup(fds); // PTY becomes standard output (1)
        dup(fds); // PTY becomes standard error (2)

        // Now the original file descriptor is useless
        close(fds);

        // Make the current process a new session leader
        setsid();

        // As the child is a session leader, set the controlling terminal to be the slave side of the PTY
        // (Mandatory for programs like the shell to make them manage correctly their outputs)
        // ioctl(0, TIOCSCTTY, 1);
        ioctl(0, TIOCSCTTY, 1);

        // Execution of the program
        {
            char **child_av;
            int i;

            // Build the command line
            child_av = (char **)malloc(ac * sizeof(char *));
            for (i = 1; i < ac; i ++)
            {
                child_av[i - 1] = strdup(av[i]);
            }
            child_av[i - 1] = NULL;
            rc = execvp(child_av[0], child_av);
        }

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

运行结果如下:

$ ./main bc
the slave is /dev/pts/1
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
1+2
3
4*5
20
quit
Error 5 on read master PTY

telnet结构

图片.png

expect的结构

图片.png

阅读 1.6k

推荐阅读
验证的进阶之路
用户专栏

虽然从事的是芯片验证行业, 但对计算机体系结构,芯片架构,设计,验证,后端,软件前端,后端,C/C++,...

24 人关注
41 篇文章
专栏主页