首页 > 解决方案 > 使用 Nix PTY 的子标准输入无

问题描述

我试图使用 Rust 来玩 PTY 和一些 Unix 概念。我最终得到了以下代码,不幸的是我无法使其工作。我真的很难找出标准输入的原因。子进程的 stdout 和 stderr 最终被None分配给 PTY 从属的文件描述符。

在 Cargo.toml 文件中,我有以下依赖项:

[dependencies]
nix = "0.17.0"
libc = "0.2"

从我已经进行的调试来看,似乎 None 选项来自于https://github.com/rust-lang/rust/blob/master/src/libstd/process中完成的 ChildStdin/ChildStdout/ChildStderr 的地图.rs#L195

我不确定这是一个 Rust 问题,这可能是我对 PTY 工作原理的理解,但我已经用 C 编写了相同的代码(尽可能多地)并且它的行为符合预期。

你能帮我找出问题所在吗

use nix::Error;
use std::path::Path;
use std::os::unix::{
    io::{AsRawFd, FromRawFd, RawFd}
};
use nix::sys::stat::Mode;
use nix::fcntl::{OFlag, open};
use std::process::{Command, Stdio};
use std::os::unix::process::CommandExt;
use nix::pty::{PtyMaster, grantpt, posix_openpt, ptsname, unlockpt};
use libc::{self, winsize};

#[macro_use]
extern crate nix;
struct Pty {
    master: PtyMaster,
    slave: RawFd
}

fn construct_internal_pty() -> Result<Pty, Error> {
    ioctl_write_ptr_bad!(pty_io_size, nix::libc::TIOCSWINSZ, winsize);
    let master_fd = posix_openpt(OFlag::O_RDWR | OFlag::O_NOCTTY)?;

    grantpt(&master_fd)?;
    unlockpt(&master_fd)?;

    let slave_name = unsafe { ptsname(&master_fd) }?;
    let slave_fd = open(
        Path::new(&slave_name),
        OFlag::O_RDWR | OFlag::O_NOCTTY,
        Mode::empty())?;

    Ok(Pty {
        master: master_fd,
        slave: slave_fd
    })
}

fn main() -> () {
    let pty = construct_internal_pty().expect("Unable to construct PTY");
    let mut builder = Command::new("/bin/sh");

    let slave = pty.slave;
    let master = pty.master.as_raw_fd();

    builder.env("LOGNAME", "ex0ns");
    builder.env("USER", "ex0ns");
    builder.env("SHELL", "/bin/sh");
    builder.env("HOME", "/home/ex0ns");

    builder.current_dir("/home/ex0ns");
    // This is not working.... The resulting fd is None
    builder.stdin(unsafe { Stdio::from_raw_fd(slave) });
    builder.stderr(unsafe { Stdio::from_raw_fd(slave) });
    builder.stdout(unsafe { Stdio::from_raw_fd(slave) });

    unsafe {
        builder.pre_exec(move || {
            libc::setsid();

            libc::close(slave);
            libc::close(master);

            libc::signal(libc::SIGCHLD, libc::SIG_DFL);
            libc::signal(libc::SIGHUP, libc::SIG_DFL);
            libc::signal(libc::SIGINT, libc::SIG_DFL);
            libc::signal(libc::SIGQUIT, libc::SIG_DFL);
            libc::signal(libc::SIGTERM, libc::SIG_DFL);
            libc::signal(libc::SIGALRM, libc::SIG_DFL);

            Ok(())
        });
    }

    match builder.spawn() {
        Ok(child) => {
            println!("{:?}", child);
            println!("{:?}", slave);
            assert_eq!(child.stdin.is_none(), false);

            unsafe { libc::close(slave) }; // close PTY ?
        }
        Err(error) => {
            println!("Unable to spawn child process {}", error);
            ::std::process::exit(1);
        }
    }

}

谢谢

标签: rustpty

解决方案


推荐阅读