-
-
Save hjr3/3dea36441196cd01918a2c12e688f140 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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