I am implementing I/O redirection in a shell written in Rust. I succeeded in piping between two children processes by using unsafe code with raw file descriptors and pipe()
from the libc crate.
When I try to redirect stdout
of the last child process to a file that I have permission to, it fails:
extern crate libc;
use std::process::{Command, Stdio};
use std::os::unix::io::{FromRawFd, IntoRawFd};
use std::fs::File;
use self::libc::c_int;
fn main() {
let mut fds = [-1 as c_int, -1 as c_int];
let fd1 = File::open("test1").unwrap().into_raw_fd();
let fd2 = File::open("test2").unwrap().into_raw_fd();
let fd3 = File::open("test3").unwrap().into_raw_fd();
println!("{:?}, {:?}, {:?}", fd1, fd2, fd3);
unsafe {
libc::pipe(&mut fds[0] as *mut c_int);
let cmd1 = Command::new("ls")
.arg("/")
.stdout(Stdio::from_raw_fd(fds[1]))
.spawn()
.unwrap();
let mut cmd2 = Command::new("grep")
.arg("etc")
.stdin(Stdio::from_raw_fd(fds[0]))
.stdout(Stdio::from_raw_fd(fd1))
.spawn()
.unwrap();
let _result = cmd2.wait().unwrap();
}
}
The result of the above piece:
3, 4, 5
grep: write error: Bad file descriptor
It seems that the file descriptor isn't correctly returned, but if there were no file named test1, test2 and test3, File::open(_).unwrap()
should panic instead of pretending to have opened a file.
The code works perfectly fine if redirection to the file is removed, i.e. only piping is used.