Chinaunix首页 | 论坛 | 博客
  • 博客访问: 748044
  • 博文数量: 370
  • 博客积分: 2334
  • 博客等级: 大尉
  • 技术积分: 3222
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-06 16:56
文章分类

全部博文(370)

文章存档

2013年(2)

2012年(368)

分类:

2012-05-16 12:53:27

++++++APUE读书笔记-19伪终端-03打开伪终端设备(1)++++++

 

3、打开伪终端设备(1)
================================================
 我们打开伪终端设备的方式随平台有所不同。Single UNIX Specification将一些函数作为XSI扩展,尝试统一这些方法。这些扩展基于一些函数,这些函数原来是用来为System V4提供基于流的伪终端管理。
 posix_openpt函数提供了一个可移植的方法,以打开一个可用的伪终端主设备。
 #include
 #include
 int posix_openpt(int oflag);
 返回:如果成功返回下一个可用的PTY master的文件描述符号,如果错误返回1。
 oflag参数是一个位掩码,指定主设备如何被打开,它和open函数的参数类似,但是也不是支持所有的open标记。通过使用posix_openpt,我们可以指定O_RDWR打开主设备用于读和写,我们也可以指定O_NOCTTY来阻止主设备变成调用者的控制终端,其他的open标记的行为都是不可知的。
 
 在一个slave伪终端可以被使用之前,需要设置它的权限,以便应用程序能够访问它。grantpt函数就做了这样的事情,它设置从设备节点的user ID为调用者的real user ID,并设置节点的group ID为一个未指定的值,一般来说,是一些具有访问终端设备权限的组。可以设置权限允许特定的属主进行读写访问以及特定的组属主进行写访问(即0620)。
 #include
 int grantpt(int filedes);
 int unlockpt(int filedes);
 两者返回:如果成功返回0,如果错误返回1。
 为了修改从设备节点的权限,grantpt可能需要调用fork和exec一个set-user-ID程序(例如Solaris上面的/usr/lib/pt_chmod)。因此,如果调用者捕获了SIGCHLD信号,那么行为就是不确定的了(捕获了说明子进程停止或者终止了,但是为什么???)。
 unlockpt函数用于给slave伪终端设备赋予访问权限,从而允许应用程序打开设备。通过阻止其他的应用程序打开从(slave)设备,设置设备的应用程序可以有机会在主从设备可以被使用之前,对它们进行合适的初始化。
 注意,对于grantpt和unlockpt函数,文件描述符号参数(filedes)是和master(主)伪终端设备关联的文件描述符号。
 
 ptsname函数可以通过给定的主设备的文件描述符号,获取slave伪终端设备的路径名称。这允许应用程序可以独立于特定平台导致的一些特性,来辨别slave。注意,返回的name可能会被存放在静态内存当中,这样它会被后来的调用覆盖。
 #include
 char *ptsname(int filedes);
 返回:如果成功返回一个指向PTY slave的指针,如果错误返回NULL。
 
 下表列出了Single UNIX Specification所支持的伪终端函数,并且标识了哪些函数在本书所讨论的平台中是被支持的。
 
          XSI的伪终端函数
