美文网首页LinuxLinux学习之路
APUE读书笔记-19伪终端(7)

APUE读书笔记-19伪终端(7)

作者: QuietHeart | 来源:发表于2020-10-03 07:00 被阅读0次

    自定义改进的伪终端处理函数

    尽管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 <errno.h>
    #include <fcntl.h>
    #include <stropts.h>
    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。

    相关文章

      网友评论

        本文标题:APUE读书笔记-19伪终端(7)

        本文链接:https://www.haomeiwen.com/subject/cgtluktx.html