非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; }
问题分析与修复
你的代码核心问题有两个:
- 错误处理forkpty返回值:forkpty在父进程中返回子进程PID,在子进程中返回0。你现在不管父进程还是子进程都执行了execve,导致父进程被替换成su,PTY的关联逻辑完全缺失。
- 未关联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
相关产品推荐
相关产品推荐

