• Unix环境高级编程(二十)伪终端


    1、综述

      伪终端对于一个应用程序而言,看上去像一个终端,但事实上伪终端并不是一个真正的终端。从内核角度看,伪终端看起来像一个双向管道,而事实上Solaris的伪终端就是用STREAMS构建的。伪终端总是成对地使用的,就好像是个管道的两端。一端的设备称为"主设备"(master),另一端的设备称为"从设备"(slave),每一对伪终端设备,例如/dev/ptys0和/dev/ttys0,就好像是通过一个管道连在一起,其"从设备"一端与普通的终端设备没有什么区别,而"主设备"一端则跟管道文件相似。

    伪终端的用途:

    (1)构造网络登录服务器,例如telnetd和rlogind服务器。

    (2)script程序,将终端会话的所有输入和输出信息复制到一个文件中,自己置于终端和登录shell的一个新调用之间。

    (3)expect程序,伪终端可以在非交互模式中驱动交互程序的运行

    (4)运行协同进程

    (5)观看长时间运行程序的输出

    2、打开伪终端设备

      各种平台打开伪终端设备的方法有所不同,posix_openpt用来打开一个可用的伪终端主设备,该函数可移植的。伪终端从设备可被使用之前,必须设置它的权限,调用grantpt函数设置权限,使得应用程序可以访问它。unlockpt函数批准读伪终端从设备的访问,从而允许应用程序打开该设备。ptsname函数找到从伪终端设备的路径名。函数原型如下:

    #include <stdlib.h>
    #include <fcntl.h>
    int posix_openpt(int oflag); //成功返回下一个可以用的PTY主设备的文件描述符,出错返回-1
    int grantpt(int fildes); //更改从PTY设备的权限
    int unlockpt(int fildes) //允许从PTY设备被打开
    char *ptsname(int fildes); //成功返回指向PTY从设备名的指针,出错返回NULL

    写个程序调用以上函数获取一个可用的PTY主设备描述符,然后获取该主设备的从伪终端的路径名。程序如下:

    复制代码
     1 #define _XOPEN_SOURCE
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <fcntl.h>
     6 
     7 int main()
     8 {
     9     int masterfd,slavefd;
    10     char *slavedevice;
    11     if((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1)
    12     {
    13         perror("posix_openpt() error");
    14         exit(-1);
    15     }
    16     if(grantpt(masterfd) == -1)
    17     {
    18         perror("grantpt() error");
    19         exit(-1);
    20     }
    21     if(unlockpt(masterfd) == -1)
    22     {
    23         perror("unlockpt() error");
    24         exit(-1);
    25     }
    26     if((slavedevice=ptsname(masterfd)) == NULL)
    27     {
    28         perror("ptsname() error");
    29         exit(-1);
    30     }
    31     printf("slave device is: %s
    ", slavedevice);
    32     exit(0);
    33 }
    复制代码

    程序执行结果如下:

     打开一个终端,输入tty 这个命令来查看当前所使用的终端名。参考自http://blog.csdn.net/heron804/article/details/8144103 

    介绍另外两个函数ptym_open和ptys_open,前者用于打开下一个可用的PTY主设备,后者打开相应的从设备。这两个函数需要自己实现,函数声明如下所示:

    int ptym_open(char *pts_name,int pts_namesz);

    int pyts_open(char *pts_name);

    3、基于STREAMS的伪终端

      基于STREAMS的PTY主克隆设备是/dev/ptmx。打开该设备,其克隆open例程自动决定第一个未被使用的PTY主设备,并打开这个设备。ptym_open和ptys_open实现如下所示:

     1 #define _XOPEN_SOURCE
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <fcntl.h>
     6 #include <stropts.h>
     7 #include <string.h>
     8 
     9 int ptym_open(char *pts_name,int pts_namesz)
    10 {
    11     char    *ptr;
    12     int     fdm;
    13     strncpy(pts_name,"/dev/ptmx",pts_namesz);
    14     pts_name[pts_namesz-1] = '';
    15     if((fdm = open(pts_name,O_RDWR)) < 0)
    16         return -1;
    17     if(grantpt(fdm) < 0)
    18     {
    19         close(fdm);
    20         return -2;
    21     }
    22     if(unlockpt(fdm) < 0)
    23     {
    24         close(fdm);
    25         return  -3;
    26     }
    27     if((ptr = ptsname(fdm)) == NULL)
    28     {
    29         close(fdm);
    30         return -4;
    31     }
    32     strncpy(pts_name,ptr,pts_namesz);
    33     pts_name[pts_namesz-1] = '';
    34     return (fdm);
    35 }
    36 int ptys_open(char *pts_name)
    37 {
    38     int     fds,setup;
    39     if((fds = open(pts_name,O_RDWR)) < 0)
    40         return -5;
    41     return fds;
    42 }
    43 int main()
    44 {
    45     int     fdm,fds;
    46     char    slave_name[20];
    47     fdm = ptym_open(slave_name,sizeof(slave_name));
    48     if(fdm<0)
    49     {
    50         perror("ptym_open() error");
    51         exit(-1);
    52     }
    53     printf("open master device successfully.
    ");
    54     printf("slave device name is:%s
    ",slave_name);
    55     fds = ptys_open(slave_name);
    56     if(fds < 0)
    57     {
    58         perror("ptys_open() error");
    59         exit(-1);
    60     }
    61     printf("open slave device successfully.
    ");
    62     exit(0);
    63 }

    测试结果如下:

     4、基于BSD的伪终端

       需要自己确定第一个可用的PTY主设备,主设备名为/dev/ptyAX(/dev/ptys0),这里的A表示16个字母"pqrstuvwxyQPRST"中的一个,X则为16个16进制数字(0~f)之一,这样一共可以256个伪终端主设备。从/dev/ptyp0开始不断尝试,直到成功打开一个可用的PTY主设备或试完了所有设备。ptym_open和ptys_open实现如下所示:

      1 #define _XOPEN_SOURCE
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <unistd.h>
      5 #include <fcntl.h>
      6 #include <stropts.h>
      7 #include <string.h>
      8 #include <errno.h>
      9 #include<grp.h>
     10 #include <sys/types.h>
     11 
     12 #ifndef _HAS_OPENPT
     13 int posix_openpt(int oflag)
     14 {
     15     int     fdm;
     16     char    *ptr1,*ptr2;
     17     char    ptm_name[16];
     18 
     19     strcpy(ptm_name,"/dev/ptyXY");
     20     for(ptr1="pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++)
     21     {
     22         ptm_name[8] = *ptr1;
     23         for(ptr2="0123456789abcdef";*ptr2 != 0;ptr2++)
     24         {
     25             ptm_name[9] = *ptr2;
     26             //try to open the master
     27             if((fdm = open(ptm_name,oflag)) < 0)
     28             {
     29                 if(errno == ENOENT)
     30                     return -1;
     31                 else
     32                     continue;
     33             }
     34             return fdm;
     35         }
     36     }
     37     errno = EAGAIN;
     38     return -1;
     39 }
     40 #endif
     41 
     42 #ifndef _HAS_PTSNAME
     43 char *ptsname(int fdm)
     44 {
     45     static char pts_name[16];
     46     char    *ptm_name;
     47     ptm_name = ttyname(fdm);
     48     if(ptm_name == NULL)
     49         return NULL;
     50     strncpy(pts_name,ptm_name,sizeof(pts_name));
     51     pts_name[sizeof(pts_name)-1] = '';
     52     if(strncmp(pts_name,"/dev/pty",9) == 0)
     53         pts_name[9] = 's';
     54     else
     55         pts_name[5] = 't';
     56     return pts_name;
     57 }
     58 #endif
     59 
     60 #ifndef _HAS_GRANTPT
     61 int grantpt(int fdm)
     62 {
     63     struct group    *grptr;
     64     int gid;
     65     char *pts_name;
     66 
     67     pts_name = ptsname(fdm);
     68     if((grptr = getgrnam("tty")) != NULL)
     69         gid = grptr->gr_gid;
     70     else
     71         gid = -1;
     72     if(chown(pts_name,getuid(),gid) < 0)
     73         return -1;
     74     return chmod(pts_name,S_IRUSR | S_IWUSR | S_IWGRP);
     75 }
     76 #endif
     77 
     78 #ifndef _HAS_UNLOCKPT
     79 int unlockput(int fdm)
     80 {
     81     return 0;
     82 }
     83 #endif
     84 int ptym_open(char *pts_name,int pts_namesz)
     85 {
     86     char    *ptr;
     87     int     fdm;
     88     strncpy(pts_name,"/dev/ptyXX",pts_namesz);
     89     pts_name[pts_namesz-1] = '';
     90     if((fdm = posix_openpt(O_RDWR)) < 0)
     91         return -1;
     92     if(grantpt(fdm) < 0)
     93     {
     94         close(fdm);
     95         return -2;
     96     }
     97     if(unlockput(fdm) < 0)
     98     {
     99         close(fdm);
    100         return -3;
    101     }
    102     if((ptr = ptsname(fdm)) < 0)
    103     {
    104         close(fdm);
    105         return -4;
    106     }
    107     strncpy(pts_name,ptr,pts_namesz);
    108     pts_name[pts_namesz-1] = '';
    109     return fdm;
    110 }
    111 int ptys_open(char *pts_name)
    112 {
    113     int     fds,setup;
    114     if((fds = open(pts_name,O_RDWR)) < 0)
    115         return -5;
    116     return fds;
    117 }

     5、基于Linux的伪终端

      Linux支持访问伪终端的BSD方法,也支持使用/dev/ptmx的克隆风格的伪终端接口。在Linux中PTY从设备以为组tty所拥有,ptym_open和ptys_open实现如下所示:

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <unistd.h>
      4 #include <fcntl.h>
      5 #include <stropts.h>
      6 #include <string.h>
      7 #include <errno.h>
      8 #include <sys/types.h>
      9 #include <asm/ioctl.h>
     10 
     11 #define TIOCGPTN    _IOR('T',0x30,  unsigned int) /* Get Pty Number (of pty-mux device) */
     12 #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
     13 
     14 #ifndef _HAS_OPENPT
     15 int posix_openpt(int oflag)
     16 {
     17     int     fdm;
     18     fdm = open("/dev/ptmx",oflag);
     19     return fdm;
     20 }
     21 #endif
     22 
     23 #ifndef _HAS_PTSNAME
     24 char *ptsname(int fdm)
     25 {
     26     static char     pts_name[16];
     27     int             sminor;
     28     if(ioctl(fdm,TIOCGPTN,&sminor) < 0)
     29         return NULL;
     30     snprintf(pts_name,sizeof(pts_name),"/dev/pts/%d",sminor);
     31     return pts_name;
     32 }
     33 #endif
     34 
     35 #ifndef _HAS_GRANTPT
     36 int grantpt(int fdm)
     37 {
     38     char *pts_name;
     39     pts_name = ptsname(fdm);
     40     return chmod(pts_name,S_IRUSR | S_IWUSR | S_IWGRP);
     41 }
     42 #endif
     43 
     44 #ifndef _HAS_UNLOCKPT
     45 int unlockput(int fdm)
     46 {
     47     int lock = 0;
     48     return (ioctl(fdm,TIOCSPTLCK,&lock));
     49 }
     50 #endif
     51 int ptym_open(char *pts_name,int pts_namesz)
     52 {
     53     char    *ptr;
     54     int     fdm;
     55     strncpy(pts_name,"/dev/ptmx",pts_namesz);
     56     pts_name[pts_namesz-1] = '';
     57     if((fdm = posix_openpt(O_RDWR)) < 0)
     58         return -1;
     59     if(grantpt(fdm) < 0)
     60     {
     61         close(fdm);
     62         return -2;
     63     }
     64     if(unlockput(fdm) < 0)
     65     {
     66         close(fdm);
     67         return -3;
     68     }
     69     if((ptr = ptsname(fdm)) < 0)
     70     {
     71         close(fdm);
     72         return -4;
     73     }
     74     strncpy(pts_name,ptr,pts_namesz);
     75     pts_name[pts_namesz-1] = '';
     76     return fdm;
     77 }
     78 int ptys_open(char *pts_name)
     79 {
     80     int     fds,setup;
     81     if((fds = open(pts_name,O_RDWR)) < 0)
     82         return -5;
     83     return fds;
     84 }
     85 int main()
     86 {
     87     int     fdm,fds;
     88     char    slave_name[20];
     89     fdm = ptym_open(slave_name,sizeof(slave_name));
     90     if(fdm<0)
     91     {
     92         perror("ptym_open() error");
     93         exit(-1);
     94     }
     95     printf("open master device successfully.
    ");
     96     printf("slave device name is:%s
    ",slave_name);
     97     fds = ptys_open(slave_name);
     98     if(fds < 0)
     99     {
    100         perror("ptys_open() error");
    101         exit(-1);
    102     }
    103     printf("open slave device successfully.
    ");
    104     exit(0);
    105 }

    执行结果如下所示:

     6、pty_fork函数

      函数功能:用fork调用打开主设备和从设备,创建作为会话首进程的子进程并使其具有控制终端。函数声明如下:

    #include <termios.h>
    #include <sys/ioctl.h>
    pid_t ptt_fork(int *ptrfdm,char *slave_name,int slave_names,const struct termiosz *slave_termios,const struct winsize *slave_winsize);

    函数实现如下所示:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <fcntl.h>
     5 #include <stropts.h>
     6 #include <string.h>
     7 #include <errno.h>
     8 #include <sys/types.h>
     9 #include <asm/ioctl.h>
    10 #include <termios.h>
    11 #ifndef IIOCGWINSZ
    12 #include <sys/ioctl.h>
    13 #endif
    14 pid_t    ptt_fork(int *ptrfdm,char *slave_name,int slave_namesz,
    15                const struct termios *slave_termios,
    16                const struct winsize *slave_winsize)
    17 {
    18     int     fdm,fds;
    19     pid_t   pid;
    20     char    pts_name[20];
    21 
    22     if((fdm=ptym_open(pts_name,sizeof(pts_name))) < 0)
    23     {
    24         perror("ptym_open()error");
    25         exit(-1);
    26     }
    27     if(slave_name != NULL)
    28     {
    29         strncpy(slave_name,pts_name,slave_namesz);
    30         slave_name[slave_namesz-1] = '';
    31     }
    32     if((pid = fork()) < 0)
    33     {
    34         perror("fork() error");
    35         exit(-1);
    36     }
    37     else if(pid == 0)
    38     {
    39         if(setsid() < 0)
    40         {
    41             perror("setsid() error");
    42             exit(-1);
    43         }
    44         if((fds = ptys_open(pts_name)) < 0)
    45         {
    46             perror("ptys_open() error");
    47             exit(-1);
    48         }
    49         close(fdm);
    50     #if defined (TIOCSCTTY)
    51         if(ioctl(fds,TIOCSCTTY,(char *)0) < 0)
    52         {
    53             perror("TIOCSCTTY error");
    54             exit(-1);
    55         }
    56     #endif
    57         if(slave_termios != NULL)
    58         {
    59             if(tcsetattr(fds,TCSANOW,slave_termios) < 0)
    60             {
    61                 perror("tcsetattr error on slave pty");
    62                 exit(-1);
    63             }
    64         }
    65         if(slave_winsize != NULL)
    66         {
    67             if(ioctl(fds,TIOCSWINSZ,slave_winsize) < 0)
    68             {
    69                 perror("TIOCSWINSZ error on slave pty");
    70                 exit(-1);
    71             }
    72         }
    73 
    74         if(dup2(fds,STDIN_FILENO) != STDIN_FILENO)
    75         {
    76             perror("dups error to stdin");
    77             exit(-1);
    78         }
    79         if(dup2(fds,STDOUT_FILENO) != STDOUT_FILENO)
    80         {
    81             perror("dups error to stdout");
    82             exit(-1);
    83         }
    84         if(dup2(fds,STDERR_FILENO) != STDERR_FILENO)
    85         {
    86             perror("dups error to stderr");
    87             exit(-1);
    88         }
    89         if(fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO)
    90             close(fds);
    91         return 0;
    92     }
    93     else
    94     {
    95         *ptrfdm = fdm;
    96         return pid;
    97     }
    98 }
  • 相关阅读:
    Git编译安装
    ES集群
    索引、分片以及副本的数量和大小原则:
    初识ELK
    zabbix自定义监控项没权限读取文件问题
    Zabbix的图形界面中文变成□□问题
    logrotate
    rsync
    Linux下的mail指令
    nohup
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8466209.html
Copyright © 2020-2023  润新知