首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >pty下的SSH仍然是来自终端的标准

pty下的SSH仍然是来自终端的标准
EN

Unix & Linux用户
提问于 2022-09-18 11:46:53
回答 1查看 110关注 0票数 3

我目前正在学习C中的伪终端功能,我能够创建pty的主、从端,在叉之后,我已经将stdin、stdout、stderr设置为fd从,在exec仍然从终端提示输入密码而不是从stdin(fds)中读取密码之后。当使用ssh复制流程会导致此行为时,其他程序将按预期工作。

当在子进程中使用setsid(); ioctl(0, TIOCSCTTY, 1);时,这个问题可以修复--在execvp之前,我找不到原因,这是一种预期的行为吗?

代码语言:javascript
复制
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#define __USE_BSD
#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[1024], output[150];

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

    fdm = posix_openpt(O_RDWR);
    if (fdm < 0)
    {
        fprintf(stderr, "Error %d on posix_openpt()\n", errno);
        return 1;
    }

    rc = grantpt(fdm);
    if (rc != 0)
    {
        fprintf(stderr, "Error %d on grantpt()\n", errno);
        return 1;
    }

    rc = unlockpt(fdm);
    if (rc != 0)
    {
        fprintf(stderr, "Error %d on unlockpt()\n", errno);
        return 1;
    }

    // Open the slave side ot the PTY
    fds = open(ptsname(fdm), O_RDWR);

    // Create the child process
    if (fork())
    {
        fd_set fd_in;

        // FATHER

        // Close the slave side of the PTY
        close(fds);
        int pass_entered = 0;
        while (1)
        {
            FD_ZERO(&fd_in);
            FD_SET(fdm, &fd_in);
            rc = select(fdm + 1, &fd_in, NULL, NULL, NULL);
            // If data on master side of PTY
            if (FD_ISSET(fdm, &fd_in))
            {
                rc = read(fdm, output, sizeof(input));
                if (rc > 0)
                {
                    // Send data on standard output
                    if (!pass_entered)
                    {
                        write(fdm, "password\n", 10);
                        pass_entered = 1;
                    }
                    write(2, "<<", 2);
                    write(2, output, rc);
                    write(2, ">>", 2);
                    int n = write(fdm, "id\n", 3);
                }
            }
        }
    }
    else
    {
        // CHILD

        // Close the master side of the PTY
        close(fdm);
        // 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)

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

        close(fds);
        // setsid();
        // ioctl(0, TIOCSCTTY, 1);

        {
            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);
        }

        return 1;
    }
    return 0;
} // main

// gcc test_pty.c  && ./a.out python3 /tty_test.py //works
// gcc test_pty.c  && ./a.out su root              //works
// gcc test_pty.c  && ./a.out ssh user@localhost   //fails
EN

回答 1

Unix & Linux用户

发布于 2022-09-18 19:17:54

"Unix环境下的高级编程“(APUE)显示了用于pty设置的不同代码。特别是,子节点总是执行一个setsid(2)来创建一个新会话。此外,还注意到FreeBSD (可能还有其他*BSD系统)需要TIOCSCTTY调用才能建立控制终端:

代码语言:javascript
复制
// from http://www.apuebook.com/src.3e.tar.gz -- lib/ptyfork.c
        if ((pid = fork()) < 0) {
                return(-1);
        } else if (pid == 0) {          /* child */
                if (setsid() < 0)
                        err_sys("setsid error");

                /*
                 * System V acquires controlling terminal on open().
                 */
                if ((fds = ptys_open(pts_name)) < 0)
                        err_sys("can't open slave pty");
                close(fdm);             /* all done with master in child */

#if     defined(BSD)
                /*
                 * TIOCSCTTY is the BSD way to acquire a controlling terminal.
                 */
                if (ioctl(fds, TIOCSCTTY, (char *)0) < 0)
                        err_sys("TIOCSCTTY error");
#endif

除了示例ptym_open代码之外,您还需要深入挖掘定位pty/main.cptys_open代码,以查看APUE代码在什么时候执行什么任务。

您的孩子执行代码似乎不必要地复杂;

代码语言:javascript
复制
    {
        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);
    }

可能被替换为

代码语言:javascript
复制
av++;
execvp(av[0], av);

它将直接使用现有的参数,而不是重复它们。作为另一种选择,APUE显示

代码语言:javascript
复制
execvp(argv[optind], &argv[optind]);

因为他们已经用getopt处理了选项。

票数 0
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/717715

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档