+--------------------------------------------------------------------------------------------------------------------------+
|   Function   |               Description                | XSI | FreeBSD 5.2.1 | Linux 2.4.22 | Mac OS X 10.3 | Solaris 9 |
|--------------+------------------------------------------+-----+---------------+--------------+---------------+-----------|
| grantpt      | Change permissions of slave PTY device.  |  •  |       •       |      •       |               |     •     |
|--------------+------------------------------------------+-----+---------------+--------------+---------------+-----------|
| posix_openpt | Open a master PTY device.                |  •  |       •       |              |               |           |
|--------------+------------------------------------------+-----+---------------+--------------+---------------+-----------|
| ptsname      | Return name of slave PTY device.         |  •  |       •       |      •       |               |     •     |
|--------------+------------------------------------------+-----+---------------+--------------+---------------+-----------|
| unlockpt     | Allow slave PTY device to be opened.     |  •  |       •       |      •       |               |     •     |
+--------------------------------------------------------------------------------------------------------------------------+
 
 在FreeBSD中,unlockpt什么也不做,O_NOCTTY标记只是为了兼容调用posix_openpt函数的应用程序。FreeBSD在打开一个终端设备的时候,并不会为它分配一个控制终端,所以O_NOCTTY标记没有任何作用。
 
 尽管Single UNIX Specification尝试在这里提高移植特性,但是正如上表所示实现上还是有些问题。因此,我们提供了两个函数处理所有的细节:ptym_open函数打开下一个可用的PTY master设备,ptys_open打开相应的slave设备。
 #include "apue.h"
 int ptym_open(char *pts_name, int pts_namesz);
 返回:如果成功返回PTY master的文件描述符号,如果错误返回1。
 int ptys_open(char *pts_name);
 返回:如果成功返回PTY slave的文件描述符号,如果错误返回1。
 一般,我们不会直接调用这两个函数;函数pty_fork会调用他们,也会fork子进程。
 ptym_open函数确定下一个可用的PTY master并且打开这个设备。调用者必须分配一个数组来存储master或者slave的名称;如果调用成功,那么相应的slave会通过pts_name返回。这个名字然后被传递给ptys_open,它会打开slave设备。缓存的字节长度会被通过pts_namesz参数传递,这样ptym_open函数就不会拷贝一个比缓存长度还长的字符串了。
 提供这两个函数来打开两个设备的原因,在我们展示pty_fork函数的时候会很明显的。一般,一个进程调用ptym_open来打开master并且获得slave的名称。进程然后进行fork,然后子进程调用setsid建立一个新的会话之后将调用ptys_open来打开slave。slave就是这样成为子进程的控制终端的。
 
 (1)基于流的伪终端
 下一个可用的PTY master设备通过一个STREAMS的克隆设备来进行访问。克隆设备就是一个特殊的设备,这个设备在被打开的时候会返回一个没有使用的设备。
 
 基于STREAMS的PTY master克隆设备是/dev/ptmx。当我们打开它的时候,这个克隆打开函数会自动确认第一个没有使用的PTY master设备,然后打开那个没有使用的设备(后面我们将会看到,在基于BSD的系统中,我们需要自己寻找第一个没有使用的PTY master设备)。
 
 基于流的伪终端打开函数
 #include "apue.h"
 #include
 #include
 #include
 int ptym_open(char *pts_name, int pts_namesz)
 {
     char    *ptr;
     int     fdm;
 
     /*
      * Return the name of the master device so that on failure
      * the caller can print an error message.  Null terminate
      * to handle case where strlen("/dev/ptmx") > pts_namesz.
      */
     strncpy(pts_name, "/dev/ptmx", pts_namesz);
     pts_name[pts_namesz - 1] = '\0';
     if ((fdm = open(pts_name, O_RDWR)) < 0)
         return(-1);
     if (grantpt(fdm) < 0) {     /* grant access to slave */
         close(fdm);
         return(-2);
     }
     if (unlockpt(fdm) < 0) {    /* clear slave's lock flag */
         close(fdm);
         return(-3);
     }
     if ((ptr = ptsname(fdm)) == NULL) { /* get slave's name */
         close(fdm);
         return(-4);
     }
 
     /*
      * Return name of slave.  Null terminate to handle
      * case where strlen(ptr) > pts_namesz.
      */
     strncpy(pts_name, ptr, pts_namesz);
     pts_name[pts_namesz - 1] = '\0';
     return(fdm);            /* return fd of master */
 }
 
 int ptys_open(char *pts_name)
 {
     int     fds, setup;
 
     /*
      * The following open should allocate a controlling terminal.
      */
     if ((fds = open(pts_name, O_RDWR)) < 0)
         return(-5);
 
     /*
      * Check if stream is already set up by autopush facility.
      */
     if ((setup = ioctl(fds, I_FIND, "ldterm")) < 0) {
         close(fds);
         return(-6);
     }
     if (setup == 0) {
         if (ioctl(fds, I_PUSH, "ptem") < 0) {
             close(fds);
             return(-7);
         }
         if (ioctl(fds, I_PUSH, "ldterm") < 0) {
             close(fds);
             return(-8);
         }
         if (ioctl(fds, I_PUSH, "ttcompat") < 0) {
             close(fds);
             return(-9);
         }
     }
     return(fds);
 }
 
 我们首先打开克隆设备/dev/ptmx来获取PTY master的文件描述符号。打开这个master 设备会自动锁住相应的slave 设备。
 然后我们调用grantpt来改变slave设备的权限。在Solaris上面,会将slave设备的属主改变成real user ID,并且改变其组属主为tty组,以及将其许可权限修改成允许用户读、写,以及组写。将组属主修改成tty以及打开组写的权限的原因就是程序wall和write是被set-group-ID为tty组的。调用grantpt会执行/usr/lib/pt_chmod程序,这个程序是被set-user-ID为root的这样它能够修改slave的属主。
 函数unlockpt被调用以清理slave设备的内部锁状态。我们需要在打开slave之前做这一步。我们必须调用ptsname来获取slave设备的名字,这个名字的形式一般为/dev/pts/NNN。
 下一个函数是ptys_open。这个函数会实际打开slave设备。Solaris遵从以前的System V的动作:如果调用者是一个没有控制终端的session leader,那么这个open会将PTY slave分配成为一个控制终端。如果我们不想这么做,那么我们需要在open的时候指定O_NOCTTY标记。
 打开slave设备之后,我们可能需要将三个STREAMS模块推送到slave流上面。伪终端模拟模块(ptem)和终端行规则模块(ldterm)一起,使得其行为如同一个终端。ttcompat模块提供了对原来的V7系统、4BSD系统以及Xenix ioctl调用的兼容,它是一个可选的模块,但是因为它在终端登陆以及网络登陆的时候会被自动推送,我们也会将它推送到slave流上面。
 如果这三个模块已经存在,那么我们不需要推送它们。STREAMS系统提供了一个叫做autopush的工具,这个工具允许管理者配置一个模块的列表,这些模块会在特定的终端设备打开的时候被推送到一个流上面。我们使用ioctl的I_FIND命令来查看ldterm是否已经被推送到流上面,如果已经推送上去了,那么我们会假定流已经被autopush机制配置好了,就不用再次推送这个模块了。
 调用ptym_open和ptys_open的结果就是给调用进程打开了两个文件描述符号:一个用于master,另外一个用于slave。
 
参考:

 


 

阅读(663) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~