Skip to content

Instantly share code, notes, and snippets.

@hjr3
Forked from Geal/fork-exec.rs
Created December 23, 2016 02:01
Show Gist options
  • Save hjr3/3dea36441196cd01918a2c12e688f140 to your computer and use it in GitHub Desktop.
Save hjr3/3dea36441196cd01918a2c12e688f140 to your computer and use it in GitHub Desktop.
extern crate nix;
extern crate libc;
use nix::sys::signal::*;
use nix::unistd::*;
use nix::fcntl::{fcntl,FcntlArg,FdFlag,FD_CLOEXEC};
use libc::c_char;
use std::mem;
use std::str;
use std::env::args;
use std::ffi::CString;
use std::io::{Read,Write};
use std::iter::repeat;
use std::process::Command;
use std::os::unix::io::{AsRawFd,FromRawFd,RawFd};
use std::os::unix::net::UnixStream;
fn main() {
for arg in args() {
println!("arg: {:?}", arg);
}
let argslist:Vec<String> = args().collect();
// if using Command::new, the first arg is the executable name
// but if using execv, there are only the arguments specified (ie, 2 file descriptors)
if argslist.len() > 2 {
println!("child({}):\tit's the child", unsafe { libc::getpid() });
let childfd = argslist[1].parse::<i32>().unwrap();
let serverfd = argslist[2].parse::<i32>().unwrap();
child(childfd, serverfd);
} else {
parent();
}
}
fn parent() {
println!("parent({}):\tHello, world!", unsafe { libc::getpid() });
let capacity = 2000usize;
let (mut server, mut client) = UnixStream::pair().unwrap();
let se_flags = fcntl(server.as_raw_fd(), FcntlArg::F_GETFD).unwrap();
let cl_flags = fcntl(client.as_raw_fd(), FcntlArg::F_GETFD).unwrap();
let mut new_cl_flags = FdFlag::from_bits(cl_flags).unwrap();
new_cl_flags.remove(FD_CLOEXEC);
fcntl(server.as_raw_fd(), FcntlArg::F_SETFD(FD_CLOEXEC));
// FD_CLOEXEC is set by default on every fd in Rust standard lib,
// so we need to remove the flag on the client, otherwise
// it won't be accessible
fcntl(client.as_raw_fd(), FcntlArg::F_SETFD(new_cl_flags));
let se_flags2 = fcntl(server.as_raw_fd(), FcntlArg::F_GETFD).unwrap();
let cl_flags2 = fcntl(client.as_raw_fd(), FcntlArg::F_GETFD).unwrap();
println!("parent({}):\t flags client before: {}, client after: {}, server before: {}, server after: {}",
unsafe { libc::getpid() }, cl_flags, cl_flags2, se_flags, se_flags2);
let path = unsafe {
let mut temp:Vec<u8> = Vec::with_capacity(capacity);
temp.extend(repeat(0).take(capacity));
let mut pathbuf = CString::from_vec_unchecked(temp);
let ptr = pathbuf.into_raw();
let proc_path = CString::new("/proc/self/exe").unwrap();
let sz = libc::readlink( proc_path.as_ptr(), ptr, 1999);
println!("parent({}):\treadlink sz: {}", unsafe { libc::getpid() }, sz);
let path = CString::from_raw(ptr);
println!("parent({}):\tcurrent path: {}", unsafe { libc::getpid() }, path.to_str().unwrap());
path
};
match fork().expect("fork failed") {
ForkResult::Parent{ child } => {
println!("parent({}):\tsleeping", unsafe { libc::getpid() });
sleep(1);
loop {
let mut buf = Vec::with_capacity(capacity);
buf.extend(repeat(0).take(capacity));
let sz = server.read(&mut buf).unwrap();
println!("parent({}):\tserver received({} bytes): {}", unsafe { libc::getpid() }, sz, str::from_utf8(&buf[..sz]).unwrap());
let sz = server.write(&b"hello from server"[..]).unwrap();
println!("parent({}):\tsleeping", unsafe { libc::getpid() });
sleep(2);
//sleep(10000);
}
}
ForkResult::Child => {
println!("child({}):\twill spawn a child", unsafe { libc::getpid() });
/*let cl = CString::new(client.as_raw_fd().to_string()).unwrap();
let se = CString::new(server.as_raw_fd().to_string()).unwrap();
let args = vec!(cl.as_ptr(), se.as_ptr());
unsafe {
libc::execv(path.as_ptr(), args.as_ptr());
}
*/
Command::new(path.to_str().unwrap())
.arg(client.as_raw_fd().to_string())
.arg(server.as_raw_fd().to_string())
.exec();
//.spawn().expect("could not execute");
sleep(1);
// if using execv, or Command::exec(), we never get here, but if using Command::spawn,
// we see it, because it creates yet another process
// example: with execv, or Command::exec() a parent of pid 10 will fork to a child of pid 11,
// and we won't see "who am I?"
// with Command::spawn(), a parent of pid 10 will fork to a child of pid 11,
// that child will create a new process of pid 12, and pid 11 will display "who am I?"
println!("child({}):\twho am I?", unsafe { libc::getpid() });
}
}
}
fn child(childfd: RawFd, serverfd: RawFd) {
let capacity = 2000usize;
let mut client = unsafe { UnixStream::from_raw_fd(childfd) };
let mut server = unsafe { UnixStream::from_raw_fd(serverfd) };
loop {
let mut buf = Vec::with_capacity(capacity);
buf.extend(repeat(0).take(capacity));
println!("child({}):\tsleeping", unsafe { libc::getpid() });
sleep(2);
let sz = client.write(&b"hello from client"[..]).unwrap();
let sz = client.read(&mut buf).unwrap();
println!("child({}):\tclient received({} bytes): {}", unsafe { libc::getpid() }, sz, str::from_utf8(&buf[..sz]).unwrap());
//THIS SHOULD FAIL
// we kept FD_CLOEXEC on the server's side
//let sz = server.write(&b"hello from client should fail"[..]).unwrap();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment