diff --git a/Cargo.toml b/Cargo.toml index d849f6cb9f..bd76ae6ceb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ipcd" version = "0.1.0" +edition = "2018" [dependencies] redox_syscall = "0.1.40" diff --git a/examples/event.rs b/examples/event.rs index 93553557e5..d5f549cc74 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -1,5 +1,3 @@ -extern crate syscall; - use std::{ fs::File, io::{self, prelude::*}, diff --git a/examples/main.rs b/examples/main.rs index 1e9726fd92..eda602b7c6 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -1,5 +1,3 @@ -extern crate syscall; - use std::{ fs::File, io::{self, prelude::*}, diff --git a/examples/hello.rs b/examples/server.rs similarity index 95% rename from examples/hello.rs rename to examples/server.rs index 33a1b9bdf5..1303b4114d 100644 --- a/examples/hello.rs +++ b/examples/server.rs @@ -1,5 +1,3 @@ -extern crate syscall; - use std::{ fs::File, io::{self, prelude::*}, diff --git a/examples/shm.rs b/examples/shm.rs new file mode 100644 index 0000000000..ce121524c1 --- /dev/null +++ b/examples/shm.rs @@ -0,0 +1,41 @@ +use std::{ + fs::File, + io, + os::unix::io::AsRawFd, + slice +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} +fn main() -> Result<(), io::Error> { + let file1 = File::open("shm:example")?; + let file2 = File::open("shm:example")?; + + let one = unsafe { + slice::from_raw_parts_mut( + syscall::fmap(file1.as_raw_fd(), 0, 128).map_err(from_syscall_error)? as *mut u8, + 128 + ) + }; + let two = unsafe { + slice::from_raw_parts_mut( + syscall::fmap(file2.as_raw_fd(), 64, 64).map_err(from_syscall_error)? as *mut u8, + 64 + ) + }; + + println!("Testing writing between"); + for i in 0..128 { + one[i as usize] = i; + } + for i in 0..64 { + assert_eq!(two[i as usize], 64 + i); + } + + println!("Testing fpath"); + let mut buf = [0; 128]; + let len = syscall::fpath(file1.as_raw_fd(), &mut buf).map_err(from_syscall_error)?; + assert_eq!(&buf[..len], b"shm:example"); + Ok(()) +} diff --git a/examples/shm_demo.rs b/examples/shm_demo.rs new file mode 100644 index 0000000000..3f3136d12a --- /dev/null +++ b/examples/shm_demo.rs @@ -0,0 +1,26 @@ +use std::{ + fs::File, + io, + mem, + os::unix::io::AsRawFd, + thread, + time::Duration +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} +fn main() -> Result<(), io::Error> { + let file = File::open("shm:counter")?; + println!("Reading from map... "); + let counter = unsafe { + &mut *(syscall::fmap(file.as_raw_fd(), 0, mem::size_of::()).map_err(from_syscall_error)? as *mut usize) + }; + println!("Read value {}", counter); + *counter += 1; + println!("Increased value to {}", counter); + + loop { + thread::sleep(Duration::from_secs(std::u64::MAX)); + } +} diff --git a/examples/unnamed.rs b/examples/unnamed.rs index 2a74d04484..f7c1c48a19 100644 --- a/examples/unnamed.rs +++ b/examples/unnamed.rs @@ -1,5 +1,3 @@ -extern crate syscall; - use std::{ fs::File, io::{self, prelude::*}, diff --git a/src/scheme.rs b/src/chan.rs similarity index 94% rename from src/scheme.rs rename to src/chan.rs index 7a81acdafd..aad76ae7d3 100644 --- a/src/scheme.rs +++ b/src/chan.rs @@ -1,6 +1,7 @@ -use post_fevent; +use crate::post_fevent; use std::{ - collections::BTreeMap, + cmp, + collections::HashMap, fs::File, io, mem @@ -69,12 +70,12 @@ impl Handle { } #[derive(Default)] -pub struct IpcScheme { - handles: BTreeMap, - listeners: BTreeMap, +pub struct ChanScheme { + handles: HashMap, + listeners: HashMap, next_id: usize } -impl IpcScheme { +impl ChanScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { match handle.extra { @@ -116,7 +117,7 @@ fn connect(target: &mut Handle, new_id: usize) -> Result<()> { target.remote = Connection::Open(new_id); Ok(()) } -impl SchemeBlockMut for IpcScheme { +impl SchemeBlockMut for ChanScheme { fn open(&mut self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result> { let path = ::std::str::from_utf8(path).or(Err(Error::new(EPERM)))?; @@ -155,7 +156,7 @@ impl SchemeBlockMut for IpcScheme { if let Connection::Open(remote_id) = remote { let new_id = self.next_id; - let mut clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); + let clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); { // This might fail if the remote side closed early @@ -174,7 +175,7 @@ impl SchemeBlockMut for IpcScheme { } }, b"connect" => { - let mut new = Handle::default(); + let new = Handle::default(); let new_id = self.next_id; { @@ -217,7 +218,7 @@ impl SchemeBlockMut for IpcScheme { (handle.flags, handle.remote) }; if let Connection::Open(remote_id) = remote { - let mut remote = self.handles.get_mut(&remote_id).unwrap(); + let remote = self.handles.get_mut(&remote_id).unwrap(); match remote.extra { Extra::Client(ref mut client) => { client.buffer.extend(buf); @@ -247,7 +248,7 @@ impl SchemeBlockMut for IpcScheme { }; if !client.buffer.is_empty() { - let len = buf.len().min(client.buffer.len()); + let len = cmp::min(buf.len(), client.buffer.len()); buf[..len].copy_from_slice(&client.buffer[..len]); client.buffer.drain(..len); Ok(Some(len)) diff --git a/src/main.rs b/src/main.rs index 3819e6f8f7..4110431c80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,61 +1,99 @@ -extern crate syscall; - use std::{ collections::VecDeque, fs::File, - io::{self, prelude::*} + io::{self, prelude::*}, + os::unix::io::AsRawFd }; -use syscall::SchemeBlockMut; +use syscall::{flag::*, Event, Packet, SchemeBlockMut, SchemeMut}; -mod scheme; +mod chan; +mod shm; -use scheme::IpcScheme; +use self::chan::ChanScheme; +use self::shm::ShmScheme; fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } -fn main() -> Result<(), Box<::std::error::Error>> { +const TOKEN_CHAN: usize = 0; +const TOKEN_SHM: usize = 1; + +fn main() -> Result<(), Box> { if unsafe { syscall::clone(0) }.map_err(from_syscall_error)? != 0 { return Ok(()); } - let mut scheme_file = File::create(":chan")?; - let mut scheme = IpcScheme::default(); + // Create event listener for both files + let mut event_file = File::open("event:")?; + let mut chan_file = File::create(":chan")?; + event_file.write(&Event { + id: chan_file.as_raw_fd() as usize, + flags: EVENT_READ, + data: TOKEN_CHAN + })?; + + let mut shm_file = File::create(":shm")?; + event_file.write(&Event { + id: shm_file.as_raw_fd() as usize, + flags: EVENT_READ, + data: TOKEN_SHM + })?; + + let mut chan = ChanScheme::default(); + let mut shm = ShmScheme::default(); let mut todo = VecDeque::with_capacity(16); syscall::setrens(0, 0).map_err(from_syscall_error)?; loop { - let mut event = syscall::Packet::default(); - scheme_file.read(&mut event)?; + let mut event = Event::default(); + event_file.read(&mut event)?; - // New event has to be handled first so any previous event - // that is now updated gets processed after. - todo.push_front(event); + match event.data { + TOKEN_CHAN => { + let mut packet = Packet::default(); + chan_file.read(&mut packet)?; - let mut error = None; + // Put new packet first in the queue + todo.push_front(packet); - todo.retain(|event| { - if let Some(a) = scheme.handle(&event) { - // Send event back with new ID - let mut event = *event; - event.a = a; - if let Err(err) = scheme_file.write(&event) { - error = Some(err); + let mut error: Option = None; + + // Process queue, delete finished items + todo.retain(|packet| { + if let Some(status) = chan.handle(&packet) { + // Send packet back with new ID + let mut packet = *packet; + packet.a = status; + if let Err(err) = chan_file.write(&packet) { + error = Some(err); + } + return false; + } + + true + }); + + if let Some(err) = error { + return Err(Box::new(err)); } - return false; - } - true - }); + // Handle fevents + chan.post_fevents(&mut chan_file)?; + }, + TOKEN_SHM => { + let mut packet = Packet::default(); + shm_file.read(&mut packet)?; - if let Some(err) = error { - return Err(Box::new(err)); + // Handle packet and update `a` to be status code + shm.handle(&mut packet); + + shm_file.write(&packet)?; + }, + _ => () } - - scheme.post_fevents(&mut scheme_file)?; } } fn post_fevent(file: &mut File, id: usize, flag: usize) -> io::Result<()> { diff --git a/src/shm.rs b/src/shm.rs new file mode 100644 index 0000000000..97847a75db --- /dev/null +++ b/src/shm.rs @@ -0,0 +1,75 @@ +use std::{ + cmp, + collections::{HashMap, hash_map::Entry}, + rc::Rc +}; +use syscall::{error::*, Error, SchemeMut, Result}; + +#[derive(Default)] +pub struct ShmHandle { + buffer: Option>, + refs: usize +} +#[derive(Default)] +pub struct ShmScheme { + maps: HashMap, ShmHandle>, + handles: HashMap>, + next_id: usize +} + +impl SchemeMut for ShmScheme { + fn open(&mut self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { + let path = std::str::from_utf8(path).or(Err(Error::new(EPERM)))?.into(); + let entry = self.maps.entry(Rc::clone(&path)).or_insert(ShmHandle::default()); + entry.refs += 1; + self.handles.insert(self.next_id, path); + + let id = self.next_id; + self.next_id += 1; + Ok(id) + } + fn fmap(&mut self, id: usize, offset: usize, len: usize) -> Result { + let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; + match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { + Some(ref mut buf) => { + if offset + len != buf.len() { + return Err(Error::new(ERANGE)); + } + Ok(buf[offset..].as_mut_ptr() as usize) + }, + ref mut buf @ None => { + *buf = Some(vec![0; offset+len].into_boxed_slice()); + Ok(buf.as_mut().unwrap()[offset..].as_mut_ptr() as usize) + } + } + } + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + // Write scheme name + const PREFIX: &[u8] = b"shm:"; + let len = cmp::min(PREFIX.len(), buf.len()); + buf[..len].copy_from_slice(&PREFIX[..len]); + if len < PREFIX.len() { + return Ok(len); + } + + // Write path + let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let len = cmp::min(path.len(), buf.len() - PREFIX.len()); + buf[PREFIX.len()..][..len].copy_from_slice(&path.as_bytes()[..len]); + + Ok(PREFIX.len() + len) + } + fn close(&mut self, id: usize) -> Result { + let path = self.handles.remove(&id).ok_or(Error::new(EBADF))?; + let mut entry = match self.maps.entry(path) { + Entry::Occupied(entry) => entry, + Entry::Vacant(_) => panic!("handle pointing to nothing") + }; + entry.get_mut().refs -= 1; + if entry.get().refs == 0 { + // There is no other reference to this entry, drop + entry.remove_entry(); + } + Ok(0) + } +}