You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非PTY环境执行su命令失败,如何通过代码分配PTY解决?

问题:创建PTY包裹su命令未生效

平时在Shell中执行su -l anotheruser切换用户多数情况正常,但当su未分配PTY时会报错:

su: must be run from a terminal

为解决这个问题,我尝试编写代码创建PTY来包裹su,但代码并未生效,不清楚遗漏了什么,是否需要在execve()前重定向stdin/stdout?

原代码如下:

#include <stdio.h>
#include <string.h>
#include <pty.h>
#include <unistd.h>

int main (int argc, char *argv[])
{
    int fd;
    char* args[] = {"/bin/su", "-l", "anotheruser", NULL };
    char name[16] = {0};
    int pid ;

    pid = forkpty(&fd, name, NULL, NULL);
    if (pid != -1) {
        printf("\n pty: [%s]\n", name);
        if (execve(args[0], args, NULL) == -1) {
            printf("\n execve: failed\n");
        }
    } else {
        printf("\n forkpty: failed \n");
    }

    return 0;
}

问题分析与修复

你的代码核心问题有两个:

  1. 错误处理forkpty返回值:forkpty在父进程中返回子进程PID,在子进程中返回0。你现在不管父进程还是子进程都执行了execve,导致父进程被替换成su,PTY的关联逻辑完全缺失。
  2. 未关联PTY与当前进程IO:su需要和PTY交互,父进程必须把自身的标准输入、输出、错误重定向到PTY的文件描述符,否则su的输入输出无法传递到原终端。

另外,execve传入NULL作为环境参数可能导致su运行缺少必要环境变量,建议传入全局的environ。

修复后的代码:

#include <stdio.h>
#include <string.h>
#include <pty.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    int fd;
    char* args[] = {"/bin/su", "-l", "anotheruser", NULL};
    char name[16] = {0};
    int pid;

    pid = forkpty(&fd, name, NULL, NULL);
    if (pid == -1) {
        perror("forkpty failed");
        return 1;
    }

    if (pid == 0) {
        // 子进程:仅执行su命令
        execve(args[0], args, environ);
        // 走到这里说明execve执行失败
        perror("execve failed");
        return 1;
    } else {
        // 父进程:处理PTY与当前终端的IO关联
        printf("PTY created: [%s]\n", name);

        // 将标准输入、输出、错误重定向到PTY
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        close(fd); // 重定向完成后关闭原文件描述符

        // 等待子进程结束
        int status;
        waitpid(pid, &status, 0);
        if (WIFEXITED(status)) {
            printf("子进程退出,状态码:%d\n", WEXITSTATUS(status));
        }
    }

    return 0;
}

关键修复点说明

  • 明确区分父/子进程逻辑:子进程只负责启动su,父进程处理PTY的IO绑定
  • 用dup2将父进程的stdin/stdout/stderr绑定到PTY,让su的输入输出能传递到原终端
  • 添加waitpid等待子进程结束,避免父进程提前退出导致PTY异常
  • 使用perror替换printf,能更直观地输出系统错误原因
  • 传入environ作为execve的环境参数,保证su运行时有完整的环境变量支持

内容的提问来源于stack exchange,提问作者Ani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 10:13:11