From 1624deadfa8aba4f9ab6cffe3c2adb72de55cdaa Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 09:00:49 +0200 Subject: [PATCH 01/57] Initial commit --- Cargo.lock | 14 +++++ Cargo.toml | 7 +++ examples/chan.rs | 46 ++++++++++++++++ src/main.rs | 57 ++++++++++++++++++++ src/scheme.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 examples/chan.rs create mode 100644 src/main.rs create mode 100644 src/scheme.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..5cc1fc1a2a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,14 @@ +[[package]] +name = "chand" +version = "0.1.0" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..841036c99e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "chand" +version = "0.1.0" +authors = ["jD91mZM2 "] + +[dependencies] +redox_syscall = "0.1.40" diff --git a/examples/chan.rs b/examples/chan.rs new file mode 100644 index 0000000000..06954e18f5 --- /dev/null +++ b/examples/chan.rs @@ -0,0 +1,46 @@ +extern crate syscall; + +use std::{ + fs::File, + io::{self, prelude::*}, + os::unix::io::{AsRawFd, FromRawFd}, + thread, + time::Duration +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} + +fn main() -> io::Result<()> { + let server = File::create("chan:hello_world")?; + let mut client = File::open("chan:hello_world")?; + + let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut dup = unsafe { File::from_raw_fd(dup) }; + + dup.write(b"abc")?; + dup.flush()?; + + let mut buf = [0; 5]; + assert_eq!(client.read(&mut buf)?, 3); + assert_eq!(&buf[..3], b"abc"); + + // Tada, you can clone handles properly! + let mut client_clone = client.try_clone()?; + + let thread = thread::spawn(move || -> io::Result<()> { + thread::sleep(Duration::from_secs(1)); + client_clone.write(b"def")?; + client_clone.flush()?; + Ok(()) + }); + + let mut buf = [0; 5]; + assert_eq!(dup.read(&mut buf)?, 3); + assert_eq!(&buf[..3], b"def"); + + thread.join().unwrap().unwrap(); + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000..2288894a9f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,57 @@ +extern crate syscall; + +use std::{ + collections::VecDeque, + fs::File, + io::{self, prelude::*} +}; +use syscall::SchemeBlockMut; + +mod scheme; + +use scheme::ChanScheme; + +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>> { + if unsafe { syscall::clone(0) }.map_err(from_syscall_error)? != 0 { + return Ok(()); + } + + let mut scheme_file = File::create(":chan")?; + let mut scheme = ChanScheme::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)?; + + // New event has to be handled first so any previous event + // that is now updated gets processed after. + todo.push_front(event); + + let mut error = None; + + 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); + } + return false; + } + + true + }); + if let Some(err) = error { + return Err(Box::new(err)); + } + } +} diff --git a/src/scheme.rs b/src/scheme.rs new file mode 100644 index 0000000000..d104e221cc --- /dev/null +++ b/src/scheme.rs @@ -0,0 +1,133 @@ +use std::collections::BTreeMap; +use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; + +pub struct Handle { + path: Option, + remote: Option, + buffer: Vec +} +impl Handle { + pub fn dup(&mut self) -> Self { + Self { + path: None, + remote: self.remote.take(), + buffer: Vec::new() + } + } +} + +#[derive(Default)] +pub struct ChanScheme { + handles: BTreeMap, + listeners: BTreeMap, + next_id: usize +} +impl SchemeBlockMut for ChanScheme { + fn open(&mut self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result> { + let path = ::std::str::from_utf8(path).unwrap_or(""); + if path.is_empty() { + return Err(Error::new(EPERM)); + } + if flags & O_CREAT == O_CREAT && self.listeners.contains_key(path) { + return Err(Error::new(EADDRINUSE)); + } else if flags & O_CREAT != O_CREAT && !self.listeners.contains_key(path) { + return Err(Error::new(ENOENT)); + } + + let mut handle = Handle { + path: None, + remote: None, + buffer: Vec::new() + }; + + let id = self.next_id; + if flags & O_CREAT == O_CREAT { + self.listeners.insert(String::from(path), id); + handle.path = Some(String::from(path)); + } else { + let listener = self.listeners[path]; + let handle = self.handles.get_mut(&listener).expect("orphan listener left over"); + handle.remote = Some(id); + } + self.handles.insert(id, handle); + self.next_id += 1; + Ok(Some(id)) + } + fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { + match buf { + b"listen" => { + let mut remote = match self.handles.get(&id) { + Some(ref handle) if handle.path.is_some() => handle.remote, + _ => return Err(Error::new(EBADF)) + }; + if let Some(remote) = remote { + let new_id = self.next_id; + let mut clone = self.handles.get_mut(&id).map(Handle::dup).unwrap(); + + self.handles.insert(new_id, clone); + self.next_id += 1; + + let mut remote = self.handles.get_mut(&remote).unwrap(); + remote.remote = Some(new_id); + Ok(Some(new_id)) + } else { + Ok(None) + } + }, + _ => { + return Err(Error::new(EBADF)); + } + } + } + fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + let remote = match self.handles.get(&id) { + Some(handle) if handle.path.is_none() => handle.remote, + _ => return Err(Error::new(EBADF)) + }; + if let Some(remote) = remote { + let mut remote = self.handles.get_mut(&remote).unwrap(); + remote.buffer.extend(buf); + Ok(Some(buf.len())) + } else { + Err(Error::new(ENOTCONN)) + } + } + fn fsync(&mut self, id: usize) -> Result> { + match self.handles.get(&id) { + Some(handle) if handle.path.is_none() => Ok(Some(id)), + _ => Err(Error::new(EBADF)) + } + } + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let handle = match self.handles.get_mut(&id) { + Some(handle) => handle, + None => return Err(Error::new(EBADF)) + }; + if handle.path.is_some() { + // This is a listener, not a stream. + Err(Error::new(EBADF)) + } else if handle.buffer.is_empty() { + Ok(None) + } else { + let len = buf.len().min(handle.buffer.len()); + buf[..len].copy_from_slice(&handle.buffer[..len]); + handle.buffer.drain(..len); + Ok(Some(len)) + } + } + fn close(&mut self, id: usize) -> Result> { + let handle = match self.handles.remove(&id) { + Some(handle) => handle, + None => return Err(Error::new(EBADF)) + }; + + if let Some(remote) = handle.remote { + let mut remote = self.handles.get_mut(&remote).unwrap(); + remote.remote = None; + } + if let Some(path) = handle.path { + self.listeners.remove(&path); + } + Ok(Some(0)) + } +} From da017f7941c613119e1d6f0a256230d34146e3aa Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 09:27:54 +0200 Subject: [PATCH 02/57] Basic nonblocking I/O --- examples/chan.rs | 14 ++++++++++++-- src/scheme.rs | 44 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/examples/chan.rs b/examples/chan.rs index 06954e18f5..b4aa478730 100644 --- a/examples/chan.rs +++ b/examples/chan.rs @@ -19,6 +19,8 @@ fn main() -> io::Result<()> { let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; let mut dup = unsafe { File::from_raw_fd(dup) }; + println!("Testing basic I/O..."); + dup.write(b"abc")?; dup.flush()?; @@ -26,7 +28,8 @@ fn main() -> io::Result<()> { assert_eq!(client.read(&mut buf)?, 3); assert_eq!(&buf[..3], b"abc"); - // Tada, you can clone handles properly! + println!("Testing blocking I/O..."); + let mut client_clone = client.try_clone()?; let thread = thread::spawn(move || -> io::Result<()> { @@ -36,11 +39,18 @@ fn main() -> io::Result<()> { Ok(()) }); - let mut buf = [0; 5]; assert_eq!(dup.read(&mut buf)?, 3); assert_eq!(&buf[..3], b"def"); thread.join().unwrap().unwrap(); + println!("Testing non-blocking I/O..."); + + syscall::fcntl(client.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + .map_err(from_syscall_error)?; + assert_eq!(client.read(&mut buf).unwrap_err().kind(), io::ErrorKind::WouldBlock); + + println!("It works!"); + Ok(()) } diff --git a/src/scheme.rs b/src/scheme.rs index d104e221cc..37507422e1 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -2,18 +2,28 @@ use std::collections::BTreeMap; use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; pub struct Handle { + flags: usize, path: Option, remote: Option, buffer: Vec } impl Handle { - pub fn dup(&mut self) -> Self { + pub fn accept(&mut self) -> Self { Self { + flags: 0, path: None, remote: self.remote.take(), buffer: Vec::new() } } + pub fn copy(&self) -> Self { + Self { + flags: self.flags, + path: None, + remote: self.remote, + buffer: Vec::new() + } + } } #[derive(Default)] @@ -35,6 +45,7 @@ impl SchemeBlockMut for ChanScheme { } let mut handle = Handle { + flags: 0, path: None, remote: None, buffer: Vec::new() @@ -56,13 +67,13 @@ impl SchemeBlockMut for ChanScheme { fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { match buf { b"listen" => { - let mut remote = match self.handles.get(&id) { - Some(ref handle) if handle.path.is_some() => handle.remote, + let mut handle = match self.handles.get(&id) { + Some(ref handle) if handle.path.is_some() => handle.copy(), _ => return Err(Error::new(EBADF)) }; - if let Some(remote) = remote { + if let Some(remote) = handle.remote { let new_id = self.next_id; - let mut clone = self.handles.get_mut(&id).map(Handle::dup).unwrap(); + let mut clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); self.handles.insert(new_id, clone); self.next_id += 1; @@ -70,6 +81,8 @@ impl SchemeBlockMut for ChanScheme { let mut remote = self.handles.get_mut(&remote).unwrap(); remote.remote = Some(new_id); Ok(Some(new_id)) + } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) } else { Ok(None) } @@ -79,6 +92,19 @@ impl SchemeBlockMut for ChanScheme { } } } + fn fcntl(&mut self, id: usize, cmd: usize, arg: usize) -> Result> { + match self.handles.get_mut(&id) { + Some(handle) => match cmd { + ::syscall::F_GETFL => Ok(Some(handle.flags)), + ::syscall::F_SETFL => { + handle.flags = arg; + Ok(Some(0)) + }, + _ => Err(Error::new(EINVAL)) + }, + _ => Err(Error::new(EBADF)) + } + } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { let remote = match self.handles.get(&id) { Some(handle) if handle.path.is_none() => handle.remote, @@ -106,13 +132,15 @@ impl SchemeBlockMut for ChanScheme { if handle.path.is_some() { // This is a listener, not a stream. Err(Error::new(EBADF)) - } else if handle.buffer.is_empty() { - Ok(None) - } else { + } else if !handle.buffer.is_empty() { let len = buf.len().min(handle.buffer.len()); buf[..len].copy_from_slice(&handle.buffer[..len]); handle.buffer.drain(..len); Ok(Some(len)) + } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) + } else { + Ok(None) } } fn close(&mut self, id: usize) -> Result> { From b4133ff956cd8e08609fc69a90c87dd9f3296e45 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 10:27:10 +0200 Subject: [PATCH 03/57] Support events --- examples/chan.rs | 59 ++++++++++++++++++++++++++++++++++++- src/main.rs | 13 +++++++++ src/scheme.rs | 75 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 122 insertions(+), 25 deletions(-) diff --git a/examples/chan.rs b/examples/chan.rs index b4aa478730..9d69cbc2d5 100644 --- a/examples/chan.rs +++ b/examples/chan.rs @@ -23,17 +23,21 @@ fn main() -> io::Result<()> { dup.write(b"abc")?; dup.flush()?; + println!("-> Wrote message"); let mut buf = [0; 5]; assert_eq!(client.read(&mut buf)?, 3); assert_eq!(&buf[..3], b"abc"); + println!("-> Read message"); println!("Testing blocking I/O..."); let mut client_clone = client.try_clone()?; let thread = thread::spawn(move || -> io::Result<()> { + println!("--> Thread: Sleeping for 1 second..."); thread::sleep(Duration::from_secs(1)); + println!("--> Thread: Writing..."); client_clone.write(b"def")?; client_clone.flush()?; Ok(()) @@ -41,6 +45,7 @@ fn main() -> io::Result<()> { assert_eq!(dup.read(&mut buf)?, 3); assert_eq!(&buf[..3], b"def"); + println!("-> Read message"); thread.join().unwrap().unwrap(); @@ -50,7 +55,59 @@ fn main() -> io::Result<()> { .map_err(from_syscall_error)?; assert_eq!(client.read(&mut buf).unwrap_err().kind(), io::ErrorKind::WouldBlock); - println!("It works!"); + println!("Testing events..."); + + let thread = thread::spawn(move || -> io::Result<()> { + println!("--> Thread: Sleeping for 1 second..."); + thread::sleep(Duration::from_secs(1)); + println!("--> Thread: Writing..."); + dup.write(b"hello")?; + dup.flush()?; + Ok(()) + }); + + let mut event_file = File::open("event:")?; + let mut time_file = File::open(format!("time:{}", syscall::CLOCK_MONOTONIC))?; + + let mut time = syscall::TimeSpec::default(); + time_file.read(&mut time)?; + time.tv_sec += 5; + time_file.write(&time)?; + + event_file.write(&syscall::Event { + id: client.as_raw_fd(), + flags: syscall::EVENT_READ | syscall::EVENT_WRITE, + data: 0 + })?; + event_file.write(&syscall::Event { + id: time_file.as_raw_fd(), + flags: syscall::EVENT_READ, + data: 1 + })?; + + let mut event = syscall::Event::default(); + + event_file.read(&mut event)?; + assert_eq!(event.id, client.as_raw_fd()); + assert_eq!(event.flags, syscall::EVENT_WRITE); + assert_eq!(event.data, 0); + println!("-> Read event"); + + event_file.read(&mut event)?; + assert_eq!(event.id, client.as_raw_fd()); + assert_eq!(event.flags, syscall::EVENT_READ); + assert_eq!(event.data, 0); + println!("-> Read event"); + + event_file.read(&mut event)?; + assert_eq!(event.id, time_file.as_raw_fd()); + assert_eq!(event.flags, syscall::EVENT_READ); + assert_eq!(event.data, 1); + println!("-> Timed out"); + + thread.join().unwrap().unwrap(); + + println!("Everything tested!"); Ok(()) } diff --git a/src/main.rs b/src/main.rs index 2288894a9f..c9e5949855 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,8 +50,21 @@ fn main() -> Result<(), Box<::std::error::Error>> { true }); + if let Some(err) = error { return Err(Box::new(err)); } + + scheme.post_fevents(&mut scheme_file)?; } } +fn post_fevent(file: &mut File, id: usize, flag: usize) -> io::Result<()> { + file.write(&syscall::Packet { + a: syscall::SYS_FEVENT, + b: id, + c: flag, + d: 1, + ..Default::default() + }) + .map(|_| ()) +} diff --git a/src/scheme.rs b/src/scheme.rs index 37507422e1..a24cb70b96 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -1,27 +1,28 @@ -use std::collections::BTreeMap; +use post_fevent; +use std::{ + collections::BTreeMap, + fs::File, + io +}; use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; +#[derive(Default)] pub struct Handle { flags: usize, + fevent: usize, + notified_read: bool, + notified_write: bool, + path: Option, remote: Option, buffer: Vec } impl Handle { pub fn accept(&mut self) -> Self { - Self { - flags: 0, - path: None, - remote: self.remote.take(), - buffer: Vec::new() - } - } - pub fn copy(&self) -> Self { Self { flags: self.flags, - path: None, - remote: self.remote, - buffer: Vec::new() + remote: self.remote.take(), + ..Default::default() } } } @@ -32,6 +33,25 @@ pub struct ChanScheme { listeners: BTreeMap, next_id: usize } +impl ChanScheme { + pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { + for (id, handle) in &mut self.handles { + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } + if !handle.buffer.is_empty() { + if !handle.notified_read { + handle.notified_read = true; + post_fevent(file, *id, EVENT_READ)?; + } + } else { + handle.notified_read = false; + } + } + Ok(()) + } +} impl SchemeBlockMut for ChanScheme { fn open(&mut self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result> { let path = ::std::str::from_utf8(path).unwrap_or(""); @@ -44,12 +64,8 @@ impl SchemeBlockMut for ChanScheme { return Err(Error::new(ENOENT)); } - let mut handle = Handle { - flags: 0, - path: None, - remote: None, - buffer: Vec::new() - }; + let mut handle = Handle::default(); + handle.flags = flags; let id = self.next_id; if flags & O_CREAT == O_CREAT { @@ -67,11 +83,11 @@ impl SchemeBlockMut for ChanScheme { fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { match buf { b"listen" => { - let mut handle = match self.handles.get(&id) { - Some(ref handle) if handle.path.is_some() => handle.copy(), + let (flags, remote) = match self.handles.get(&id) { + Some(ref handle) if handle.path.is_some() => (handle.flags, handle.remote), _ => return Err(Error::new(EBADF)) }; - if let Some(remote) = handle.remote { + if let Some(remote) = remote { let new_id = self.next_id; let mut clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); @@ -81,7 +97,7 @@ impl SchemeBlockMut for ChanScheme { let mut remote = self.handles.get_mut(&remote).unwrap(); remote.remote = Some(new_id); Ok(Some(new_id)) - } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + } else if flags & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) } else { Ok(None) @@ -95,8 +111,8 @@ impl SchemeBlockMut for ChanScheme { fn fcntl(&mut self, id: usize, cmd: usize, arg: usize) -> Result> { match self.handles.get_mut(&id) { Some(handle) => match cmd { - ::syscall::F_GETFL => Ok(Some(handle.flags)), - ::syscall::F_SETFL => { + F_GETFL => Ok(Some(handle.flags)), + F_SETFL => { handle.flags = arg; Ok(Some(0)) }, @@ -105,6 +121,17 @@ impl SchemeBlockMut for ChanScheme { _ => Err(Error::new(EBADF)) } } + fn fevent(&mut self, id: usize, flags: usize) -> Result> { + match self.handles.get_mut(&id) { + Some(handle) => { + handle.fevent = flags; + handle.notified_read = false; + handle.notified_write = false; + Ok(Some(id)) + }, + _ => Err(Error::new(EBADF)) + } + } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { let remote = match self.handles.get(&id) { Some(handle) if handle.path.is_none() => handle.remote, From ac7ddf2e406c06ac6b282a00bfb3ab119677c39f Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 10:39:20 +0200 Subject: [PATCH 04/57] Add README and LICENSE --- Cargo.toml | 1 - LICENSE | 21 +++++++++++++++++++++ README.md | 12 ++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 LICENSE create mode 100644 README.md diff --git a/Cargo.toml b/Cargo.toml index 841036c99e..2c9b763835 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "chand" version = "0.1.0" -authors = ["jD91mZM2 "] [dependencies] redox_syscall = "0.1.40" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..897e4bc3ee --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Redox OS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0b813dea4a --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# chand + +A userspace daemon for interprocess communication. + +## Usage + +[Example usage](https://github.com/redox-os/chand/blob/master/examples/chan.rs) + +Simply open `chan:` with O_CREAT where `` is any name you'd like to create a listener. +This listener can accept clients by calling `dup("listen")`. + +Open `chan:` without O_CREAT to connect. Now you can read and write between both streams. From 9369ea58d87475268489267407912484b0b353ad Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 10:52:56 +0200 Subject: [PATCH 05/57] Minor code cleanup --- src/scheme.rs | 58 +++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/scheme.rs b/src/scheme.rs index a24cb70b96..0fc6231541 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -25,6 +25,9 @@ impl Handle { ..Default::default() } } + pub fn is_listener(&self) -> bool { + self.path.is_some() + } } #[derive(Default)] @@ -84,7 +87,7 @@ impl SchemeBlockMut for ChanScheme { match buf { b"listen" => { let (flags, remote) = match self.handles.get(&id) { - Some(ref handle) if handle.path.is_some() => (handle.flags, handle.remote), + Some(ref handle) if handle.is_listener() => (handle.flags, handle.remote), _ => return Err(Error::new(EBADF)) }; if let Some(remote) = remote { @@ -109,32 +112,26 @@ impl SchemeBlockMut for ChanScheme { } } fn fcntl(&mut self, id: usize, cmd: usize, arg: usize) -> Result> { - match self.handles.get_mut(&id) { - Some(handle) => match cmd { - F_GETFL => Ok(Some(handle.flags)), - F_SETFL => { - handle.flags = arg; - Ok(Some(0)) - }, - _ => Err(Error::new(EINVAL)) + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + match cmd { + F_GETFL => Ok(Some(handle.flags)), + F_SETFL => { + handle.flags = arg; + Ok(Some(0)) }, - _ => Err(Error::new(EBADF)) + _ => Err(Error::new(EINVAL)) } } fn fevent(&mut self, id: usize, flags: usize) -> Result> { - match self.handles.get_mut(&id) { - Some(handle) => { - handle.fevent = flags; - handle.notified_read = false; - handle.notified_write = false; - Ok(Some(id)) - }, - _ => Err(Error::new(EBADF)) - } + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + handle.fevent = flags; + handle.notified_read = false; + handle.notified_write = false; + Ok(Some(id)) } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { let remote = match self.handles.get(&id) { - Some(handle) if handle.path.is_none() => handle.remote, + Some(handle) if !handle.is_listener() => handle.remote, _ => return Err(Error::new(EBADF)) }; if let Some(remote) = remote { @@ -146,18 +143,14 @@ impl SchemeBlockMut for ChanScheme { } } fn fsync(&mut self, id: usize) -> Result> { - match self.handles.get(&id) { - Some(handle) if handle.path.is_none() => Ok(Some(id)), - _ => Err(Error::new(EBADF)) - } + self.handles.get(&id) + .ok_or(Error::new(EBADF)) + .and(Ok(Some(id))) } fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { - let handle = match self.handles.get_mut(&id) { - Some(handle) => handle, - None => return Err(Error::new(EBADF)) - }; - if handle.path.is_some() { - // This is a listener, not a stream. + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + if handle.is_listener() { Err(Error::new(EBADF)) } else if !handle.buffer.is_empty() { let len = buf.len().min(handle.buffer.len()); @@ -171,10 +164,7 @@ impl SchemeBlockMut for ChanScheme { } } fn close(&mut self, id: usize) -> Result> { - let handle = match self.handles.remove(&id) { - Some(handle) => handle, - None => return Err(Error::new(EBADF)) - }; + let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; if let Some(remote) = handle.remote { let mut remote = self.handles.get_mut(&remote).unwrap(); From 974d37f8e2b401081ab1e0cad0ac5d32e3edf72f Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 10:59:20 +0200 Subject: [PATCH 06/57] Properly send EOF --- examples/chan.rs | 52 +++++++++++++++++++++++++++++++++--------------- src/scheme.rs | 5 ++++- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/examples/chan.rs b/examples/chan.rs index 9d69cbc2d5..0006437fd5 100644 --- a/examples/chan.rs +++ b/examples/chan.rs @@ -13,23 +13,35 @@ fn from_syscall_error(error: syscall::Error) -> io::Error { } fn main() -> io::Result<()> { + let mut buf = [0; 5]; let server = File::create("chan:hello_world")?; + { + let mut client = File::open("chan:hello_world")?; + + let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut dup = unsafe { File::from_raw_fd(dup) }; + + println!("Testing basic I/O..."); + + dup.write(b"abc")?; + dup.flush()?; + println!("-> Wrote message"); + + assert_eq!(client.read(&mut buf)?, 3); + assert_eq!(&buf[..3], b"abc"); + println!("-> Read message"); + + println!("Testing close..."); + + drop(client); + assert_eq!(dup.write(b"a").unwrap_err().kind(), io::ErrorKind::NotConnected); + assert_eq!(dup.read(&mut buf)?, 0); + } let mut client = File::open("chan:hello_world")?; let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; let mut dup = unsafe { File::from_raw_fd(dup) }; - println!("Testing basic I/O..."); - - dup.write(b"abc")?; - dup.flush()?; - println!("-> Wrote message"); - - let mut buf = [0; 5]; - assert_eq!(client.read(&mut buf)?, 3); - assert_eq!(&buf[..3], b"abc"); - println!("-> Read message"); - println!("Testing blocking I/O..."); let mut client_clone = client.try_clone()?; @@ -63,6 +75,10 @@ fn main() -> io::Result<()> { println!("--> Thread: Writing..."); dup.write(b"hello")?; dup.flush()?; + println!("--> Thread: Sleeping for 1 second..."); + thread::sleep(Duration::from_secs(1)); + println!("--> Thread: Dropping..."); + drop(dup); Ok(()) }); @@ -93,11 +109,15 @@ fn main() -> io::Result<()> { assert_eq!(event.data, 0); println!("-> Read event"); - event_file.read(&mut event)?; - assert_eq!(event.id, client.as_raw_fd()); - assert_eq!(event.flags, syscall::EVENT_READ); - assert_eq!(event.data, 0); - println!("-> Read event"); + for _ in 0..2 { + event_file.read(&mut event)?; + assert_eq!(event.id, client.as_raw_fd()); + assert_eq!(event.flags, syscall::EVENT_READ); + assert_eq!(event.data, 0); + println!("-> Read event"); + + client.read(&mut buf)?; + } event_file.read(&mut event)?; assert_eq!(event.id, time_file.as_raw_fd()); diff --git a/src/scheme.rs b/src/scheme.rs index 0fc6231541..4b88fa14c4 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -43,7 +43,7 @@ impl ChanScheme { handle.notified_write = true; post_fevent(file, *id, EVENT_WRITE)?; } - if !handle.buffer.is_empty() { + if !handle.buffer.is_empty() || (!handle.is_listener() && handle.remote.is_none()) { if !handle.notified_read { handle.notified_read = true; post_fevent(file, *id, EVENT_READ)?; @@ -157,6 +157,9 @@ impl SchemeBlockMut for ChanScheme { buf[..len].copy_from_slice(&handle.buffer[..len]); handle.buffer.drain(..len); Ok(Some(len)) + } else if handle.remote.is_none() { + // Remote dropped, send EOF + Ok(Some(0)) } else if handle.flags & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) } else { From eae2252c5f27e6534726b3dbd7ca5a2ebf293c24 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 11:56:51 +0200 Subject: [PATCH 07/57] Announce when ready to dup --- examples/chan.rs | 61 +++++++++++++++++++++++++++++++++++++++--------- src/scheme.rs | 43 ++++++++++++++++++++++------------ 2 files changed, 78 insertions(+), 26 deletions(-) diff --git a/examples/chan.rs b/examples/chan.rs index 0006437fd5..281b99eb65 100644 --- a/examples/chan.rs +++ b/examples/chan.rs @@ -17,6 +17,8 @@ fn main() -> io::Result<()> { let server = File::create("chan:hello_world")?; { let mut client = File::open("chan:hello_world")?; + // First client not accepted yet + assert_eq!(File::open("chan:hello_world").unwrap_err().kind(), io::ErrorKind::ConnectionRefused); let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; let mut dup = unsafe { File::from_raw_fd(dup) }; @@ -65,7 +67,21 @@ fn main() -> io::Result<()> { syscall::fcntl(client.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) .map_err(from_syscall_error)?; + syscall::fcntl(server.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + .map_err(from_syscall_error)?; assert_eq!(client.read(&mut buf).unwrap_err().kind(), io::ErrorKind::WouldBlock); + println!("-> Read would block"); + match syscall::dup(server.as_raw_fd(), b"listen") { + Ok(dup) => { + unsafe { File::from_raw_fd(dup); } + panic!("this is supposed to fail"); + }, + Err(err) => { + let err = from_syscall_error(err); + assert_eq!(err.kind(), io::ErrorKind::WouldBlock); + } + } + println!("-> Accept would block"); println!("Testing events..."); @@ -73,12 +89,19 @@ fn main() -> io::Result<()> { println!("--> Thread: Sleeping for 1 second..."); thread::sleep(Duration::from_secs(1)); println!("--> Thread: Writing..."); - dup.write(b"hello")?; - dup.flush()?; + client.write(b"hello")?; + client.flush()?; println!("--> Thread: Sleeping for 1 second..."); thread::sleep(Duration::from_secs(1)); println!("--> Thread: Dropping..."); - drop(dup); + drop(client); + println!("--> Thread: Sleeping for 3 seconds..."); + thread::sleep(Duration::from_secs(3)); + println!("--> Thread: Opening new..."); + let mut client = File::open("chan:hello_world")?; + let mut buf = [0; 5]; + assert_eq!(client.read(&mut buf)?, 0); + println!("--> Thread: Closing..."); Ok(()) }); @@ -87,45 +110,61 @@ fn main() -> io::Result<()> { let mut time = syscall::TimeSpec::default(); time_file.read(&mut time)?; - time.tv_sec += 5; + time.tv_sec += 3; time_file.write(&time)?; event_file.write(&syscall::Event { - id: client.as_raw_fd(), + id: dup.as_raw_fd(), flags: syscall::EVENT_READ | syscall::EVENT_WRITE, data: 0 })?; + event_file.write(&syscall::Event { + id: server.as_raw_fd(), + flags: syscall::EVENT_WRITE | syscall::EVENT_READ, + data: 1 + })?; event_file.write(&syscall::Event { id: time_file.as_raw_fd(), flags: syscall::EVENT_READ, - data: 1 + data: 2 })?; let mut event = syscall::Event::default(); event_file.read(&mut event)?; - assert_eq!(event.id, client.as_raw_fd()); + assert_eq!(event.id, dup.as_raw_fd()); assert_eq!(event.flags, syscall::EVENT_WRITE); assert_eq!(event.data, 0); println!("-> Read event"); for _ in 0..2 { event_file.read(&mut event)?; - assert_eq!(event.id, client.as_raw_fd()); + assert_eq!(event.id, dup.as_raw_fd()); assert_eq!(event.flags, syscall::EVENT_READ); assert_eq!(event.data, 0); println!("-> Read event"); - client.read(&mut buf)?; + dup.read(&mut buf)?; } event_file.read(&mut event)?; assert_eq!(event.id, time_file.as_raw_fd()); assert_eq!(event.flags, syscall::EVENT_READ); - assert_eq!(event.data, 1); + assert_eq!(event.data, 2); println!("-> Timed out"); - thread.join().unwrap().unwrap(); + event_file.read(&mut event)?; + assert_eq!(event.id, server.as_raw_fd()); + assert_eq!(event.flags, syscall::EVENT_WRITE); + assert_eq!(event.data, 1); + println!("-> Read accept event"); + + let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let dup = unsafe { File::from_raw_fd(dup) }; + + drop(dup); + + thread.join().unwrap()?; println!("Everything tested!"); diff --git a/src/scheme.rs b/src/scheme.rs index 4b88fa14c4..04bd345aa5 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -39,17 +39,29 @@ pub struct ChanScheme { impl ChanScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; - } - if !handle.buffer.is_empty() || (!handle.is_listener() && handle.remote.is_none()) { - if !handle.notified_read { - handle.notified_read = true; - post_fevent(file, *id, EVENT_READ)?; + if handle.is_listener() { + if handle.remote.is_some() { + // Send writable because that's what smolnetd does for TcpListener + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } + } else { + handle.notified_write = false; } } else { - handle.notified_read = false; + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } + if !handle.buffer.is_empty() || handle.remote.is_none() { + if !handle.notified_read { + handle.notified_read = true; + post_fevent(file, *id, EVENT_READ)?; + } + } else { + handle.notified_read = false; + } } } Ok(()) @@ -61,22 +73,23 @@ impl SchemeBlockMut for ChanScheme { if path.is_empty() { return Err(Error::new(EPERM)); } - if flags & O_CREAT == O_CREAT && self.listeners.contains_key(path) { - return Err(Error::new(EADDRINUSE)); - } else if flags & O_CREAT != O_CREAT && !self.listeners.contains_key(path) { - return Err(Error::new(ENOENT)); - } let mut handle = Handle::default(); handle.flags = flags; let id = self.next_id; if flags & O_CREAT == O_CREAT { + if self.listeners.contains_key(path) { + return Err(Error::new(EADDRINUSE)); + } self.listeners.insert(String::from(path), id); handle.path = Some(String::from(path)); } else { - let listener = self.listeners[path]; + let listener = self.listeners.get(path).ok_or(Error::new(ENOENT))?; let handle = self.handles.get_mut(&listener).expect("orphan listener left over"); + if handle.remote.is_some() { + return Err(Error::new(ECONNREFUSED)); + } handle.remote = Some(id); } self.handles.insert(id, handle); From 1a731fcb241b8ef61b61ad254aaba654d4b00974 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 12:14:48 +0200 Subject: [PATCH 08/57] Rename to ipcd because that sounds better --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5cc1fc1a2a..bf3021c4b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,5 @@ [[package]] -name = "chand" +name = "ipcd" version = "0.1.0" dependencies = [ "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 2c9b763835..d849f6cb9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chand" +name = "ipcd" version = "0.1.0" [dependencies] diff --git a/README.md b/README.md index 0b813dea4a..d59bc12aa5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# chand +# ipcd A userspace daemon for interprocess communication. From 4327481d3c4a5a367af4bfc364d5f574bc6383a4 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 12:22:47 +0200 Subject: [PATCH 09/57] Rename example --- examples/{chan.rs => main.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{chan.rs => main.rs} (100%) diff --git a/examples/chan.rs b/examples/main.rs similarity index 100% rename from examples/chan.rs rename to examples/main.rs From 92ba44c99b8a7cc8a47948bba230bf790578a982 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 15:58:34 +0200 Subject: [PATCH 10/57] README: Fix broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d59bc12aa5..409d6f5d4a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A userspace daemon for interprocess communication. ## Usage -[Example usage](https://github.com/redox-os/chand/blob/master/examples/chan.rs) +[Example usage](https://github.com/redox-os/ipcd/blob/master/examples/main.rs) Simply open `chan:` with O_CREAT where `` is any name you'd like to create a listener. This listener can accept clients by calling `dup("listen")`. From af3813c518a80ea37eeb9c6614cfab864c1b81a5 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 16:28:33 +0200 Subject: [PATCH 11/57] Add unnamed sockets & don't send EOF before connected --- examples/main.rs | 8 ++-- examples/unnamed.rs | 38 ++++++++++++++++++ src/scheme.rs | 98 ++++++++++++++++++++++++++++++++------------- 3 files changed, 113 insertions(+), 31 deletions(-) create mode 100644 examples/unnamed.rs diff --git a/examples/main.rs b/examples/main.rs index 281b99eb65..9b2cddf0c6 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -39,7 +39,9 @@ fn main() -> io::Result<()> { assert_eq!(dup.write(b"a").unwrap_err().kind(), io::ErrorKind::NotConnected); assert_eq!(dup.read(&mut buf)?, 0); } - let mut client = File::open("chan:hello_world")?; + println!("Testing alternative connect method..."); + let client = syscall::dup(server.as_raw_fd(), b"connect").map_err(from_syscall_error)?; + let mut client = unsafe { File::from_raw_fd(client) }; let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; let mut dup = unsafe { File::from_raw_fd(dup) }; @@ -135,14 +137,14 @@ fn main() -> io::Result<()> { assert_eq!(event.id, dup.as_raw_fd()); assert_eq!(event.flags, syscall::EVENT_WRITE); assert_eq!(event.data, 0); - println!("-> Read event"); + println!("-> Writable event"); for _ in 0..2 { event_file.read(&mut event)?; assert_eq!(event.id, dup.as_raw_fd()); assert_eq!(event.flags, syscall::EVENT_READ); assert_eq!(event.data, 0); - println!("-> Read event"); + println!("-> Readable event"); dup.read(&mut buf)?; } diff --git a/examples/unnamed.rs b/examples/unnamed.rs new file mode 100644 index 0000000000..819352ab68 --- /dev/null +++ b/examples/unnamed.rs @@ -0,0 +1,38 @@ +extern crate syscall; + +use std::{ + fs::File, + io::{self, prelude::*}, + os::unix::io::{AsRawFd, FromRawFd} +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} + +fn main() -> io::Result<()> { + let mut buf = [0; 5]; + let server = File::create("chan:")?; + + let client = syscall::dup(server.as_raw_fd(), b"connect").map_err(from_syscall_error)?; + let mut client = unsafe { File::from_raw_fd(client) }; + + let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut dup = unsafe { File::from_raw_fd(dup) }; + + println!("Testing basic I/O..."); + + dup.write(b"abc")?; + dup.flush()?; + println!("-> Wrote message"); + + assert_eq!(client.read(&mut buf)?, 3); + assert_eq!(&buf[..3], b"abc"); + println!("-> Read message"); + + println!("Testing connecting to unnamed socket by name (makes no sense)..."); + assert_eq!(File::open("chan:").unwrap_err().kind(), io::ErrorKind::NotFound); + + println!("Everything tested!"); + Ok(()) +} diff --git a/src/scheme.rs b/src/scheme.rs index 04bd345aa5..73ae5a3761 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -2,10 +2,27 @@ use post_fevent; use std::{ collections::BTreeMap, fs::File, - io + io, + mem }; use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; +#[derive(Default)] +pub struct Listener { + path: Option +} +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Connection { + Waiting, + Open(usize), + Closed +} +impl Default for Connection { + fn default() -> Self { + Connection::Waiting + } +} + #[derive(Default)] pub struct Handle { flags: usize, @@ -13,20 +30,20 @@ pub struct Handle { notified_read: bool, notified_write: bool, - path: Option, - remote: Option, + listener: Option, + remote: Connection, buffer: Vec } impl Handle { pub fn accept(&mut self) -> Self { Self { flags: self.flags, - remote: self.remote.take(), + remote: mem::replace(&mut self.remote, Connection::Waiting), ..Default::default() } } pub fn is_listener(&self) -> bool { - self.path.is_some() + self.listener.is_some() } } @@ -40,7 +57,7 @@ impl ChanScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { if handle.is_listener() { - if handle.remote.is_some() { + if let Connection::Open(_) = handle.remote { // Send writable because that's what smolnetd does for TcpListener if !handle.notified_write { handle.notified_write = true; @@ -54,7 +71,7 @@ impl ChanScheme { handle.notified_write = true; post_fevent(file, *id, EVENT_WRITE)?; } - if !handle.buffer.is_empty() || handle.remote.is_none() { + if !handle.buffer.is_empty() || handle.remote == Connection::Closed { if !handle.notified_read { handle.notified_read = true; post_fevent(file, *id, EVENT_READ)?; @@ -69,30 +86,31 @@ impl ChanScheme { } impl SchemeBlockMut for ChanScheme { fn open(&mut self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result> { - let path = ::std::str::from_utf8(path).unwrap_or(""); - if path.is_empty() { - return Err(Error::new(EPERM)); - } + let path = ::std::str::from_utf8(path).or(Err(Error::new(EPERM)))?; - let mut handle = Handle::default(); - handle.flags = flags; + let mut new = Handle::default(); + new.flags = flags; let id = self.next_id; if flags & O_CREAT == O_CREAT { if self.listeners.contains_key(path) { return Err(Error::new(EADDRINUSE)); } - self.listeners.insert(String::from(path), id); - handle.path = Some(String::from(path)); + let mut listener = Listener::default(); + if !path.is_empty() { + self.listeners.insert(String::from(path), id); + listener.path = Some(String::from(path)); + } + new.listener = Some(listener); } else { let listener = self.listeners.get(path).ok_or(Error::new(ENOENT))?; let handle = self.handles.get_mut(&listener).expect("orphan listener left over"); - if handle.remote.is_some() { + if handle.remote != Connection::Waiting { return Err(Error::new(ECONNREFUSED)); } - handle.remote = Some(id); + handle.remote = Connection::Open(id); } - self.handles.insert(id, handle); + self.handles.insert(id, new); self.next_id += 1; Ok(Some(id)) } @@ -103,7 +121,7 @@ impl SchemeBlockMut for ChanScheme { Some(ref handle) if handle.is_listener() => (handle.flags, handle.remote), _ => return Err(Error::new(EBADF)) }; - if let Some(remote) = remote { + if let Connection::Open(remote) = remote { let new_id = self.next_id; let mut clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); @@ -111,7 +129,7 @@ impl SchemeBlockMut for ChanScheme { self.next_id += 1; let mut remote = self.handles.get_mut(&remote).unwrap(); - remote.remote = Some(new_id); + remote.remote = Connection::Open(new_id); Ok(Some(new_id)) } else if flags & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) @@ -119,6 +137,24 @@ impl SchemeBlockMut for ChanScheme { Ok(None) } }, + b"connect" => { + let mut new = Handle::default(); + new.remote = Connection::Open(id); + + let new_id = self.next_id; + match self.handles.get_mut(&id) { + Some(ref mut handle) if handle.is_listener() => { + if handle.remote != Connection::Waiting { + return Err(Error::new(ECONNREFUSED)); + } + handle.remote = Connection::Open(new_id); + }, + _ => return Err(Error::new(EBADF)) + } + self.handles.insert(new_id, new); + self.next_id += 1; + Ok(Some(new_id)) + }, _ => { return Err(Error::new(EBADF)); } @@ -143,14 +179,18 @@ impl SchemeBlockMut for ChanScheme { Ok(Some(id)) } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { - let remote = match self.handles.get(&id) { - Some(handle) if !handle.is_listener() => handle.remote, + let (flags, remote) = match self.handles.get(&id) { + Some(handle) if !handle.is_listener() => (handle.flags, handle.remote), _ => return Err(Error::new(EBADF)) }; - if let Some(remote) = remote { + if let Connection::Open(remote) = remote { let mut remote = self.handles.get_mut(&remote).unwrap(); remote.buffer.extend(buf); Ok(Some(buf.len())) + } else if remote == Connection::Waiting && flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) + } else if remote == Connection::Waiting { + Ok(None) } else { Err(Error::new(ENOTCONN)) } @@ -170,7 +210,7 @@ impl SchemeBlockMut for ChanScheme { buf[..len].copy_from_slice(&handle.buffer[..len]); handle.buffer.drain(..len); Ok(Some(len)) - } else if handle.remote.is_none() { + } else if handle.remote == Connection::Closed { // Remote dropped, send EOF Ok(Some(0)) } else if handle.flags & O_NONBLOCK == O_NONBLOCK { @@ -182,12 +222,14 @@ impl SchemeBlockMut for ChanScheme { fn close(&mut self, id: usize) -> Result> { let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - if let Some(remote) = handle.remote { + if let Connection::Open(remote) = handle.remote { let mut remote = self.handles.get_mut(&remote).unwrap(); - remote.remote = None; + remote.remote = Connection::Closed; } - if let Some(path) = handle.path { - self.listeners.remove(&path); + if let Some(listener) = handle.listener { + if let Some(path) = listener.path { + self.listeners.remove(&path); + } } Ok(Some(0)) } From 8a54eadae0ef2f1762dbbb8a733f6916371a2a66 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 16:34:47 +0200 Subject: [PATCH 12/57] Fix events & more event tests --- examples/event.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++ examples/main.rs | 84 ------------------------------ src/scheme.rs | 8 +-- 3 files changed, 133 insertions(+), 87 deletions(-) create mode 100644 examples/event.rs diff --git a/examples/event.rs b/examples/event.rs new file mode 100644 index 0000000000..a0aa4f0d25 --- /dev/null +++ b/examples/event.rs @@ -0,0 +1,128 @@ +extern crate syscall; + +use std::{ + fs::File, + io::{self, prelude::*}, + os::unix::io::{AsRawFd, FromRawFd} +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} + +fn main() -> io::Result<()> { + let server = File::create("chan:hello_world")?; + + println!("Testing events..."); + + syscall::fcntl(server.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + .map_err(from_syscall_error)?; + + let mut event_file = File::open("event:")?; + let mut time_file = File::open(format!("time:{}", syscall::CLOCK_MONOTONIC))?; + + let mut time = syscall::TimeSpec::default(); + time_file.read(&mut time)?; + time.tv_sec += 1; + time_file.write(&time)?; + time.tv_sec += 2; + time_file.write(&time)?; + time.tv_sec += 2; + time_file.write(&time)?; + + const TOKEN_TIMER: usize = 0; + const TOKEN_STREAM: usize = 1; + const TOKEN_SERVER: usize = 2; + const TOKEN_CLIENT: usize = 3; + + event_file.write(&syscall::Event { + id: time_file.as_raw_fd(), + flags: syscall::EVENT_READ, + data: TOKEN_TIMER + })?; + event_file.write(&syscall::Event { + id: server.as_raw_fd(), + flags: syscall::EVENT_WRITE | syscall::EVENT_READ, + data: TOKEN_SERVER + })?; + + let mut event = syscall::Event::default(); + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_TIMER); + assert_eq!(event.flags, syscall::EVENT_READ); + println!("-> Timed out"); + + let mut client = File::open("chan:hello_world")?; + event_file.write(&syscall::Event { + id: client.as_raw_fd(), + flags: syscall::EVENT_WRITE | syscall::EVENT_READ, + data: TOKEN_CLIENT + })?; + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_SERVER); + assert_eq!(event.flags, syscall::EVENT_WRITE); + println!("-> Accept event"); + + let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut dup = unsafe { File::from_raw_fd(dup) }; + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_CLIENT); + assert_eq!(event.flags, syscall::EVENT_WRITE); + println!("-> Writable event"); + + event_file.write(&syscall::Event { + id: dup.as_raw_fd(), + flags: syscall::EVENT_READ | syscall::EVENT_WRITE, + data: TOKEN_STREAM + })?; + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_STREAM); + assert_eq!(event.flags, syscall::EVENT_WRITE); + println!("-> Writable event"); + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_TIMER); + assert_eq!(event.flags, syscall::EVENT_READ); + println!("-> Timed out"); + + client.write(b"a")?; + + let mut buf = [0; 5]; + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_STREAM); + assert_eq!(event.flags, syscall::EVENT_READ); + println!("-> Readable event"); + + assert_eq!(dup.read(&mut buf)?, 1); + assert_eq!(buf[0], b'a'); + + dup.write(b"b")?; + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_CLIENT); + assert_eq!(event.flags, syscall::EVENT_READ); + println!("-> Readable event"); + + assert_eq!(client.read(&mut buf)?, 1); + assert_eq!(buf[0], b'b'); + + drop(client); + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_STREAM); + println!("-> Readable event (EOF)"); + + assert_eq!(dup.read(&mut buf)?, 0); + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_TIMER); + println!("-> Timed out"); + + println!("Everything tested!"); + Ok(()) +} diff --git a/examples/main.rs b/examples/main.rs index 9b2cddf0c6..af5f58a3a1 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -85,90 +85,6 @@ fn main() -> io::Result<()> { } println!("-> Accept would block"); - println!("Testing events..."); - - let thread = thread::spawn(move || -> io::Result<()> { - println!("--> Thread: Sleeping for 1 second..."); - thread::sleep(Duration::from_secs(1)); - println!("--> Thread: Writing..."); - client.write(b"hello")?; - client.flush()?; - println!("--> Thread: Sleeping for 1 second..."); - thread::sleep(Duration::from_secs(1)); - println!("--> Thread: Dropping..."); - drop(client); - println!("--> Thread: Sleeping for 3 seconds..."); - thread::sleep(Duration::from_secs(3)); - println!("--> Thread: Opening new..."); - let mut client = File::open("chan:hello_world")?; - let mut buf = [0; 5]; - assert_eq!(client.read(&mut buf)?, 0); - println!("--> Thread: Closing..."); - Ok(()) - }); - - let mut event_file = File::open("event:")?; - let mut time_file = File::open(format!("time:{}", syscall::CLOCK_MONOTONIC))?; - - let mut time = syscall::TimeSpec::default(); - time_file.read(&mut time)?; - time.tv_sec += 3; - time_file.write(&time)?; - - event_file.write(&syscall::Event { - id: dup.as_raw_fd(), - flags: syscall::EVENT_READ | syscall::EVENT_WRITE, - data: 0 - })?; - event_file.write(&syscall::Event { - id: server.as_raw_fd(), - flags: syscall::EVENT_WRITE | syscall::EVENT_READ, - data: 1 - })?; - event_file.write(&syscall::Event { - id: time_file.as_raw_fd(), - flags: syscall::EVENT_READ, - data: 2 - })?; - - let mut event = syscall::Event::default(); - - event_file.read(&mut event)?; - assert_eq!(event.id, dup.as_raw_fd()); - assert_eq!(event.flags, syscall::EVENT_WRITE); - assert_eq!(event.data, 0); - println!("-> Writable event"); - - for _ in 0..2 { - event_file.read(&mut event)?; - assert_eq!(event.id, dup.as_raw_fd()); - assert_eq!(event.flags, syscall::EVENT_READ); - assert_eq!(event.data, 0); - println!("-> Readable event"); - - dup.read(&mut buf)?; - } - - event_file.read(&mut event)?; - assert_eq!(event.id, time_file.as_raw_fd()); - assert_eq!(event.flags, syscall::EVENT_READ); - assert_eq!(event.data, 2); - println!("-> Timed out"); - - event_file.read(&mut event)?; - assert_eq!(event.id, server.as_raw_fd()); - assert_eq!(event.flags, syscall::EVENT_WRITE); - assert_eq!(event.data, 1); - println!("-> Read accept event"); - - let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let dup = unsafe { File::from_raw_fd(dup) }; - - drop(dup); - - thread.join().unwrap()?; - println!("Everything tested!"); - Ok(()) } diff --git a/src/scheme.rs b/src/scheme.rs index 73ae5a3761..13f721b301 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -67,9 +67,11 @@ impl ChanScheme { handle.notified_write = false; } } else { - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; + if let Connection::Open(_) = handle.remote { + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } } if !handle.buffer.is_empty() || handle.remote == Connection::Closed { if !handle.notified_read { From 34e82a7235da73903c67e4dd842146a2c0c3e837 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 18:04:30 +0200 Subject: [PATCH 13/57] Return EPIPE instead of ENOTCONN --- examples/main.rs | 2 +- src/scheme.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/main.rs b/examples/main.rs index af5f58a3a1..337d04534e 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -36,7 +36,7 @@ fn main() -> io::Result<()> { println!("Testing close..."); drop(client); - assert_eq!(dup.write(b"a").unwrap_err().kind(), io::ErrorKind::NotConnected); + assert_eq!(dup.write(b"a").unwrap_err().kind(), io::ErrorKind::BrokenPipe); assert_eq!(dup.read(&mut buf)?, 0); } println!("Testing alternative connect method..."); diff --git a/src/scheme.rs b/src/scheme.rs index 13f721b301..11c30052e8 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -194,7 +194,7 @@ impl SchemeBlockMut for ChanScheme { } else if remote == Connection::Waiting { Ok(None) } else { - Err(Error::new(ENOTCONN)) + Err(Error::new(EPIPE)) } } fn fsync(&mut self, id: usize) -> Result> { From 6f362b45481637229c8d20d43ba2d65325ed7e56 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 18:07:18 +0200 Subject: [PATCH 14/57] Rename struct --- src/main.rs | 4 ++-- src/scheme.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index c9e5949855..3819e6f8f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ use syscall::SchemeBlockMut; mod scheme; -use scheme::ChanScheme; +use scheme::IpcScheme; fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) @@ -21,7 +21,7 @@ fn main() -> Result<(), Box<::std::error::Error>> { } let mut scheme_file = File::create(":chan")?; - let mut scheme = ChanScheme::default(); + let mut scheme = IpcScheme::default(); let mut todo = VecDeque::with_capacity(16); diff --git a/src/scheme.rs b/src/scheme.rs index 11c30052e8..fecf4e9bc8 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -48,12 +48,12 @@ impl Handle { } #[derive(Default)] -pub struct ChanScheme { +pub struct IpcScheme { handles: BTreeMap, listeners: BTreeMap, next_id: usize } -impl ChanScheme { +impl IpcScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { if handle.is_listener() { @@ -86,7 +86,7 @@ impl ChanScheme { Ok(()) } } -impl SchemeBlockMut for ChanScheme { +impl SchemeBlockMut for IpcScheme { 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)))?; From 31a2b95fe0ab8466923719ccf7bfbb40ecabbbb0 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 2 Jun 2018 18:19:55 +0200 Subject: [PATCH 15/57] Add hello example --- README.md | 2 +- examples/event.rs | 12 ++++++------ examples/hello.rs | 22 ++++++++++++++++++++++ examples/main.rs | 22 +++++++++++----------- examples/unnamed.rs | 8 ++++---- 5 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 examples/hello.rs diff --git a/README.md b/README.md index 409d6f5d4a..f8222b2e68 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A userspace daemon for interprocess communication. ## Usage -[Example usage](https://github.com/redox-os/ipcd/blob/master/examples/main.rs) +See the `examples` folder. [Hello World example](https://github.com/redox-os/ipcd/blob/master/examples/hello.rs) Simply open `chan:` with O_CREAT where `` is any name you'd like to create a listener. This listener can accept clients by calling `dup("listen")`. diff --git a/examples/event.rs b/examples/event.rs index a0aa4f0d25..f05d311761 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -65,8 +65,8 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_WRITE); println!("-> Accept event"); - let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut dup = unsafe { File::from_raw_fd(dup) }; + let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut stream = unsafe { File::from_raw_fd(stream) }; event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_CLIENT); @@ -74,7 +74,7 @@ fn main() -> io::Result<()> { println!("-> Writable event"); event_file.write(&syscall::Event { - id: dup.as_raw_fd(), + id: stream.as_raw_fd(), flags: syscall::EVENT_READ | syscall::EVENT_WRITE, data: TOKEN_STREAM })?; @@ -98,10 +98,10 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_READ); println!("-> Readable event"); - assert_eq!(dup.read(&mut buf)?, 1); + assert_eq!(stream.read(&mut buf)?, 1); assert_eq!(buf[0], b'a'); - dup.write(b"b")?; + stream.write(b"b")?; event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_CLIENT); @@ -117,7 +117,7 @@ fn main() -> io::Result<()> { assert_eq!(event.data, TOKEN_STREAM); println!("-> Readable event (EOF)"); - assert_eq!(dup.read(&mut buf)?, 0); + assert_eq!(stream.read(&mut buf)?, 0); event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_TIMER); diff --git a/examples/hello.rs b/examples/hello.rs new file mode 100644 index 0000000000..33a1b9bdf5 --- /dev/null +++ b/examples/hello.rs @@ -0,0 +1,22 @@ +extern crate syscall; + +use std::{ + fs::File, + io::{self, prelude::*}, + os::unix::io::{AsRawFd, FromRawFd} +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} + +fn main() -> io::Result<()> { + let server = File::create("chan:hello")?; + + loop { + let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut stream = unsafe { File::from_raw_fd(stream) }; + + stream.write(b"Hello World!\n")?; + } +} diff --git a/examples/main.rs b/examples/main.rs index 337d04534e..acfc42d644 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -20,13 +20,13 @@ fn main() -> io::Result<()> { // First client not accepted yet assert_eq!(File::open("chan:hello_world").unwrap_err().kind(), io::ErrorKind::ConnectionRefused); - let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut dup = unsafe { File::from_raw_fd(dup) }; + let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut stream = unsafe { File::from_raw_fd(stream) }; println!("Testing basic I/O..."); - dup.write(b"abc")?; - dup.flush()?; + stream.write(b"abc")?; + stream.flush()?; println!("-> Wrote message"); assert_eq!(client.read(&mut buf)?, 3); @@ -36,15 +36,15 @@ fn main() -> io::Result<()> { println!("Testing close..."); drop(client); - assert_eq!(dup.write(b"a").unwrap_err().kind(), io::ErrorKind::BrokenPipe); - assert_eq!(dup.read(&mut buf)?, 0); + assert_eq!(stream.write(b"a").unwrap_err().kind(), io::ErrorKind::BrokenPipe); + assert_eq!(stream.read(&mut buf)?, 0); } println!("Testing alternative connect method..."); let client = syscall::dup(server.as_raw_fd(), b"connect").map_err(from_syscall_error)?; let mut client = unsafe { File::from_raw_fd(client) }; - let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut dup = unsafe { File::from_raw_fd(dup) }; + let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut stream = unsafe { File::from_raw_fd(stream) }; println!("Testing blocking I/O..."); @@ -59,7 +59,7 @@ fn main() -> io::Result<()> { Ok(()) }); - assert_eq!(dup.read(&mut buf)?, 3); + assert_eq!(stream.read(&mut buf)?, 3); assert_eq!(&buf[..3], b"def"); println!("-> Read message"); @@ -74,8 +74,8 @@ fn main() -> io::Result<()> { assert_eq!(client.read(&mut buf).unwrap_err().kind(), io::ErrorKind::WouldBlock); println!("-> Read would block"); match syscall::dup(server.as_raw_fd(), b"listen") { - Ok(dup) => { - unsafe { File::from_raw_fd(dup); } + Ok(stream) => { + unsafe { File::from_raw_fd(stream); } panic!("this is supposed to fail"); }, Err(err) => { diff --git a/examples/unnamed.rs b/examples/unnamed.rs index 819352ab68..015aaaa711 100644 --- a/examples/unnamed.rs +++ b/examples/unnamed.rs @@ -17,13 +17,13 @@ fn main() -> io::Result<()> { let client = syscall::dup(server.as_raw_fd(), b"connect").map_err(from_syscall_error)?; let mut client = unsafe { File::from_raw_fd(client) }; - let dup = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut dup = unsafe { File::from_raw_fd(dup) }; + let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; + let mut stream = unsafe { File::from_raw_fd(stream) }; println!("Testing basic I/O..."); - dup.write(b"abc")?; - dup.flush()?; + stream.write(b"abc")?; + stream.flush()?; println!("-> Wrote message"); assert_eq!(client.read(&mut buf)?, 3); From 9454cdfbb4eaec2dcce1469afb86db777292591e Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 3 Jun 2018 15:00:40 +0200 Subject: [PATCH 16/57] Code cleanup & bugfixes related to connections dropped early --- examples/client.rs | 11 ++++ examples/event.rs | 15 +++-- examples/main.rs | 55 +++++++++++------- examples/unnamed.rs | 11 ++-- src/scheme.rs | 133 ++++++++++++++++++++++++++++---------------- 5 files changed, 149 insertions(+), 76 deletions(-) create mode 100644 examples/client.rs diff --git a/examples/client.rs b/examples/client.rs new file mode 100644 index 0000000000..42163fd1e6 --- /dev/null +++ b/examples/client.rs @@ -0,0 +1,11 @@ +use std::{ + fs::File, + io::{self, prelude::*} +}; + +fn main() -> io::Result<()> { + let mut client = File::open("chan:hello")?; + io::copy(&mut client, &mut io::stdout())?; + + Ok(()) +} diff --git a/examples/event.rs b/examples/event.rs index f05d311761..561976f101 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -9,14 +9,22 @@ use std::{ fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } +fn nonblock(file: &File) -> io::Result<()> { + syscall::fcntl(file.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + .map(|_| ()) + .map_err(from_syscall_error) +} +fn dup(file: &File, buf: &str) -> io::Result { + let stream = syscall::dup(file.as_raw_fd(), buf.as_bytes()).map_err(from_syscall_error)?; + Ok(unsafe { File::from_raw_fd(stream) }) +} fn main() -> io::Result<()> { let server = File::create("chan:hello_world")?; println!("Testing events..."); - syscall::fcntl(server.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) - .map_err(from_syscall_error)?; + nonblock(&server)?; let mut event_file = File::open("event:")?; let mut time_file = File::open(format!("time:{}", syscall::CLOCK_MONOTONIC))?; @@ -65,8 +73,7 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_WRITE); println!("-> Accept event"); - let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut stream = unsafe { File::from_raw_fd(stream) }; + let mut stream = dup(&server, "listen")?; event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_CLIENT); diff --git a/examples/main.rs b/examples/main.rs index acfc42d644..1e9726fd92 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -11,6 +11,15 @@ use std::{ fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } +fn nonblock(file: &File) -> io::Result<()> { + syscall::fcntl(file.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + .map(|_| ()) + .map_err(from_syscall_error) +} +fn dup(file: &File, buf: &str) -> io::Result { + let stream = syscall::dup(file.as_raw_fd(), buf.as_bytes()).map_err(from_syscall_error)?; + Ok(unsafe { File::from_raw_fd(stream) }) +} fn main() -> io::Result<()> { let mut buf = [0; 5]; @@ -20,8 +29,7 @@ fn main() -> io::Result<()> { // First client not accepted yet assert_eq!(File::open("chan:hello_world").unwrap_err().kind(), io::ErrorKind::ConnectionRefused); - let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut stream = unsafe { File::from_raw_fd(stream) }; + let mut stream = dup(&server, "listen")?; println!("Testing basic I/O..."); @@ -40,11 +48,9 @@ fn main() -> io::Result<()> { assert_eq!(stream.read(&mut buf)?, 0); } println!("Testing alternative connect method..."); - let client = syscall::dup(server.as_raw_fd(), b"connect").map_err(from_syscall_error)?; - let mut client = unsafe { File::from_raw_fd(client) }; - let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut stream = unsafe { File::from_raw_fd(stream) }; + let mut client = dup(&server, "connect")?; + let mut stream = dup(&server, "listen")?; println!("Testing blocking I/O..."); @@ -67,24 +73,33 @@ fn main() -> io::Result<()> { println!("Testing non-blocking I/O..."); - syscall::fcntl(client.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) - .map_err(from_syscall_error)?; - syscall::fcntl(server.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) - .map_err(from_syscall_error)?; + nonblock(&client)?; + nonblock(&server)?; + assert_eq!(client.read(&mut buf).unwrap_err().kind(), io::ErrorKind::WouldBlock); println!("-> Read would block"); - match syscall::dup(server.as_raw_fd(), b"listen") { - Ok(stream) => { - unsafe { File::from_raw_fd(stream); } - panic!("this is supposed to fail"); - }, - Err(err) => { - let err = from_syscall_error(err); - assert_eq!(err.kind(), io::ErrorKind::WouldBlock); - } - } + + assert_eq!(dup(&server, "listen").unwrap_err().kind(), io::ErrorKind::WouldBlock); println!("-> Accept would block"); + drop(client); + { + let mut client = File::open("chan:hello_world")?; + nonblock(&client)?; + + assert_eq!(client.write(b"a").unwrap_err().kind(), io::ErrorKind::WouldBlock); + println!("-> Write before accept would block"); + } + + assert_eq!(dup(&server, "listen").unwrap_err().kind(), io::ErrorKind::ConnectionReset); + println!("-> Server can't accept dropped client"); + + let mut client = dup(&server, "connect")?; + nonblock(&client)?; + + assert_eq!(client.write(b"a").unwrap_err().kind(), io::ErrorKind::WouldBlock); + println!("-> Write before accept would block (alternative connection method)"); + println!("Everything tested!"); Ok(()) } diff --git a/examples/unnamed.rs b/examples/unnamed.rs index 015aaaa711..2a74d04484 100644 --- a/examples/unnamed.rs +++ b/examples/unnamed.rs @@ -9,16 +9,17 @@ use std::{ fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } +fn dup(file: &File, buf: &str) -> io::Result { + let stream = syscall::dup(file.as_raw_fd(), buf.as_bytes()).map_err(from_syscall_error)?; + Ok(unsafe { File::from_raw_fd(stream) }) +} fn main() -> io::Result<()> { let mut buf = [0; 5]; let server = File::create("chan:")?; - let client = syscall::dup(server.as_raw_fd(), b"connect").map_err(from_syscall_error)?; - let mut client = unsafe { File::from_raw_fd(client) }; - - let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut stream = unsafe { File::from_raw_fd(stream) }; + let mut client = dup(&server, "connect")?; + let mut stream = dup(&server, "listen")?; println!("Testing basic I/O..."); diff --git a/src/scheme.rs b/src/scheme.rs index fecf4e9bc8..75892ef826 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -7,11 +7,25 @@ use std::{ }; use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; -#[derive(Default)] +#[derive(Debug, Default)] +pub struct Client { + buffer: Vec +} +#[derive(Debug, Default)] pub struct Listener { path: Option } -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Debug)] +pub enum Extra { + Client(Client), + Listener(Listener) +} +impl Default for Extra { + fn default() -> Self { + Extra::Client(Client::default()) + } +} +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Connection { Waiting, Open(usize), @@ -23,16 +37,15 @@ impl Default for Connection { } } -#[derive(Default)] +#[derive(Debug, Default)] pub struct Handle { flags: usize, fevent: usize, notified_read: bool, notified_write: bool, - listener: Option, remote: Connection, - buffer: Vec + extra: Extra } impl Handle { pub fn accept(&mut self) -> Self { @@ -43,7 +56,11 @@ impl Handle { } } pub fn is_listener(&self) -> bool { - self.listener.is_some() + if let Extra::Listener(_) = self.extra { + true + } else { + false + } } } @@ -56,30 +73,33 @@ pub struct IpcScheme { impl IpcScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { - if handle.is_listener() { - if let Connection::Open(_) = handle.remote { - // Send writable because that's what smolnetd does for TcpListener - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; + match handle.extra { + Extra::Listener(_) => { + if let Connection::Open(_) = handle.remote { + // Send writable because that's what smolnetd does for TcpListener + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } + } else { + handle.notified_write = false; } - } else { - handle.notified_write = false; - } - } else { - if let Connection::Open(_) = handle.remote { - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; + }, + Extra::Client(ref mut client) => { + if let Connection::Open(_) = handle.remote { + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } } - } - if !handle.buffer.is_empty() || handle.remote == Connection::Closed { - if !handle.notified_read { - handle.notified_read = true; - post_fevent(file, *id, EVENT_READ)?; + if !client.buffer.is_empty() || handle.remote == Connection::Closed { + if !handle.notified_read { + handle.notified_read = true; + post_fevent(file, *id, EVENT_READ)?; + } + } else { + handle.notified_read = false; } - } else { - handle.notified_read = false; } } } @@ -103,7 +123,7 @@ impl SchemeBlockMut for IpcScheme { self.listeners.insert(String::from(path), id); listener.path = Some(String::from(path)); } - new.listener = Some(listener); + new.extra = Extra::Listener(listener); } else { let listener = self.listeners.get(path).ok_or(Error::new(ENOENT))?; let handle = self.handles.get_mut(&listener).expect("orphan listener left over"); @@ -125,13 +145,18 @@ impl SchemeBlockMut for IpcScheme { }; if let Connection::Open(remote) = remote { let new_id = self.next_id; + let mut clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); + { + // This might fail if the remote side closed early + let mut remote = self.handles.get_mut(&remote).ok_or(Error::new(ECONNRESET))?; + remote.remote = Connection::Open(new_id); + } + self.handles.insert(new_id, clone); self.next_id += 1; - let mut remote = self.handles.get_mut(&remote).unwrap(); - remote.remote = Connection::Open(new_id); Ok(Some(new_id)) } else if flags & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) @@ -141,9 +166,8 @@ impl SchemeBlockMut for IpcScheme { }, b"connect" => { let mut new = Handle::default(); - new.remote = Connection::Open(id); - let new_id = self.next_id; + match self.handles.get_mut(&id) { Some(ref mut handle) if handle.is_listener() => { if handle.remote != Connection::Waiting { @@ -153,6 +177,7 @@ impl SchemeBlockMut for IpcScheme { }, _ => return Err(Error::new(EBADF)) } + self.handles.insert(new_id, new); self.next_id += 1; Ok(Some(new_id)) @@ -187,8 +212,13 @@ impl SchemeBlockMut for IpcScheme { }; if let Connection::Open(remote) = remote { let mut remote = self.handles.get_mut(&remote).unwrap(); - remote.buffer.extend(buf); - Ok(Some(buf.len())) + match remote.extra { + Extra::Client(ref mut client) => { + client.buffer.extend(buf); + Ok(Some(buf.len())) + }, + Extra::Listener(_) => panic!("somehow, a client was connected to a listener directly") + } } else if remote == Connection::Waiting && flags & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) } else if remote == Connection::Waiting { @@ -205,12 +235,15 @@ impl SchemeBlockMut for IpcScheme { fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - if handle.is_listener() { - Err(Error::new(EBADF)) - } else if !handle.buffer.is_empty() { - let len = buf.len().min(handle.buffer.len()); - buf[..len].copy_from_slice(&handle.buffer[..len]); - handle.buffer.drain(..len); + let client = match handle.extra { + Extra::Client(ref mut client) => client, + Extra::Listener(_) => return Err(Error::new(EBADF)) + }; + + if !client.buffer.is_empty() { + let len = buf.len().min(client.buffer.len()); + buf[..len].copy_from_slice(&client.buffer[..len]); + client.buffer.drain(..len); Ok(Some(len)) } else if handle.remote == Connection::Closed { // Remote dropped, send EOF @@ -224,13 +257,19 @@ impl SchemeBlockMut for IpcScheme { fn close(&mut self, id: usize) -> Result> { let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - if let Connection::Open(remote) = handle.remote { - let mut remote = self.handles.get_mut(&remote).unwrap(); - remote.remote = Connection::Closed; - } - if let Some(listener) = handle.listener { - if let Some(path) = listener.path { - self.listeners.remove(&path); + match handle.extra { + Extra::Client(_) => { + if let Connection::Open(remote) = handle.remote { + let mut remote = self.handles.get_mut(&remote).unwrap(); + remote.remote = Connection::Closed; + } + }, + Extra::Listener(listener) => { + // Clients never register server's remote + + if let Some(path) = listener.path { + self.listeners.remove(&path); + } } } Ok(Some(0)) From 438acd7d09e1228e13adea39d3ca1b4462b5557a Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 3 Jun 2018 17:09:56 +0200 Subject: [PATCH 17/57] Only send events that were asked for --- examples/event.rs | 24 ++++++++++++++++++++++-- src/scheme.rs | 30 ++++++++++++------------------ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/examples/event.rs b/examples/event.rs index 561976f101..f6f1e11a74 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -22,8 +22,6 @@ fn dup(file: &File, buf: &str) -> io::Result { fn main() -> io::Result<()> { let server = File::create("chan:hello_world")?; - println!("Testing events..."); - nonblock(&server)?; let mut event_file = File::open("event:")?; @@ -37,6 +35,8 @@ fn main() -> io::Result<()> { time_file.write(&time)?; time.tv_sec += 2; time_file.write(&time)?; + time.tv_sec += 1; + time_file.write(&time)?; const TOKEN_TIMER: usize = 0; const TOKEN_STREAM: usize = 1; @@ -56,6 +56,8 @@ fn main() -> io::Result<()> { let mut event = syscall::Event::default(); + println!("Testing accept events..."); + event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_TIMER); assert_eq!(event.flags, syscall::EVENT_READ); @@ -73,6 +75,8 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_WRITE); println!("-> Accept event"); + println!("Testing write events..."); + let mut stream = dup(&server, "listen")?; event_file.read(&mut event)?; @@ -96,6 +100,8 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_READ); println!("-> Timed out"); + println!("Testing read events..."); + client.write(b"a")?; let mut buf = [0; 5]; @@ -130,6 +136,20 @@ fn main() -> io::Result<()> { assert_eq!(event.data, TOKEN_TIMER); println!("-> Timed out"); + println!("Testing no events..."); + + event_file.write(&syscall::Event { + id: server.as_raw_fd(), + flags: 0, + data: TOKEN_SERVER + })?; + + let _client = File::open("chan:hello_world")?; + + event_file.read(&mut event)?; + assert_eq!(event.data, TOKEN_TIMER); + println!("-> Timed out"); + println!("Everything tested!"); Ok(()) } diff --git a/src/scheme.rs b/src/scheme.rs index 75892ef826..1066f8df5e 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -73,25 +73,19 @@ pub struct IpcScheme { impl IpcScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { - match handle.extra { - Extra::Listener(_) => { - if let Connection::Open(_) = handle.remote { - // Send writable because that's what smolnetd does for TcpListener - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; - } - } else { - handle.notified_write = false; - } - }, - Extra::Client(ref mut client) => { - if let Connection::Open(_) = handle.remote { - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; - } + if handle.fevent & EVENT_WRITE == EVENT_WRITE { + if let Connection::Open(_) = handle.remote { + // Send writable even for listeners because that's what smolnetd does for TcpListener + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; } + } else { + handle.notified_write = false; + } + } + if let Extra::Client(ref mut client) = handle.extra { + if handle.fevent & EVENT_READ == EVENT_READ { if !client.buffer.is_empty() || handle.remote == Connection::Closed { if !handle.notified_read { handle.notified_read = true; From 879bbf0dadc8a8c713116a630a424c69776bba8a Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 3 Jun 2018 17:22:39 +0200 Subject: [PATCH 18/57] Revert "Only send events that were asked for" This reverts commit 438acd7d09e1228e13adea39d3ca1b4462b5557a. Apparently the test I wrote passes anyway with the old code. I assume the kernel is filtering the events out. --- examples/event.rs | 24 ++---------------------- src/scheme.rs | 30 ++++++++++++++++++------------ 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/examples/event.rs b/examples/event.rs index f6f1e11a74..561976f101 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -22,6 +22,8 @@ fn dup(file: &File, buf: &str) -> io::Result { fn main() -> io::Result<()> { let server = File::create("chan:hello_world")?; + println!("Testing events..."); + nonblock(&server)?; let mut event_file = File::open("event:")?; @@ -35,8 +37,6 @@ fn main() -> io::Result<()> { time_file.write(&time)?; time.tv_sec += 2; time_file.write(&time)?; - time.tv_sec += 1; - time_file.write(&time)?; const TOKEN_TIMER: usize = 0; const TOKEN_STREAM: usize = 1; @@ -56,8 +56,6 @@ fn main() -> io::Result<()> { let mut event = syscall::Event::default(); - println!("Testing accept events..."); - event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_TIMER); assert_eq!(event.flags, syscall::EVENT_READ); @@ -75,8 +73,6 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_WRITE); println!("-> Accept event"); - println!("Testing write events..."); - let mut stream = dup(&server, "listen")?; event_file.read(&mut event)?; @@ -100,8 +96,6 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_READ); println!("-> Timed out"); - println!("Testing read events..."); - client.write(b"a")?; let mut buf = [0; 5]; @@ -136,20 +130,6 @@ fn main() -> io::Result<()> { assert_eq!(event.data, TOKEN_TIMER); println!("-> Timed out"); - println!("Testing no events..."); - - event_file.write(&syscall::Event { - id: server.as_raw_fd(), - flags: 0, - data: TOKEN_SERVER - })?; - - let _client = File::open("chan:hello_world")?; - - event_file.read(&mut event)?; - assert_eq!(event.data, TOKEN_TIMER); - println!("-> Timed out"); - println!("Everything tested!"); Ok(()) } diff --git a/src/scheme.rs b/src/scheme.rs index 1066f8df5e..75892ef826 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -73,19 +73,25 @@ pub struct IpcScheme { impl IpcScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { - if handle.fevent & EVENT_WRITE == EVENT_WRITE { - if let Connection::Open(_) = handle.remote { - // Send writable even for listeners because that's what smolnetd does for TcpListener - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; + match handle.extra { + Extra::Listener(_) => { + if let Connection::Open(_) = handle.remote { + // Send writable because that's what smolnetd does for TcpListener + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } + } else { + handle.notified_write = false; + } + }, + Extra::Client(ref mut client) => { + if let Connection::Open(_) = handle.remote { + if !handle.notified_write { + handle.notified_write = true; + post_fevent(file, *id, EVENT_WRITE)?; + } } - } else { - handle.notified_write = false; - } - } - if let Extra::Client(ref mut client) = handle.extra { - if handle.fevent & EVENT_READ == EVENT_READ { if !client.buffer.is_empty() || handle.remote == Connection::Closed { if !handle.notified_read { handle.notified_read = true; From 97179331df27360e0c8ac3d8d3c3eccc8a591eb0 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 3 Jun 2018 17:25:40 +0200 Subject: [PATCH 19/57] Add more messages to event test & delete unused variable --- examples/event.rs | 8 ++++++-- src/scheme.rs | 4 +--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/event.rs b/examples/event.rs index 561976f101..93553557e5 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -22,8 +22,6 @@ fn dup(file: &File, buf: &str) -> io::Result { fn main() -> io::Result<()> { let server = File::create("chan:hello_world")?; - println!("Testing events..."); - nonblock(&server)?; let mut event_file = File::open("event:")?; @@ -56,6 +54,8 @@ fn main() -> io::Result<()> { let mut event = syscall::Event::default(); + println!("Testing accept events..."); + event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_TIMER); assert_eq!(event.flags, syscall::EVENT_READ); @@ -73,6 +73,8 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_WRITE); println!("-> Accept event"); + println!("Testing write events..."); + let mut stream = dup(&server, "listen")?; event_file.read(&mut event)?; @@ -96,6 +98,8 @@ fn main() -> io::Result<()> { assert_eq!(event.flags, syscall::EVENT_READ); println!("-> Timed out"); + println!("Testing read events..."); + client.write(b"a")?; let mut buf = [0; 5]; diff --git a/src/scheme.rs b/src/scheme.rs index 75892ef826..8ab8a7b690 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -40,7 +40,6 @@ impl Default for Connection { #[derive(Debug, Default)] pub struct Handle { flags: usize, - fevent: usize, notified_read: bool, notified_write: bool, @@ -198,9 +197,8 @@ impl SchemeBlockMut for IpcScheme { _ => Err(Error::new(EINVAL)) } } - fn fevent(&mut self, id: usize, flags: usize) -> Result> { + fn fevent(&mut self, id: usize, _flags: usize) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - handle.fevent = flags; handle.notified_read = false; handle.notified_write = false; Ok(Some(id)) From 85aa51328a8da4935f8a94f6e8f4049d13c2b47d Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Thu, 7 Jun 2018 18:04:59 +0200 Subject: [PATCH 20/57] Slightly improve code for requiring listener/client --- src/scheme.rs | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/scheme.rs b/src/scheme.rs index 8ab8a7b690..57e62b876a 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -54,11 +54,16 @@ impl Handle { ..Default::default() } } - pub fn is_listener(&self) -> bool { - if let Extra::Listener(_) = self.extra { - true - } else { - false + pub fn require_listener(&self) -> Result<()> { + match self.extra { + Extra::Listener(_) => Ok(()), + _ => Err(Error::new(EBADF)) + } + } + pub fn require_client(&self) -> Result<()> { + match self.extra { + Extra::Client(_) => Ok(()), + _ => Err(Error::new(EBADF)) } } } @@ -138,9 +143,10 @@ impl SchemeBlockMut for IpcScheme { fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { match buf { b"listen" => { - let (flags, remote) = match self.handles.get(&id) { - Some(ref handle) if handle.is_listener() => (handle.flags, handle.remote), - _ => return Err(Error::new(EBADF)) + let (flags, remote) = { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + handle.require_listener()?; + (handle.flags, handle.remote) }; if let Connection::Open(remote) = remote { let new_id = self.next_id; @@ -167,14 +173,14 @@ impl SchemeBlockMut for IpcScheme { let mut new = Handle::default(); let new_id = self.next_id; - match self.handles.get_mut(&id) { - Some(ref mut handle) if handle.is_listener() => { - if handle.remote != Connection::Waiting { - return Err(Error::new(ECONNREFUSED)); - } - handle.remote = Connection::Open(new_id); - }, - _ => return Err(Error::new(EBADF)) + { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + handle.require_listener()?; + + if handle.remote != Connection::Waiting { + return Err(Error::new(ECONNREFUSED)); + } + handle.remote = Connection::Open(new_id); } self.handles.insert(new_id, new); @@ -204,9 +210,10 @@ impl SchemeBlockMut for IpcScheme { Ok(Some(id)) } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { - let (flags, remote) = match self.handles.get(&id) { - Some(handle) if !handle.is_listener() => (handle.flags, handle.remote), - _ => return Err(Error::new(EBADF)) + let (flags, remote) = { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + handle.require_client()?; + (handle.flags, handle.remote) }; if let Connection::Open(remote) = remote { let mut remote = self.handles.get_mut(&remote).unwrap(); From a33edcf32288a228df2c6300f624e7d30e30b2eb Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Thu, 7 Jun 2018 20:11:57 +0200 Subject: [PATCH 21/57] Remove unused import from example --- examples/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/client.rs b/examples/client.rs index 42163fd1e6..95af217d0e 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,6 +1,6 @@ use std::{ fs::File, - io::{self, prelude::*} + io }; fn main() -> io::Result<()> { From 1e091076845f0ac6b0666e7f804928db520981d2 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 10 Jun 2018 20:04:32 +0200 Subject: [PATCH 22/57] Add GitLab CI --- .gitlab-ci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..a812edea17 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,26 @@ +# This file is a template, and might need editing before it works on your project. +# Official language image. Look for the different tagged releases at: +# https://hub.docker.com/r/library/rust/tags/ +image: "rust:latest" + +# Optional: Pick zero or more services to be used on all builds. +# Only needed when using a docker container to run your tests in. +# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service +#services: +# - mysql:latest +# - redis:latest +# - postgres:latest + +# Optional: Install a C compiler, cmake and git into the container. +# You will often need this when you (or any of your dependencies) depends on C code. +#before_script: +#- apt-get update -yqq +#- apt-get install -yqq --no-install-recommends build-essential + +# Use cargo to test the project +test:cargo: + script: + - rustup toolchain add nightly + - rustup target add x86_64-unknown-redox --toolchain nightly + - rustup show # Print version info for debugging + - cargo +nightly check --verbose --target x86_64-unknown-redox From 19f8137eef18ea4cbcec119402741dae6feec68e Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sat, 16 Jun 2018 16:17:25 +0200 Subject: [PATCH 23/57] Tiny cleanup --- src/scheme.rs | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/scheme.rs b/src/scheme.rs index 57e62b876a..7a81acdafd 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -85,8 +85,6 @@ impl IpcScheme { handle.notified_write = true; post_fevent(file, *id, EVENT_WRITE)?; } - } else { - handle.notified_write = false; } }, Extra::Client(ref mut client) => { @@ -110,6 +108,14 @@ impl IpcScheme { Ok(()) } } + +fn connect(target: &mut Handle, new_id: usize) -> Result<()> { + if target.remote != Connection::Waiting { + return Err(Error::new(ECONNREFUSED)); + } + target.remote = Connection::Open(new_id); + Ok(()) +} impl SchemeBlockMut for IpcScheme { 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)))?; @@ -117,28 +123,26 @@ impl SchemeBlockMut for IpcScheme { let mut new = Handle::default(); new.flags = flags; - let id = self.next_id; + let new_id = self.next_id; if flags & O_CREAT == O_CREAT { if self.listeners.contains_key(path) { return Err(Error::new(EADDRINUSE)); } let mut listener = Listener::default(); if !path.is_empty() { - self.listeners.insert(String::from(path), id); + self.listeners.insert(String::from(path), new_id); listener.path = Some(String::from(path)); } new.extra = Extra::Listener(listener); } else { - let listener = self.listeners.get(path).ok_or(Error::new(ENOENT))?; - let handle = self.handles.get_mut(&listener).expect("orphan listener left over"); - if handle.remote != Connection::Waiting { - return Err(Error::new(ECONNREFUSED)); - } - handle.remote = Connection::Open(id); + let listener_id = self.listeners.get(path).ok_or(Error::new(ENOENT))?; + let listener = self.handles.get_mut(listener_id).expect("orphan listener left over"); + connect(listener, new_id)?; } - self.handles.insert(id, new); + + self.handles.insert(new_id, new); self.next_id += 1; - Ok(Some(id)) + Ok(Some(new_id)) } fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { match buf { @@ -148,14 +152,14 @@ impl SchemeBlockMut for IpcScheme { handle.require_listener()?; (handle.flags, handle.remote) }; - if let Connection::Open(remote) = remote { + 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(); { // This might fail if the remote side closed early - let mut remote = self.handles.get_mut(&remote).ok_or(Error::new(ECONNRESET))?; + let mut remote = self.handles.get_mut(&remote_id).ok_or(Error::new(ECONNRESET))?; remote.remote = Connection::Open(new_id); } @@ -177,10 +181,7 @@ impl SchemeBlockMut for IpcScheme { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; handle.require_listener()?; - if handle.remote != Connection::Waiting { - return Err(Error::new(ECONNREFUSED)); - } - handle.remote = Connection::Open(new_id); + connect(handle, new_id)?; } self.handles.insert(new_id, new); @@ -215,8 +216,8 @@ impl SchemeBlockMut for IpcScheme { handle.require_client()?; (handle.flags, handle.remote) }; - if let Connection::Open(remote) = remote { - let mut remote = self.handles.get_mut(&remote).unwrap(); + if let Connection::Open(remote_id) = remote { + let mut remote = self.handles.get_mut(&remote_id).unwrap(); match remote.extra { Extra::Client(ref mut client) => { client.buffer.extend(buf); @@ -264,8 +265,8 @@ impl SchemeBlockMut for IpcScheme { match handle.extra { Extra::Client(_) => { - if let Connection::Open(remote) = handle.remote { - let mut remote = self.handles.get_mut(&remote).unwrap(); + if let Connection::Open(remote_id) = handle.remote { + let mut remote = self.handles.get_mut(&remote_id).unwrap(); remote.remote = Connection::Closed; } }, From f9ed9ab2631a396e22e11d4e9bb9bb3fe4498274 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 9 Sep 2018 18:47:43 +0200 Subject: [PATCH 24/57] Add gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..53eaa21960 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk From 49e424d8a4aea7e78923bad5c118b7a89358f8b9 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Fri, 28 Dec 2018 17:38:57 +0100 Subject: [PATCH 25/57] Add separate scheme for fmap --- Cargo.toml | 1 + examples/event.rs | 2 - examples/main.rs | 2 - examples/{hello.rs => server.rs} | 2 - examples/shm.rs | 41 +++++++++++++ examples/shm_demo.rs | 26 +++++++++ examples/unnamed.rs | 2 - src/{scheme.rs => chan.rs} | 23 ++++---- src/main.rs | 98 ++++++++++++++++++++++---------- src/shm.rs | 75 ++++++++++++++++++++++++ 10 files changed, 223 insertions(+), 49 deletions(-) rename examples/{hello.rs => server.rs} (95%) create mode 100644 examples/shm.rs create mode 100644 examples/shm_demo.rs rename src/{scheme.rs => chan.rs} (94%) create mode 100644 src/shm.rs 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) + } +} From 06f4d6b9a37a105ac5f53351eedf2cababb63bdd Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 30 Dec 2018 15:43:59 +0100 Subject: [PATCH 26/57] Support new fmap stuff! --- Cargo.lock | 6 +++--- Cargo.toml | 5 ++--- examples/event.rs | 16 ++++++++-------- examples/main.rs | 8 ++++---- examples/server.rs | 6 +++--- examples/shm.rs | 14 +++++++++++--- examples/shm_demo.rs | 6 +++++- examples/unnamed.rs | 6 +++--- src/shm.rs | 13 +++++++------ 9 files changed, 46 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf3021c4b0..fc543909b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,13 @@ name = "ipcd" version = "0.1.0" dependencies = [ - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.40" +version = "0.1.49" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f22c50afdcf3f0a31ebb6b47697f6a7c5e5a24967e842858118bce0615f0afad" diff --git a/Cargo.toml b/Cargo.toml index bd76ae6ceb..1b92c1d369 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [package] +edition = "2018" name = "ipcd" version = "0.1.0" -edition = "2018" - [dependencies] -redox_syscall = "0.1.40" +redox_syscall = "0.1.49" diff --git a/examples/event.rs b/examples/event.rs index d5f549cc74..35379e54af 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -1,20 +1,20 @@ use std::{ fs::File, io::{self, prelude::*}, - os::unix::io::{AsRawFd, FromRawFd} + os::unix::io::{AsRawFd, FromRawFd, RawFd} }; fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } fn nonblock(file: &File) -> io::Result<()> { - syscall::fcntl(file.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + syscall::fcntl(file.as_raw_fd() as usize, syscall::F_SETFL, syscall::O_NONBLOCK) .map(|_| ()) .map_err(from_syscall_error) } fn dup(file: &File, buf: &str) -> io::Result { - let stream = syscall::dup(file.as_raw_fd(), buf.as_bytes()).map_err(from_syscall_error)?; - Ok(unsafe { File::from_raw_fd(stream) }) + let stream = syscall::dup(file.as_raw_fd() as usize, buf.as_bytes()).map_err(from_syscall_error)?; + Ok(unsafe { File::from_raw_fd(stream as RawFd) }) } fn main() -> io::Result<()> { @@ -40,12 +40,12 @@ fn main() -> io::Result<()> { const TOKEN_CLIENT: usize = 3; event_file.write(&syscall::Event { - id: time_file.as_raw_fd(), + id: time_file.as_raw_fd() as usize, flags: syscall::EVENT_READ, data: TOKEN_TIMER })?; event_file.write(&syscall::Event { - id: server.as_raw_fd(), + id: server.as_raw_fd() as usize, flags: syscall::EVENT_WRITE | syscall::EVENT_READ, data: TOKEN_SERVER })?; @@ -61,7 +61,7 @@ fn main() -> io::Result<()> { let mut client = File::open("chan:hello_world")?; event_file.write(&syscall::Event { - id: client.as_raw_fd(), + id: client.as_raw_fd() as usize, flags: syscall::EVENT_WRITE | syscall::EVENT_READ, data: TOKEN_CLIENT })?; @@ -81,7 +81,7 @@ fn main() -> io::Result<()> { println!("-> Writable event"); event_file.write(&syscall::Event { - id: stream.as_raw_fd(), + id: stream.as_raw_fd() as usize, flags: syscall::EVENT_READ | syscall::EVENT_WRITE, data: TOKEN_STREAM })?; diff --git a/examples/main.rs b/examples/main.rs index eda602b7c6..2a41aebb6d 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -1,7 +1,7 @@ use std::{ fs::File, io::{self, prelude::*}, - os::unix::io::{AsRawFd, FromRawFd}, + os::unix::io::{AsRawFd, FromRawFd, RawFd}, thread, time::Duration }; @@ -10,13 +10,13 @@ fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } fn nonblock(file: &File) -> io::Result<()> { - syscall::fcntl(file.as_raw_fd(), syscall::F_SETFL, syscall::O_NONBLOCK) + syscall::fcntl(file.as_raw_fd() as usize, syscall::F_SETFL, syscall::O_NONBLOCK) .map(|_| ()) .map_err(from_syscall_error) } fn dup(file: &File, buf: &str) -> io::Result { - let stream = syscall::dup(file.as_raw_fd(), buf.as_bytes()).map_err(from_syscall_error)?; - Ok(unsafe { File::from_raw_fd(stream) }) + let stream = syscall::dup(file.as_raw_fd() as usize, buf.as_bytes()).map_err(from_syscall_error)?; + Ok(unsafe { File::from_raw_fd(stream as RawFd) }) } fn main() -> io::Result<()> { diff --git a/examples/server.rs b/examples/server.rs index 1303b4114d..5a2df14095 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,7 +1,7 @@ use std::{ fs::File, io::{self, prelude::*}, - os::unix::io::{AsRawFd, FromRawFd} + os::unix::io::{AsRawFd, FromRawFd, RawFd} }; fn from_syscall_error(error: syscall::Error) -> io::Error { @@ -12,8 +12,8 @@ fn main() -> io::Result<()> { let server = File::create("chan:hello")?; loop { - let stream = syscall::dup(server.as_raw_fd(), b"listen").map_err(from_syscall_error)?; - let mut stream = unsafe { File::from_raw_fd(stream) }; + let stream = syscall::dup(server.as_raw_fd() as usize, b"listen").map_err(from_syscall_error)?; + let mut stream = unsafe { File::from_raw_fd(stream as RawFd) }; stream.write(b"Hello World!\n")?; } diff --git a/examples/shm.rs b/examples/shm.rs index ce121524c1..55bbcdfb1a 100644 --- a/examples/shm.rs +++ b/examples/shm.rs @@ -14,13 +14,21 @@ fn main() -> Result<(), io::Error> { let one = unsafe { slice::from_raw_parts_mut( - syscall::fmap(file1.as_raw_fd(), 0, 128).map_err(from_syscall_error)? as *mut u8, + syscall::fmap(file1.as_raw_fd() as usize, &syscall::Map { + offset: 0, + size: 128, + flags: syscall::PROT_READ | syscall::PROT_WRITE + }).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, + syscall::fmap(file2.as_raw_fd() as usize, &syscall::Map { + offset: 64, + size: 64, + flags: syscall::PROT_READ | syscall::PROT_WRITE + }).map_err(from_syscall_error)? as *mut u8, 64 ) }; @@ -35,7 +43,7 @@ fn main() -> Result<(), io::Error> { println!("Testing fpath"); let mut buf = [0; 128]; - let len = syscall::fpath(file1.as_raw_fd(), &mut buf).map_err(from_syscall_error)?; + let len = syscall::fpath(file1.as_raw_fd() as usize, &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 index 3f3136d12a..369a7ffbde 100644 --- a/examples/shm_demo.rs +++ b/examples/shm_demo.rs @@ -14,7 +14,11 @@ 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) + &mut *(syscall::fmap(file.as_raw_fd() as usize, &syscall::Map { + offset: 0, + size: mem::size_of::(), + flags: syscall::PROT_READ | syscall::PROT_WRITE + }).map_err(from_syscall_error)? as *mut usize) }; println!("Read value {}", counter); *counter += 1; diff --git a/examples/unnamed.rs b/examples/unnamed.rs index f7c1c48a19..914bc930fc 100644 --- a/examples/unnamed.rs +++ b/examples/unnamed.rs @@ -1,15 +1,15 @@ use std::{ fs::File, io::{self, prelude::*}, - os::unix::io::{AsRawFd, FromRawFd} + os::unix::io::{AsRawFd, FromRawFd, RawFd} }; fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } fn dup(file: &File, buf: &str) -> io::Result { - let stream = syscall::dup(file.as_raw_fd(), buf.as_bytes()).map_err(from_syscall_error)?; - Ok(unsafe { File::from_raw_fd(stream) }) + let stream = syscall::dup(file.as_raw_fd() as usize, buf.as_bytes()).map_err(from_syscall_error)?; + Ok(unsafe { File::from_raw_fd(stream as RawFd) }) } fn main() -> io::Result<()> { diff --git a/src/shm.rs b/src/shm.rs index 97847a75db..8a4b529151 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -3,7 +3,7 @@ use std::{ collections::{HashMap, hash_map::Entry}, rc::Rc }; -use syscall::{error::*, Error, SchemeMut, Result}; +use syscall::{error::*, Error, Map, SchemeMut, Result}; #[derive(Default)] pub struct ShmHandle { @@ -28,18 +28,19 @@ impl SchemeMut for ShmScheme { self.next_id += 1; Ok(id) } - fn fmap(&mut self, id: usize, offset: usize, len: usize) -> Result { + fn fmap(&mut self, id: usize, map: &Map) -> Result { let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let total_size = map.offset + map.size; match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { Some(ref mut buf) => { - if offset + len != buf.len() { + if total_size != buf.len() { return Err(Error::new(ERANGE)); } - Ok(buf[offset..].as_mut_ptr() as usize) + Ok(buf[map.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) + *buf = Some(vec![0; total_size].into_boxed_slice()); + Ok(buf.as_mut().unwrap()[map.offset..].as_mut_ptr() as usize) } } } From 3efdf1db8abf945bd526d1eead1314feccfdd832 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 13 Mar 2019 13:58:08 -0600 Subject: [PATCH 27/57] Support for new fevent --- src/chan.rs | 66 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/chan.rs b/src/chan.rs index aad76ae7d3..0626903c91 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -55,18 +55,52 @@ impl Handle { ..Default::default() } } + pub fn require_listener(&self) -> Result<()> { match self.extra { Extra::Listener(_) => Ok(()), _ => Err(Error::new(EBADF)) } } + pub fn require_client(&self) -> Result<()> { match self.extra { Extra::Client(_) => Ok(()), _ => Err(Error::new(EBADF)) } } + + pub fn events(&mut self) -> usize { + let mut events = 0; + match self.extra { + Extra::Listener(_) => { + if let Connection::Open(_) = self.remote { + // Send writable because that's what smolnetd does for TcpListener + if !self.notified_write { + self.notified_write = true; + events |= EVENT_WRITE; + } + } + }, + Extra::Client(ref mut client) => { + if let Connection::Open(_) = self.remote { + if !self.notified_write { + self.notified_write = true; + events |= EVENT_WRITE; + } + } + if !client.buffer.is_empty() || self.remote == Connection::Closed { + if !self.notified_read { + self.notified_read = true; + events |= EVENT_READ; + } + } else { + self.notified_read = false; + } + } + } + events + } } #[derive(Default)] @@ -78,32 +112,9 @@ pub struct ChanScheme { impl ChanScheme { pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { for (id, handle) in &mut self.handles { - match handle.extra { - Extra::Listener(_) => { - if let Connection::Open(_) = handle.remote { - // Send writable because that's what smolnetd does for TcpListener - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; - } - } - }, - Extra::Client(ref mut client) => { - if let Connection::Open(_) = handle.remote { - if !handle.notified_write { - handle.notified_write = true; - post_fevent(file, *id, EVENT_WRITE)?; - } - } - if !client.buffer.is_empty() || handle.remote == Connection::Closed { - if !handle.notified_read { - handle.notified_read = true; - post_fevent(file, *id, EVENT_READ)?; - } - } else { - handle.notified_read = false; - } - } + let events = handle.events(); + if events > 0 { + post_fevent(file, *id, events)?; } } Ok(()) @@ -209,7 +220,8 @@ impl SchemeBlockMut for ChanScheme { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; handle.notified_read = false; handle.notified_write = false; - Ok(Some(id)) + + Ok(Some(handle.events())) } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { let (flags, remote) = { From 50e0b499f401e5bc59ac25709b15c1a39d1f68d4 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Thu, 25 Apr 2019 14:50:50 +0200 Subject: [PATCH 28/57] Cleanup event system --- .gitignore | 1 + Cargo.lock | 2 + examples/event.rs | 2 +- examples/{client.rs => helloclient.rs} | 0 examples/{server.rs => helloserver.rs} | 0 examples/logserver.rs | 92 ++++++++++++++ rust-toolchain | 1 + src/chan.rs | 166 +++++++++++++------------ src/main.rs | 31 ++--- src/shm.rs | 16 ++- 10 files changed, 209 insertions(+), 102 deletions(-) rename examples/{client.rs => helloclient.rs} (100%) rename examples/{server.rs => helloserver.rs} (100%) create mode 100644 examples/logserver.rs create mode 100644 rust-toolchain diff --git a/.gitignore b/.gitignore index 53eaa21960..899ce2fc38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target **/*.rs.bk +/Xargo.toml diff --git a/Cargo.lock b/Cargo.lock index fc543909b3..44729d936b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "ipcd" version = "0.1.0" diff --git a/examples/event.rs b/examples/event.rs index 35379e54af..6a42b45a78 100644 --- a/examples/event.rs +++ b/examples/event.rs @@ -68,7 +68,7 @@ fn main() -> io::Result<()> { event_file.read(&mut event)?; assert_eq!(event.data, TOKEN_SERVER); - assert_eq!(event.flags, syscall::EVENT_WRITE); + assert_eq!(event.flags, syscall::EVENT_WRITE | syscall::EVENT_READ); println!("-> Accept event"); println!("Testing write events..."); diff --git a/examples/client.rs b/examples/helloclient.rs similarity index 100% rename from examples/client.rs rename to examples/helloclient.rs diff --git a/examples/server.rs b/examples/helloserver.rs similarity index 100% rename from examples/server.rs rename to examples/helloserver.rs diff --git a/examples/logserver.rs b/examples/logserver.rs new file mode 100644 index 0000000000..708e11d363 --- /dev/null +++ b/examples/logserver.rs @@ -0,0 +1,92 @@ +use std::{ + collections::HashMap, + fs::File, + io::{self, prelude::*}, + os::unix::io::{AsRawFd, FromRawFd, RawFd}, + str +}; + +fn from_syscall_error(error: syscall::Error) -> io::Error { + io::Error::from_raw_os_error(error.errno as i32) +} +fn nonblock(file: &File) -> io::Result<()> { + syscall::fcntl(file.as_raw_fd() as usize, syscall::F_SETFL, syscall::O_NONBLOCK) + .map(|_| ()) + .map_err(from_syscall_error) +} + +fn main() -> io::Result<()> { + let server = File::create("chan:log")?; + let mut event_file = File::open("event:")?; + + nonblock(&server)?; + event_file.write(&syscall::Event { + id: server.as_raw_fd() as usize, + data: 0, + flags: syscall::EVENT_READ | syscall::EVENT_WRITE, + })?; + + let mut clients = HashMap::new(); + let mut next_id = 1; + + 'outer: loop { + let mut event = syscall::Event::default(); + event_file.read(&mut event)?; + + if event.data == 0 { + println!("Listener recevied flags: {}", event.flags); + if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE { + let stream = syscall::dup(server.as_raw_fd() as usize, b"listen").map_err(from_syscall_error)?; + let stream = unsafe { File::from_raw_fd(stream as RawFd) }; + + nonblock(&stream)?; + event_file.write(&syscall::Event { + id: stream.as_raw_fd() as usize, + data: next_id, + flags: syscall::EVENT_READ | syscall::EVENT_WRITE, + })?; + + clients.insert(next_id, stream); + next_id += 1; + } + } else { + println!("Client #{} received flags: {}", event.data, event.flags); + let client = clients.get_mut(&event.data).unwrap(); + + if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE { + println!("-> Writing"); + const BUF: &str = "Hello from the log server\n"; + let mut written = 0; + while written < BUF.len() { + let len = match client.write(BUF[written..].as_bytes()) { + Ok(0) => { + println!("--> EOF"); + clients.remove(&event.data); + continue 'outer; + }, + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, + len => len? + }; + println!("--> Wrote {}/{} bytes: {:?}", len, BUF.len(), &BUF[written..]); + written += len; + } + } + if event.flags & syscall::EVENT_READ == syscall::EVENT_READ { + println!("-> Reading"); + let mut buf = [0; 128]; + loop { + let len = match client.read(&mut buf) { + Ok(0) => { + println!("--> EOF"); + clients.remove(&event.data); + continue 'outer; + }, + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, + len => len? + }; + println!("--> Read {}/128 bytes: {:?}", len, str::from_utf8(&buf)); + } + } + } + } +} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000000..07ade694b1 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/src/chan.rs b/src/chan.rs index 0626903c91..7ce69b3d95 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -40,14 +40,14 @@ impl Default for Connection { #[derive(Debug, Default)] pub struct Handle { + id: usize, flags: usize, - notified_read: bool, - notified_write: bool, - remote: Connection, extra: Extra } impl Handle { + /// Duplicate this listener handle (no checks done) into one that + /// can be connected to. pub fn accept(&mut self) -> Self { Self { flags: self.flags, @@ -56,6 +56,18 @@ impl Handle { } } + /// Mark this listener handle (no checks done) as having a + /// connection which can be accepted, but only if it is ready to + /// accept. + pub fn connect(&mut self, other: usize) -> Result<()> { + if self.remote != Connection::Waiting { + return Err(Error::new(ECONNREFUSED)); + } + self.remote = Connection::Open(other); + Ok(()) + } + + /// Error if this is not a listener pub fn require_listener(&self) -> Result<()> { match self.extra { Extra::Listener(_) => Ok(()), @@ -63,79 +75,40 @@ impl Handle { } } + /// Error if this is not a client pub fn require_client(&self) -> Result<()> { match self.extra { Extra::Client(_) => Ok(()), _ => Err(Error::new(EBADF)) } } - - pub fn events(&mut self) -> usize { - let mut events = 0; - match self.extra { - Extra::Listener(_) => { - if let Connection::Open(_) = self.remote { - // Send writable because that's what smolnetd does for TcpListener - if !self.notified_write { - self.notified_write = true; - events |= EVENT_WRITE; - } - } - }, - Extra::Client(ref mut client) => { - if let Connection::Open(_) = self.remote { - if !self.notified_write { - self.notified_write = true; - events |= EVENT_WRITE; - } - } - if !client.buffer.is_empty() || self.remote == Connection::Closed { - if !self.notified_read { - self.notified_read = true; - events |= EVENT_READ; - } - } else { - self.notified_read = false; - } - } - } - events - } } -#[derive(Default)] pub struct ChanScheme { handles: HashMap, listeners: HashMap, - next_id: usize + next_id: usize, + pub socket: File } impl ChanScheme { - pub fn post_fevents(&mut self, file: &mut File) -> io::Result<()> { - for (id, handle) in &mut self.handles { - let events = handle.events(); - if events > 0 { - post_fevent(file, *id, events)?; - } - } - Ok(()) + pub fn new() -> io::Result { + Ok(Self { + handles: HashMap::new(), + listeners: HashMap::new(), + next_id: 0, + socket: File::create(":chan")? + }) } } -fn connect(target: &mut Handle, new_id: usize) -> Result<()> { - if target.remote != Connection::Waiting { - return Err(Error::new(ECONNREFUSED)); - } - target.remote = Connection::Open(new_id); - Ok(()) -} 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)))?; + let new_id = self.next_id; let mut new = Handle::default(); new.flags = flags; - let new_id = self.next_id; if flags & O_CREAT == O_CREAT { if self.listeners.contains_key(path) { return Err(Error::new(EADDRINUSE)); @@ -147,9 +120,14 @@ impl SchemeBlockMut for ChanScheme { } new.extra = Extra::Listener(listener); } else { - let listener_id = self.listeners.get(path).ok_or(Error::new(ENOENT))?; - let listener = self.handles.get_mut(listener_id).expect("orphan listener left over"); - connect(listener, new_id)?; + let listener_id = *self.listeners.get(path).ok_or(Error::new(ENOENT))?; + let listener = self.handles.get_mut(&listener_id).expect("orphan listener left over"); + listener.connect(new_id)?; + + // smoltcp sends writeable whenever a listener gets a + // client, we'll do the same too (but also readable, why + // not) + post_fevent(&mut self.socket, listener_id, EVENT_READ | EVENT_WRITE)?; } self.handles.insert(new_id, new); @@ -166,18 +144,17 @@ impl SchemeBlockMut for ChanScheme { }; if let Connection::Open(remote_id) = remote { let new_id = self.next_id; + let new = self.handles.get_mut(&id).map(Handle::accept).unwrap(); - let clone = self.handles.get_mut(&id).map(Handle::accept).unwrap(); + // Hook the remote side, assuming it's still + // connected, up to this one so the connection is + // mutal. + let mut remote = self.handles.get_mut(&remote_id).ok_or(Error::new(ECONNRESET))?; + remote.remote = Connection::Open(new_id); + post_fevent(&mut self.socket, remote_id, EVENT_WRITE)?; - { - // This might fail if the remote side closed early - let mut remote = self.handles.get_mut(&remote_id).ok_or(Error::new(ECONNRESET))?; - remote.remote = Connection::Open(new_id); - } - - self.handles.insert(new_id, clone); + self.handles.insert(new_id, new); self.next_id += 1; - Ok(Some(new_id)) } else if flags & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) @@ -186,15 +163,17 @@ impl SchemeBlockMut for ChanScheme { } }, b"connect" => { - let new = Handle::default(); let new_id = self.next_id; + let new = Handle::default(); - { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - handle.require_listener()?; + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + handle.require_listener()?; + handle.connect(new_id)?; - connect(handle, new_id)?; - } + // smoltcp sends writeable whenever a listener gets a + // client, we'll do the same too (but also readable, + // why not) + post_fevent(&mut self.socket, id, EVENT_READ | EVENT_WRITE)?; self.handles.insert(new_id, new); self.next_id += 1; @@ -218,10 +197,21 @@ impl SchemeBlockMut for ChanScheme { } fn fevent(&mut self, id: usize, _flags: usize) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - handle.notified_read = false; - handle.notified_write = false; - - Ok(Some(handle.events())) + let mut events = 0; + match handle.extra { + Extra::Client(ref client) => { + if let Connection::Open(_) = handle.remote { + events |= EVENT_WRITE; + } + if !client.buffer.is_empty() || handle.remote == Connection::Closed { + events |= EVENT_READ; + } + }, + Extra::Listener(_) => if let Connection::Open(_) = handle.remote { + events |= EVENT_READ; + } + } + Ok(Some(events)) } fn write(&mut self, id: usize, buf: &[u8]) -> Result> { let (flags, remote) = { @@ -234,16 +224,21 @@ impl SchemeBlockMut for ChanScheme { match remote.extra { Extra::Client(ref mut client) => { client.buffer.extend(buf); + if client.buffer.len() == buf.len() { + // Send readable only if it wasn't readable + // before + post_fevent(&mut self.socket, remote_id, EVENT_READ)?; + } Ok(Some(buf.len())) }, Extra::Listener(_) => panic!("somehow, a client was connected to a listener directly") } - } else if remote == Connection::Waiting && flags & O_NONBLOCK == O_NONBLOCK { - Err(Error::new(EAGAIN)) - } else if remote == Connection::Waiting { - Ok(None) - } else { + } else if remote == Connection::Closed { Err(Error::new(EPIPE)) + } else if flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) + } else { + Ok(None) } } fn fsync(&mut self, id: usize) -> Result> { @@ -281,10 +276,19 @@ impl SchemeBlockMut for ChanScheme { if let Connection::Open(remote_id) = handle.remote { let mut remote = self.handles.get_mut(&remote_id).unwrap(); remote.remote = Connection::Closed; + match remote.extra { + Extra::Client(ref client) => if client.buffer.is_empty() { + // Post readable on EOF only if it wasn't + // readable before + post_fevent(&mut self.socket, remote_id, EVENT_READ)?; + }, + Extra::Listener(_) => panic!("a client can't be connected to a listener!") + } } }, Extra::Listener(listener) => { - // Clients never register server's remote + // Clients never reference listeners in any way, it's + // safe to drop if let Some(path) = listener.path { self.listeners.remove(&path); diff --git a/src/main.rs b/src/main.rs index 4110431c80..2242cacafa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::{ io::{self, prelude::*}, os::unix::io::AsRawFd }; -use syscall::{flag::*, Event, Packet, SchemeBlockMut, SchemeMut}; +use syscall::{flag::*, error, Error, Event, Packet, SchemeBlockMut, SchemeMut}; mod chan; mod shm; @@ -19,7 +19,7 @@ fn from_syscall_error(error: syscall::Error) -> io::Error { const TOKEN_CHAN: usize = 0; const TOKEN_SHM: usize = 1; -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if unsafe { syscall::clone(0) }.map_err(from_syscall_error)? != 0 { return Ok(()); } @@ -27,22 +27,19 @@ fn main() -> Result<(), Box> { // Create event listener for both files let mut event_file = File::open("event:")?; - let mut chan_file = File::create(":chan")?; + let mut chan = ChanScheme::new()?; event_file.write(&Event { - id: chan_file.as_raw_fd() as usize, + id: chan.socket.as_raw_fd() as usize, flags: EVENT_READ, data: TOKEN_CHAN })?; - - let mut shm_file = File::create(":shm")?; + let mut shm = ShmScheme::new()?; event_file.write(&Event { - id: shm_file.as_raw_fd() as usize, + id: shm.socket.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)?; @@ -54,7 +51,7 @@ fn main() -> Result<(), Box> { match event.data { TOKEN_CHAN => { let mut packet = Packet::default(); - chan_file.read(&mut packet)?; + chan.socket.read(&mut packet)?; // Put new packet first in the queue todo.push_front(packet); @@ -67,7 +64,7 @@ fn main() -> Result<(), Box> { // Send packet back with new ID let mut packet = *packet; packet.a = status; - if let Err(err) = chan_file.write(&packet) { + if let Err(err) = chan.socket.write(&packet) { error = Some(err); } return false; @@ -79,24 +76,21 @@ fn main() -> Result<(), Box> { if let Some(err) = error { return Err(Box::new(err)); } - - // Handle fevents - chan.post_fevents(&mut chan_file)?; }, TOKEN_SHM => { let mut packet = Packet::default(); - shm_file.read(&mut packet)?; + shm.socket.read(&mut packet)?; // Handle packet and update `a` to be status code shm.handle(&mut packet); - shm_file.write(&packet)?; + shm.socket.write(&packet)?; }, _ => () } } } -fn post_fevent(file: &mut File, id: usize, flag: usize) -> io::Result<()> { +fn post_fevent(file: &mut File, id: usize, flag: usize) -> syscall::Result<()> { file.write(&syscall::Packet { a: syscall::SYS_FEVENT, b: id, @@ -104,5 +98,6 @@ fn post_fevent(file: &mut File, id: usize, flag: usize) -> io::Result<()> { d: 1, ..Default::default() }) - .map(|_| ()) + .map(|_| ()) + .map_err(|_| Error::new(error::EIO)) } diff --git a/src/shm.rs b/src/shm.rs index 8a4b529151..a11b34e463 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -1,6 +1,8 @@ use std::{ cmp, collections::{HashMap, hash_map::Entry}, + fs::File, + io, rc::Rc }; use syscall::{error::*, Error, Map, SchemeMut, Result}; @@ -10,11 +12,21 @@ pub struct ShmHandle { buffer: Option>, refs: usize } -#[derive(Default)] pub struct ShmScheme { maps: HashMap, ShmHandle>, handles: HashMap>, - next_id: usize + next_id: usize, + pub socket: File +} +impl ShmScheme { + pub fn new() -> io::Result { + Ok(Self { + maps: HashMap::new(), + handles: HashMap::new(), + next_id: 0, + socket: File::create(":shm")? + }) + } } impl SchemeMut for ShmScheme { From 85f6cea0bdfaaa9ae7a01ee5a8e9bb99233915be Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Thu, 25 Apr 2019 17:06:37 +0200 Subject: [PATCH 29/57] Support multiple connectors to listener --- examples/logserver.rs | 68 +++++----- examples/main.rs | 24 +++- src/chan.rs | 294 +++++++++++++++++++++++------------------- 3 files changed, 210 insertions(+), 176 deletions(-) diff --git a/examples/logserver.rs b/examples/logserver.rs index 708e11d363..1a2159a519 100644 --- a/examples/logserver.rs +++ b/examples/logserver.rs @@ -9,17 +9,14 @@ use std::{ fn from_syscall_error(error: syscall::Error) -> io::Error { io::Error::from_raw_os_error(error.errno as i32) } -fn nonblock(file: &File) -> io::Result<()> { - syscall::fcntl(file.as_raw_fd() as usize, syscall::F_SETFL, syscall::O_NONBLOCK) - .map(|_| ()) - .map_err(from_syscall_error) -} fn main() -> io::Result<()> { let server = File::create("chan:log")?; let mut event_file = File::open("event:")?; - nonblock(&server)?; + syscall::fcntl(server.as_raw_fd() as usize, syscall::F_SETFL, syscall::O_NONBLOCK) + .map(|_| ()) + .map_err(from_syscall_error)?; event_file.write(&syscall::Event { id: server.as_raw_fd() as usize, data: 0, @@ -36,41 +33,28 @@ fn main() -> io::Result<()> { if event.data == 0 { println!("Listener recevied flags: {}", event.flags); if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE { - let stream = syscall::dup(server.as_raw_fd() as usize, b"listen").map_err(from_syscall_error)?; - let stream = unsafe { File::from_raw_fd(stream as RawFd) }; + loop { + let stream = match syscall::dup(server.as_raw_fd() as usize, b"listen").map_err(from_syscall_error) { + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, + stream => stream? + }; + let stream = unsafe { File::from_raw_fd(stream as RawFd) }; - nonblock(&stream)?; - event_file.write(&syscall::Event { - id: stream.as_raw_fd() as usize, - data: next_id, - flags: syscall::EVENT_READ | syscall::EVENT_WRITE, - })?; + event_file.write(&syscall::Event { + id: stream.as_raw_fd() as usize, + data: next_id, + flags: syscall::EVENT_READ | syscall::EVENT_WRITE, + })?; - clients.insert(next_id, stream); - next_id += 1; + clients.insert(next_id, stream); + println!("-> Spawned client #{}", next_id); + next_id += 1; + } } } else { println!("Client #{} received flags: {}", event.data, event.flags); let client = clients.get_mut(&event.data).unwrap(); - if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE { - println!("-> Writing"); - const BUF: &str = "Hello from the log server\n"; - let mut written = 0; - while written < BUF.len() { - let len = match client.write(BUF[written..].as_bytes()) { - Ok(0) => { - println!("--> EOF"); - clients.remove(&event.data); - continue 'outer; - }, - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, - len => len? - }; - println!("--> Wrote {}/{} bytes: {:?}", len, BUF.len(), &BUF[written..]); - written += len; - } - } if event.flags & syscall::EVENT_READ == syscall::EVENT_READ { println!("-> Reading"); let mut buf = [0; 128]; @@ -84,7 +68,21 @@ fn main() -> io::Result<()> { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, len => len? }; - println!("--> Read {}/128 bytes: {:?}", len, str::from_utf8(&buf)); + println!("--> Read {}/128 bytes: {:?}", len, str::from_utf8(&buf[..len])); + } + } + if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE { + println!("-> Writing"); + const BUF: &str = "Hello from the log server\n"; + let mut written = 0; + while written < BUF.len() { + let len = match client.write(BUF[written..].as_bytes()) { + Ok(0) => panic!("EOF should never happen here"), + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, + len => len? + }; + println!("--> Wrote {}/{} bytes: {:?}", len, BUF.len(), &BUF[written..]); + written += len; } } } diff --git a/examples/main.rs b/examples/main.rs index 2a41aebb6d..7a15762386 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -1,5 +1,5 @@ use std::{ - fs::File, + fs::{File, OpenOptions}, io::{self, prelude::*}, os::unix::io::{AsRawFd, FromRawFd, RawFd}, thread, @@ -23,11 +23,24 @@ fn main() -> io::Result<()> { let mut buf = [0; 5]; let server = File::create("chan:hello_world")?; { - let mut client = File::open("chan:hello_world")?; - // First client not accepted yet - assert_eq!(File::open("chan:hello_world").unwrap_err().kind(), io::ErrorKind::ConnectionRefused); + println!("Testing O_EXCL..."); + assert_eq!( + OpenOptions::new() + .write(true) + .create_new(true) + .open("chan:hello_world").unwrap_err().kind(), + io::ErrorKind::AlreadyExists + ); + + println!("Testing connecting..."); + + File::open("chan:hello_world")?; // closed connection will silently be skipped + let mut client = File::create("chan:hello_world")?; // O_CREAT without O_EXCL does nothing + let tmp = File::open("chan:hello_world")?; // multiple connections are handled let mut stream = dup(&server, "listen")?; + assert!(dup(&server, "listen").is_ok()); + drop(tmp); println!("Testing basic I/O..."); @@ -89,9 +102,6 @@ fn main() -> io::Result<()> { println!("-> Write before accept would block"); } - assert_eq!(dup(&server, "listen").unwrap_err().kind(), io::ErrorKind::ConnectionReset); - println!("-> Server can't accept dropped client"); - let mut client = dup(&server, "connect")?; nonblock(&client)?; diff --git a/src/chan.rs b/src/chan.rs index 7ce69b3d95..0378d0099a 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -1,20 +1,21 @@ use crate::post_fevent; use std::{ cmp, - collections::HashMap, + collections::{HashMap, VecDeque}, fs::File, - io, - mem + io }; use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; #[derive(Debug, Default)] pub struct Client { - buffer: Vec + buffer: Vec, + remote: Connection } #[derive(Debug, Default)] pub struct Listener { - path: Option + path: Option, + awaiting: VecDeque } #[derive(Debug)] pub enum Extra { @@ -42,43 +43,48 @@ impl Default for Connection { pub struct Handle { id: usize, flags: usize, - remote: Connection, extra: Extra } impl Handle { - /// Duplicate this listener handle (no checks done) into one that - /// can be connected to. - pub fn accept(&mut self) -> Self { + /// Duplicate this listener handle into one that is linked to the + /// specified remote. + /// Does NOT error if this is not a listener + pub fn accept(&self, remote: usize) -> Self { Self { flags: self.flags, - remote: mem::replace(&mut self.remote, Connection::Waiting), + extra: Extra::Client(Client { + remote: Connection::Open(remote), + ..Client::default() + }), ..Default::default() } } - /// Mark this listener handle (no checks done) as having a - /// connection which can be accepted, but only if it is ready to - /// accept. + /// Mark this listener handle as having a connection which can be + /// accepted, but only if it is ready to accept. + /// Errors if this is not a listener pub fn connect(&mut self, other: usize) -> Result<()> { - if self.remote != Connection::Waiting { - return Err(Error::new(ECONNREFUSED)); + match self.extra { + Extra::Listener(ref mut listener) => { + listener.awaiting.push_back(other); + Ok(()) + }, + _ => Err(Error::new(EBADF)) } - self.remote = Connection::Open(other); - Ok(()) } /// Error if this is not a listener - pub fn require_listener(&self) -> Result<()> { + pub fn require_listener(&mut self) -> Result<&mut Listener> { match self.extra { - Extra::Listener(_) => Ok(()), + Extra::Listener(ref mut listener) => Ok(listener), _ => Err(Error::new(EBADF)) } } /// Error if this is not a client - pub fn require_client(&self) -> Result<()> { + pub fn require_client(&mut self) -> Result<&mut Client> { match self.extra { - Extra::Client(_) => Ok(()), + Extra::Client(ref mut client) => Ok(client), _ => Err(Error::new(EBADF)) } } @@ -102,6 +108,12 @@ impl ChanScheme { } 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)))?; @@ -109,16 +121,17 @@ impl SchemeBlockMut for ChanScheme { let mut new = Handle::default(); new.flags = flags; - if flags & O_CREAT == O_CREAT { - if self.listeners.contains_key(path) { - return Err(Error::new(EADDRINUSE)); - } + let create = flags & O_CREAT == O_CREAT; + + if create && !self.listeners.contains_key(path) { let mut listener = Listener::default(); if !path.is_empty() { self.listeners.insert(String::from(path), new_id); listener.path = Some(String::from(path)); } new.extra = Extra::Listener(listener); + } else if create && flags & O_EXCL == O_EXCL { + return Err(Error::new(EEXIST)); } else { let listener_id = *self.listeners.get(path).ok_or(Error::new(ENOENT))?; let listener = self.handles.get_mut(&listener_id).expect("orphan listener left over"); @@ -137,29 +150,37 @@ impl SchemeBlockMut for ChanScheme { fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { match buf { b"listen" => { - let (flags, remote) = { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - handle.require_listener()?; - (handle.flags, handle.remote) - }; - if let Connection::Open(remote_id) = remote { - let new_id = self.next_id; - let new = self.handles.get_mut(&id).map(Handle::accept).unwrap(); + loop { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let listener = handle.require_listener()?; - // Hook the remote side, assuming it's still - // connected, up to this one so the connection is - // mutal. - let mut remote = self.handles.get_mut(&remote_id).ok_or(Error::new(ECONNRESET))?; - remote.remote = Connection::Open(new_id); - post_fevent(&mut self.socket, remote_id, EVENT_WRITE)?; + break if let Some(remote_id) = listener.awaiting.pop_front() { + let new_id = self.next_id; + let new = handle.accept(remote_id); - self.handles.insert(new_id, new); - self.next_id += 1; - Ok(Some(new_id)) - } else if flags & O_NONBLOCK == O_NONBLOCK { - Err(Error::new(EAGAIN)) - } else { - Ok(None) + // Hook the remote side, assuming it's still + // connected, up to this one so the connection is + // mutal. + let remote = match self.handles.get_mut(&remote_id) { + Some(client) => client, + None => continue // Check next client + }; + match remote.extra { + Extra::Client(ref mut client) => { + client.remote = Connection::Open(new_id); + }, + Extra::Listener(_) => panic!("newly created handle can't possibly be a listener") + } + post_fevent(&mut self.socket, remote_id, EVENT_WRITE)?; + + self.handles.insert(new_id, new); + self.next_id += 1; + Ok(Some(new_id)) + } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) + } else { + Ok(None) + }; } }, b"connect" => { @@ -184,6 +205,95 @@ impl SchemeBlockMut for ChanScheme { } } } + + // ___ ___ ___ ____ _ ___ ____ _____ + // |_ _/ _ \ ( _ ) / ___| | / _ \/ ___|| ____| + // | | | | | / _ \/\ | | | | | | | \___ \| _| + // | | |_| | | (_> < | |___| |__| |_| |___) | |___ + // |___\___/ \___/\/ \____|_____\___/|____/|_____| + + fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let client = handle.require_client()?; + + if let Connection::Open(remote_id) = client.remote { + let remote = self.handles.get_mut(&remote_id).unwrap(); + match remote.extra { + Extra::Client(ref mut client) => { + client.buffer.extend(buf); + if client.buffer.len() == buf.len() { + // Send readable only if it wasn't readable + // before + post_fevent(&mut self.socket, remote_id, EVENT_READ)?; + } + Ok(Some(buf.len())) + }, + Extra::Listener(_) => panic!("somehow, a client was connected to a listener directly") + } + } else if client.remote == Connection::Closed { + Err(Error::new(EPIPE)) + } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) + } else { + Ok(None) + } + } + fn fsync(&mut self, id: usize) -> Result> { + self.handles.get(&id) + .ok_or(Error::new(EBADF)) + .and(Ok(Some(id))) + } + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let client = handle.require_client()?; + + if !client.buffer.is_empty() { + let len = cmp::min(buf.len(), client.buffer.len()); + buf[..len].copy_from_slice(&client.buffer[..len]); + client.buffer.drain(..len); + Ok(Some(len)) + } else if client.remote == Connection::Closed { + // Remote dropped, send EOF + Ok(Some(0)) + } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + Err(Error::new(EAGAIN)) + } else { + Ok(None) + } + } + fn close(&mut self, id: usize) -> Result> { + let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; + + match handle.extra { + Extra::Client(client) => if let Connection::Open(remote_id) = client.remote { + let remote = self.handles.get_mut(&remote_id).unwrap(); + + match remote.extra { + Extra::Client(ref mut client) => { + client.remote = Connection::Closed; + if client.buffer.is_empty() { + // Post readable on EOF only if it wasn't + // readable before + post_fevent(&mut self.socket, remote_id, EVENT_READ)?; + } + }, + Extra::Listener(_) => panic!("a client can't be connected to a listener!") + } + }, + Extra::Listener(listener) => if let Some(path) = listener.path { + self.listeners.remove(&path); + } + } + Ok(Some(0)) + } + + + // ____ _ ____ _ __ __ _____ _____ _____ ____ ____ + // | _ \ / \ | _ \ / \ | \/ | ____|_ _| ____| _ \/ ___| + // | |_) / _ \ | |_) | / _ \ | |\/| | _| | | | _| | |_) \___ \ + // | __/ ___ \| _ < / ___ \| | | | |___ | | | |___| _ < ___) | + // |_| /_/ \_\_| \_\/_/ \_\_| |_|_____| |_| |_____|_| \_\____/ + fn fcntl(&mut self, id: usize, cmd: usize, arg: usize) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; match cmd { @@ -200,101 +310,17 @@ impl SchemeBlockMut for ChanScheme { let mut events = 0; match handle.extra { Extra::Client(ref client) => { - if let Connection::Open(_) = handle.remote { + if let Connection::Open(_) = client.remote { events |= EVENT_WRITE; } - if !client.buffer.is_empty() || handle.remote == Connection::Closed { + if !client.buffer.is_empty() || client.remote == Connection::Closed { events |= EVENT_READ; } }, - Extra::Listener(_) => if let Connection::Open(_) = handle.remote { - events |= EVENT_READ; + Extra::Listener(ref listener) => if !listener.awaiting.is_empty() { + events |= EVENT_READ | EVENT_WRITE; } } Ok(Some(events)) } - fn write(&mut self, id: usize, buf: &[u8]) -> Result> { - let (flags, remote) = { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - handle.require_client()?; - (handle.flags, handle.remote) - }; - if let Connection::Open(remote_id) = remote { - let remote = self.handles.get_mut(&remote_id).unwrap(); - match remote.extra { - Extra::Client(ref mut client) => { - client.buffer.extend(buf); - if client.buffer.len() == buf.len() { - // Send readable only if it wasn't readable - // before - post_fevent(&mut self.socket, remote_id, EVENT_READ)?; - } - Ok(Some(buf.len())) - }, - Extra::Listener(_) => panic!("somehow, a client was connected to a listener directly") - } - } else if remote == Connection::Closed { - Err(Error::new(EPIPE)) - } else if flags & O_NONBLOCK == O_NONBLOCK { - Err(Error::new(EAGAIN)) - } else { - Ok(None) - } - } - fn fsync(&mut self, id: usize) -> Result> { - self.handles.get(&id) - .ok_or(Error::new(EBADF)) - .and(Ok(Some(id))) - } - fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - - let client = match handle.extra { - Extra::Client(ref mut client) => client, - Extra::Listener(_) => return Err(Error::new(EBADF)) - }; - - if !client.buffer.is_empty() { - let len = cmp::min(buf.len(), client.buffer.len()); - buf[..len].copy_from_slice(&client.buffer[..len]); - client.buffer.drain(..len); - Ok(Some(len)) - } else if handle.remote == Connection::Closed { - // Remote dropped, send EOF - Ok(Some(0)) - } else if handle.flags & O_NONBLOCK == O_NONBLOCK { - Err(Error::new(EAGAIN)) - } else { - Ok(None) - } - } - fn close(&mut self, id: usize) -> Result> { - let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - - match handle.extra { - Extra::Client(_) => { - if let Connection::Open(remote_id) = handle.remote { - let mut remote = self.handles.get_mut(&remote_id).unwrap(); - remote.remote = Connection::Closed; - match remote.extra { - Extra::Client(ref client) => if client.buffer.is_empty() { - // Post readable on EOF only if it wasn't - // readable before - post_fevent(&mut self.socket, remote_id, EVENT_READ)?; - }, - Extra::Listener(_) => panic!("a client can't be connected to a listener!") - } - } - }, - Extra::Listener(listener) => { - // Clients never reference listeners in any way, it's - // safe to drop - - if let Some(path) = listener.path { - self.listeners.remove(&path); - } - } - } - Ok(Some(0)) - } } From 2653a4ff982e9b81b677eb07620bca4329898fe7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sun, 7 Jul 2019 11:53:34 -0600 Subject: [PATCH 30/57] Add redoxer ci --- .gitlab-ci.yml | 45 ++++++++++++++++++++++----------------------- redoxer.sh | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) create mode 100755 redoxer.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a812edea17..ecd54e59f9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,26 +1,25 @@ -# This file is a template, and might need editing before it works on your project. -# Official language image. Look for the different tagged releases at: -# https://hub.docker.com/r/library/rust/tags/ -image: "rust:latest" +image: "redoxos/redoxer" -# Optional: Pick zero or more services to be used on all builds. -# Only needed when using a docker container to run your tests in. -# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service -#services: -# - mysql:latest -# - redis:latest -# - postgres:latest +stages: + - build + - test -# Optional: Install a C compiler, cmake and git into the container. -# You will often need this when you (or any of your dependencies) depends on C code. -#before_script: -#- apt-get update -yqq -#- apt-get install -yqq --no-install-recommends build-essential +cache: + paths: + - target/ -# Use cargo to test the project -test:cargo: - script: - - rustup toolchain add nightly - - rustup target add x86_64-unknown-redox --toolchain nightly - - rustup show # Print version info for debugging - - cargo +nightly check --verbose --target x86_64-unknown-redox +build:redox: + stage: build + script: redoxer build --verbose + +test:redox: + stage: test + dependencies: + - build:redox + script: + - redoxer build --verbose --examples + - redoxer exec --folder . -- sh -- ./redoxer.sh + event + main + unnamed + #TODO: shm diff --git a/redoxer.sh b/redoxer.sh new file mode 100755 index 0000000000..2bcdc680bd --- /dev/null +++ b/redoxer.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -e + +old_path="file:/bin/ipcd" +new_path="target/x86_64-unknown-redox/debug/ipcd" +if [ -e "${new_path}" ] +then + mv -v "${new_path}" "${old_path}" + shutdown --reboot +fi + +while [ "$#" != "0" ] +do + example="$1" + shift + + echo "# ${example} #" + "target/x86_64-unknown-redox/debug/examples/${example}" +done From b6cc9f2ebdf4873fa0e1df24795d03071c54d795 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 18 Jul 2019 20:22:38 -0600 Subject: [PATCH 31/57] Mount chan and shm O_NONBLOCK --- src/chan.rs | 12 +++++++++--- src/shm.rs | 14 ++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/chan.rs b/src/chan.rs index 0378d0099a..38df91fca7 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -2,8 +2,9 @@ use crate::post_fevent; use std::{ cmp, collections::{HashMap, VecDeque}, - fs::File, - io + fs::{File, OpenOptions}, + io, + os::unix::fs::OpenOptionsExt, }; use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; @@ -102,7 +103,12 @@ impl ChanScheme { handles: HashMap::new(), listeners: HashMap::new(), next_id: 0, - socket: File::create(":chan")? + socket: OpenOptions::new() + .read(true) + .write(true) + .create(true) + .custom_flags(O_NONBLOCK as i32) + .open(":chan")? }) } } diff --git a/src/shm.rs b/src/shm.rs index a11b34e463..2a9a081364 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -1,11 +1,12 @@ use std::{ cmp, collections::{HashMap, hash_map::Entry}, - fs::File, + fs::{File, OpenOptions}, io, - rc::Rc + os::unix::fs::OpenOptionsExt, + rc::Rc, }; -use syscall::{error::*, Error, Map, SchemeMut, Result}; +use syscall::{error::*, flag::O_NONBLOCK, Error, Map, SchemeMut, Result}; #[derive(Default)] pub struct ShmHandle { @@ -24,7 +25,12 @@ impl ShmScheme { maps: HashMap::new(), handles: HashMap::new(), next_id: 0, - socket: File::create(":shm")? + socket: OpenOptions::new() + .read(true) + .write(true) + .create(true) + .custom_flags(O_NONBLOCK as i32) + .open(":shm")? }) } } From b0bb843a65d28a34d03f1419299f78fd5227aee8 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 18 Jul 2019 20:22:56 -0600 Subject: [PATCH 32/57] Allow unmount of chan and shm --- src/main.rs | 100 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 27 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2242cacafa..637252cae4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,13 +27,13 @@ fn main() -> Result<(), Box> { // Create event listener for both files let mut event_file = File::open("event:")?; - let mut chan = ChanScheme::new()?; + let chan = ChanScheme::new()?; event_file.write(&Event { id: chan.socket.as_raw_fd() as usize, flags: EVENT_READ, data: TOKEN_CHAN })?; - let mut shm = ShmScheme::new()?; + let shm = ShmScheme::new()?; event_file.write(&Event { id: shm.socket.as_raw_fd() as usize, flags: EVENT_READ, @@ -44,51 +44,97 @@ fn main() -> Result<(), Box> { syscall::setrens(0, 0).map_err(from_syscall_error)?; - loop { + let mut chan_opt = Some(chan); + let mut shm_opt = Some(shm); + while chan_opt.is_some() && shm_opt.is_some() { let mut event = Event::default(); event_file.read(&mut event)?; match event.data { TOKEN_CHAN => { - let mut packet = Packet::default(); - chan.socket.read(&mut packet)?; - - // Put new packet first in the queue - todo.push_front(packet); - 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.socket.write(&packet) { - error = Some(err); - } - return false; - } + let unmount = if let Some(ref mut chan) = chan_opt { + let eof = loop { + let mut packet = Packet::default(); + match chan.socket.read(&mut packet) { + Ok(0) => break true, + Ok(_) => todo.push_front(packet), + Err(err) => if err.kind() == io::ErrorKind::WouldBlock { + break false; + } else { + return Err(Box::new(err)); + } + }; + }; - true - }); + // 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.socket.write(&packet) { + error = Some(err); + } + return false; + } + + true + }); + + eof + } else { + false + }; + + if unmount { + if let Some(mut chan) = chan_opt.take() { + for mut packet in todo.drain(..) { + packet.a = Error::mux(Err(Error::new(error::ENODEV))); + if let Err(err) = chan.socket.write(&packet) { + error = Some(err); + } + } + } + } if let Some(err) = error { return Err(Box::new(err)); } }, TOKEN_SHM => { - let mut packet = Packet::default(); - shm.socket.read(&mut packet)?; + let unmount = if let Some(ref mut shm) = shm_opt { + let eof = loop { + let mut packet = Packet::default(); + match shm.socket.read(&mut packet) { + Ok(0) => break true, + Ok(_) => { + shm.handle(&mut packet); + shm.socket.write(&packet)?; + }, + Err(err) => if err.kind() == io::ErrorKind::WouldBlock { + break false; + } else { + return Err(Box::new(err)); + } + }; + }; - // Handle packet and update `a` to be status code - shm.handle(&mut packet); + eof + } else { + false + }; - shm.socket.write(&packet)?; + if unmount { + shm_opt.take(); + } }, _ => () } } + + Ok(()) } fn post_fevent(file: &mut File, id: usize, flag: usize) -> syscall::Result<()> { file.write(&syscall::Packet { From f5bcff90f80f83500ffacc4ef7930c47d1b009c6 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 18 Jul 2019 20:24:52 -0600 Subject: [PATCH 33/57] Fix unmount logic --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 637252cae4..b356ea88ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,7 +46,7 @@ fn main() -> Result<(), Box> { let mut chan_opt = Some(chan); let mut shm_opt = Some(shm); - while chan_opt.is_some() && shm_opt.is_some() { + while chan_opt.is_some() || shm_opt.is_some() { let mut event = Event::default(); event_file.read(&mut event)?; From 3ff2d28a92f7e93ee1138898a2606a5d82f01bba Mon Sep 17 00:00:00 2001 From: Coleman McFarland Date: Sun, 22 Sep 2019 13:46:46 +0000 Subject: [PATCH 34/57] Update README.md to remove broken link. Opting to just remove the link altogether to fix #1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8222b2e68..553034bdf4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A userspace daemon for interprocess communication. ## Usage -See the `examples` folder. [Hello World example](https://github.com/redox-os/ipcd/blob/master/examples/hello.rs) +See the `examples` folder. Simply open `chan:` with O_CREAT where `` is any name you'd like to create a listener. This listener can accept clients by calling `dup("listen")`. From 9526ffa80fb1ffcfd242e09803ee87d31fdc1073 Mon Sep 17 00:00:00 2001 From: Tiago Lam Date: Wed, 5 Feb 2020 23:07:49 +0000 Subject: [PATCH 35/57] chan: Support opening "empty" ChanScheme. Similarly to how the "tcp:" in Redox's netstack supports opening a first empty scheme, only to later on being dup()'ed and set with an appropriate path, this follows the same approach - a map of NullFile structs is kept and initially associated with an id. Next time dup() is called, the true Handle is created, keeping the same definitions of the previously created NullFile. Not that it is important to add support for this in the ipcd to support AF_UNIX sockets, otherwise the socket() created in relibc won't be associated with a address / path. --- src/chan.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/chan.rs b/src/chan.rs index 38df91fca7..97d1f64987 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -40,6 +40,10 @@ impl Default for Connection { } } +pub struct NullFile { + pub flags: usize, +} + #[derive(Debug, Default)] pub struct Handle { id: usize, @@ -92,6 +96,7 @@ impl Handle { } pub struct ChanScheme { + nulls: HashMap, handles: HashMap, listeners: HashMap, next_id: usize, @@ -100,6 +105,7 @@ pub struct ChanScheme { impl ChanScheme { pub fn new() -> io::Result { Ok(Self { + nulls: HashMap::new(), handles: HashMap::new(), listeners: HashMap::new(), next_id: 0, @@ -124,6 +130,20 @@ impl SchemeBlockMut for ChanScheme { let path = ::std::str::from_utf8(path).or(Err(Error::new(EPERM)))?; let new_id = self.next_id; + + if path.is_empty() { + let null = NullFile { + flags: flags, + }; + + let id = new_id; + + self.nulls.insert(id, null); + self.next_id += 1; + + return Ok(Some(id)); + } + let mut new = Handle::default(); new.flags = flags; @@ -154,6 +174,13 @@ impl SchemeBlockMut for ChanScheme { Ok(Some(new_id)) } fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { + if let Some(flags) = self.nulls + .get(&id) + .map(|null| null.flags) + { + return self.open(buf, flags, 0, 0); + } + match buf { b"listen" => { loop { @@ -268,6 +295,10 @@ impl SchemeBlockMut for ChanScheme { } } fn close(&mut self, id: usize) -> Result> { + if let Some(_null) = self.nulls.remove(&id) { + return Ok(Some(0)); + } + let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; match handle.extra { From fad13404d5583b42f7a65c985df5c066c4cfa8da Mon Sep 17 00:00:00 2001 From: Tiago Lam Date: Wed, 5 Feb 2020 23:17:30 +0000 Subject: [PATCH 36/57] chan: Keep path in for new Handle. While supporting AF_UNIX, once an accept() is called in relibc, in turn, a dup() is also called with "listen". While processing this ipcd effectively creates a new handler, losing track of the path of the previous handler. However, it is important to keep this path in the newly created Handler as well, so the socket can be queried later by relibc (when calling fpath()) and fill in appropriately its sockaddr_un struct. --- src/chan.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/chan.rs b/src/chan.rs index 97d1f64987..f9b3f1ccb8 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -48,7 +48,8 @@ pub struct NullFile { pub struct Handle { id: usize, flags: usize, - extra: Extra + extra: Extra, + path: Option, } impl Handle { /// Duplicate this listener handle into one that is linked to the @@ -186,10 +187,11 @@ impl SchemeBlockMut for ChanScheme { loop { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; let listener = handle.require_listener()?; + let listener_path = listener.path.clone(); break if let Some(remote_id) = listener.awaiting.pop_front() { let new_id = self.next_id; - let new = handle.accept(remote_id); + let mut new = handle.accept(remote_id); // Hook the remote side, assuming it's still // connected, up to this one so the connection is @@ -206,6 +208,8 @@ impl SchemeBlockMut for ChanScheme { } post_fevent(&mut self.socket, remote_id, EVENT_WRITE)?; + new.path = listener_path; + self.handles.insert(new_id, new); self.next_id += 1; Ok(Some(new_id)) @@ -271,6 +275,23 @@ impl SchemeBlockMut for ChanScheme { Ok(None) } } + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result> { + // Write scheme name + const PREFIX: &[u8] = b"chan:"; + let len = cmp::min(PREFIX.len(), buf.len()); + buf[..len].copy_from_slice(&PREFIX[..len]); + if len < PREFIX.len() { + return Ok(Some(len)); + } + + // Write path + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let path = handle.path.as_ref().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(Some(PREFIX.len() + len)) + } fn fsync(&mut self, id: usize) -> Result> { self.handles.get(&id) .ok_or(Error::new(EBADF)) From 73570b0926107d958cc406f26cea86d70d756a57 Mon Sep 17 00:00:00 2001 From: Tiago Lam Date: Sat, 8 Feb 2020 10:24:57 +0000 Subject: [PATCH 37/57] Revert "chan: Support opening "empty" ChanScheme." This reverts commit 9526ffa80fb1ffcfd242e09803ee87d31fdc1073. --- src/chan.rs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/chan.rs b/src/chan.rs index f9b3f1ccb8..86f64ae39e 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -40,10 +40,6 @@ impl Default for Connection { } } -pub struct NullFile { - pub flags: usize, -} - #[derive(Debug, Default)] pub struct Handle { id: usize, @@ -97,7 +93,6 @@ impl Handle { } pub struct ChanScheme { - nulls: HashMap, handles: HashMap, listeners: HashMap, next_id: usize, @@ -106,7 +101,6 @@ pub struct ChanScheme { impl ChanScheme { pub fn new() -> io::Result { Ok(Self { - nulls: HashMap::new(), handles: HashMap::new(), listeners: HashMap::new(), next_id: 0, @@ -131,20 +125,6 @@ impl SchemeBlockMut for ChanScheme { let path = ::std::str::from_utf8(path).or(Err(Error::new(EPERM)))?; let new_id = self.next_id; - - if path.is_empty() { - let null = NullFile { - flags: flags, - }; - - let id = new_id; - - self.nulls.insert(id, null); - self.next_id += 1; - - return Ok(Some(id)); - } - let mut new = Handle::default(); new.flags = flags; @@ -175,13 +155,6 @@ impl SchemeBlockMut for ChanScheme { Ok(Some(new_id)) } fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { - if let Some(flags) = self.nulls - .get(&id) - .map(|null| null.flags) - { - return self.open(buf, flags, 0, 0); - } - match buf { b"listen" => { loop { @@ -316,10 +289,6 @@ impl SchemeBlockMut for ChanScheme { } } fn close(&mut self, id: usize) -> Result> { - if let Some(_null) = self.nulls.remove(&id) { - return Ok(Some(0)); - } - let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; match handle.extra { From ea4247c0b5390fd8b91e9c345757ad99fe692d35 Mon Sep 17 00:00:00 2001 From: Tiago Lam Date: Sat, 8 Feb 2020 10:29:52 +0000 Subject: [PATCH 38/57] chan: Support turning unnamed into named sockets. Previous approach, reverted in commit c7cf2ca, would break unnamed sockets - since it assumed there would always be an interim step were one would first name an unnamed socket, before "listen" / "connect". Instead of using the same approach as the "tcp:" scheme in Redox's netstack, which requires an extra mapping of temporary unnamed sockets, handle the different use cases in dup(). Thus, if a "buf" is provided that's not a "connect" or "listen", it must be a new path for turning the unnamed socket into a named socket and should be handled as such. --- src/chan.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/chan.rs b/src/chan.rs index 86f64ae39e..db20f7ad28 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -211,7 +211,20 @@ impl SchemeBlockMut for ChanScheme { Ok(Some(new_id)) }, _ => { - return Err(Error::new(EBADF)); + // If a buf is provided, different than "connect" / "listen", + // turn the socket into a named socket. + + if buf == b"" { + return Err(Error::new(EBADF)); + } + + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if handle.path.is_some() { + return Err(Error::new(EBADF)); + } + + let flags = handle.flags; + return self.open(buf, flags, 0, 0); } } } From ea390f66ac6b51436a25f1e46dbd16f818d8116f Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Sun, 28 Jun 2020 12:23:47 +0200 Subject: [PATCH 39/57] Comment explaining when ipcd connects to existing --- src/chan.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chan.rs b/src/chan.rs index db20f7ad28..cd2fbf9e09 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -140,6 +140,8 @@ impl SchemeBlockMut for ChanScheme { } else if create && flags & O_EXCL == O_EXCL { return Err(Error::new(EEXIST)); } else { + // Connect to existing if: O_CREAT isn't set or it already exists + // and O_EXCL isn't set let listener_id = *self.listeners.get(path).ok_or(Error::new(ENOENT))?; let listener = self.handles.get_mut(&listener_id).expect("orphan listener left over"); listener.connect(new_id)?; @@ -214,7 +216,7 @@ impl SchemeBlockMut for ChanScheme { // If a buf is provided, different than "connect" / "listen", // turn the socket into a named socket. - if buf == b"" { + if buf.is_empty() { return Err(Error::new(EBADF)); } From 05b16898eed8a65ccfbe671b0f4a27b967597e51 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Thu, 16 Jul 2020 15:56:15 +0200 Subject: [PATCH 40/57] Update Cargo.lock --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44729d936b..53f0363a55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,13 +4,13 @@ name = "ipcd" version = "0.1.0" dependencies = [ - "redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.49" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f22c50afdcf3f0a31ebb6b47697f6a7c5e5a24967e842858118bce0615f0afad" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" From 328b88a4a4fa4a0cf4804fccb18cc035cdd3c0de Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sun, 2 Aug 2020 16:03:50 -0600 Subject: [PATCH 41/57] Update redox_syscall --- Cargo.lock | 8 +++----- rust-toolchain | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 rust-toolchain diff --git a/Cargo.lock b/Cargo.lock index 53f0363a55..b8cf9b0044 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,13 +4,11 @@ name = "ipcd" version = "0.1.0" dependencies = [ - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall", ] [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 07ade694b1..0000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly \ No newline at end of file From 0c5c45c58030e0517f051ceb4072535f5ef62419 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 19 Jan 2021 11:23:26 -0700 Subject: [PATCH 42/57] Update redox_syscall --- Cargo.lock | 13 +++++++++++-- Cargo.toml | 2 +- src/chan.rs | 4 ++-- src/main.rs | 6 +++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8cf9b0044..9b40bd5c14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "ipcd" version = "0.1.0" @@ -9,6 +15,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml index 1b92c1d369..8670d6445e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,4 @@ edition = "2018" name = "ipcd" version = "0.1.0" [dependencies] -redox_syscall = "0.1.49" +redox_syscall = "0.2.4" diff --git a/src/chan.rs b/src/chan.rs index cd2fbf9e09..a71e82716b 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -347,9 +347,9 @@ impl SchemeBlockMut for ChanScheme { _ => Err(Error::new(EINVAL)) } } - fn fevent(&mut self, id: usize, _flags: usize) -> Result> { + fn fevent(&mut self, id: usize, _flags: EventFlags) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let mut events = 0; + let mut events = EventFlags::empty(); match handle.extra { Extra::Client(ref client) => { if let Connection::Open(_) = client.remote { diff --git a/src/main.rs b/src/main.rs index b356ea88ea..345570830f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ 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 { + if unsafe { syscall::clone(CloneFlags::empty()) }.map_err(from_syscall_error)? != 0 { return Ok(()); } @@ -136,11 +136,11 @@ fn main() -> Result<(), Box> { Ok(()) } -fn post_fevent(file: &mut File, id: usize, flag: usize) -> syscall::Result<()> { +fn post_fevent(file: &mut File, id: usize, flag: EventFlags) -> syscall::Result<()> { file.write(&syscall::Packet { a: syscall::SYS_FEVENT, b: id, - c: flag, + c: flag.bits(), d: 1, ..Default::default() }) From 4080e5dd09438e4608d96f9f75086e890d5318c0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Jun 2021 14:37:32 +0200 Subject: [PATCH 43/57] Update syscall. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/chan.rs | 8 ++++---- src/shm.rs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b40bd5c14..fd0f4e11f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.4" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 8670d6445e..8ca5d13aee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,4 @@ edition = "2018" name = "ipcd" version = "0.1.0" [dependencies] -redox_syscall = "0.2.4" +redox_syscall = "0.2.9" diff --git a/src/chan.rs b/src/chan.rs index a71e82716b..717e59257f 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -121,9 +121,7 @@ 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)))?; - + fn open(&mut self, path: &str, flags: usize, _uid: u32, _gid: u32) -> Result> { let new_id = self.next_id; let mut new = Handle::default(); new.flags = flags; @@ -220,13 +218,15 @@ impl SchemeBlockMut for ChanScheme { return Err(Error::new(EBADF)); } + let path = core::str::from_utf8(buf).map_err(|_| Error::new(EBADF))?; + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; if handle.path.is_some() { return Err(Error::new(EBADF)); } let flags = handle.flags; - return self.open(buf, flags, 0, 0); + return self.open(path, flags, 0, 0); } } } diff --git a/src/shm.rs b/src/shm.rs index 2a9a081364..92a1bdb684 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -36,8 +36,8 @@ impl ShmScheme { } 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(); + fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { + let path = Rc::from(path); let entry = self.maps.entry(Rc::clone(&path)).or_insert(ShmHandle::default()); entry.refs += 1; self.handles.insert(self.next_id, path); From 3aa241558c3fb7c3039195e136a5e967a064577a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 25 Mar 2022 15:08:49 +0100 Subject: [PATCH 44/57] Update syscall --- Cargo.lock | 10 ++++++---- Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd0f4e11f2..25e5316254 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "ipcd" @@ -15,9 +17,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 8ca5d13aee..a85a34c1d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,4 @@ edition = "2018" name = "ipcd" version = "0.1.0" [dependencies] -redox_syscall = "0.2.9" +redox_syscall = "0.2.12" From 1668052a1c3b3d0330ec00ec34c7fafe5e82645f Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 26 Jul 2022 17:27:40 -0600 Subject: [PATCH 45/57] Update Cargo.lock --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25e5316254..15e3cb4027 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,9 +17,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] From c930dfdc0887dc22d544d1d7db292ac3dcbdf129 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 18 Jul 2022 16:19:28 +0200 Subject: [PATCH 46/57] Replace syscall::clone() with libc::fork(). --- Cargo.lock | 7 +++++++ Cargo.toml | 3 ++- src/main.rs | 6 ++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15e3cb4027..cc256c11bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,9 +12,16 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" name = "ipcd" version = "0.1.0" dependencies = [ + "libc", "redox_syscall", ] +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + [[package]] name = "redox_syscall" version = "0.2.16" diff --git a/Cargo.toml b/Cargo.toml index a85a34c1d4..e3a780c384 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,5 @@ edition = "2018" name = "ipcd" version = "0.1.0" [dependencies] -redox_syscall = "0.2.12" +libc = "0.2" +redox_syscall = "0.2" diff --git a/src/main.rs b/src/main.rs index 345570830f..ba556599c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,10 @@ const TOKEN_CHAN: usize = 0; const TOKEN_SHM: usize = 1; fn main() -> Result<(), Box> { - if unsafe { syscall::clone(CloneFlags::empty()) }.map_err(from_syscall_error)? != 0 { - return Ok(()); + match unsafe { libc::fork() } { + 0 => (), + -1 => return Err(Box::new(io::Error::last_os_error())), + _child => return Ok(()), } // Create event listener for both files From 1dee81c7578c1d433b4b2b49c3fb6c9d70c59385 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 11 Feb 2023 14:42:46 -0700 Subject: [PATCH 47/57] Update libc crate --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc256c11bc..c41c13af03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,9 +18,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.126" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "redox_syscall" From 113583cfca1f55e936a1e869b019d37aa09a56ce Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 4 Jul 2023 18:34:50 +0200 Subject: [PATCH 48/57] Enforce page alignment in shm:, better fmap. --- Cargo.lock | 8 ++--- Cargo.toml | 2 +- src/main.rs | 4 ++- src/shm.rs | 85 +++++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c41c13af03..9bffb936bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,15 +18,13 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +version = "0.3.5" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index e3a780c384..846a17fd2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ name = "ipcd" version = "0.1.0" [dependencies] libc = "0.2" -redox_syscall = "0.2" +redox_syscall = { path = "../../kernel/source/syscall" } diff --git a/src/main.rs b/src/main.rs index ba556599c1..0c45d7d102 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,7 +112,9 @@ fn main() -> Result<(), Box> { match shm.socket.read(&mut packet) { Ok(0) => break true, Ok(_) => { - shm.handle(&mut packet); + unsafe { + shm.do_handle(&mut packet); + } shm.socket.write(&packet)?; }, Err(err) => if err.kind() == io::ErrorKind::WouldBlock { diff --git a/src/shm.rs b/src/shm.rs index 92a1bdb684..f1855e1ee9 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -6,11 +6,11 @@ use std::{ os::unix::fs::OpenOptionsExt, rc::Rc, }; -use syscall::{error::*, flag::O_NONBLOCK, Error, Map, SchemeMut, Result}; +use syscall::{error::*, flag::O_NONBLOCK, Error, Map, SchemeMut, Result, Packet, KSMSG_MMAP_PREP, MapFlags, KSMSG_MMAP, PAGE_SIZE, MAP_PRIVATE}; #[derive(Default)] pub struct ShmHandle { - buffer: Option>, + buffer: Option, refs: usize } pub struct ShmScheme { @@ -33,6 +33,41 @@ impl ShmScheme { .open(":shm")? }) } + pub unsafe fn do_handle(&mut self, packet: &mut Packet) { + match packet.a { + KSMSG_MMAP_PREP | KSMSG_MMAP => { + let req_file = packet.b; + let req_flags = MapFlags::from_bits_truncate(packet.c); + let req_page_count = packet.d; + let req_offset = u64::from(packet.uid) | (u64::from(packet.gid) << 32); + + let res = self.ksmsg_mmap(req_file, req_flags, req_offset, req_page_count); + + *packet = Packet { + id: packet.id, + a: syscall::Error::mux(res), + ..Packet::default() + }; + } + _ => self.handle(packet), + } + } + pub fn ksmsg_mmap(&mut self, id: usize, _flags: MapFlags, offset: u64, page_count: usize) -> Result { + let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let total_size = offset as usize + page_count * PAGE_SIZE; + match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { + Some(ref mut buf) => { + if total_size > buf.len() { + return Err(Error::new(ERANGE)); + } + Ok(buf.as_ptr() + offset as usize) + }, + ref mut buf @ None => { + *buf = Some(MmapGuard::alloc(page_count)?); + Ok(buf.as_mut().unwrap().as_ptr() + offset as usize) + } + } + } } impl SchemeMut for ShmScheme { @@ -46,22 +81,6 @@ impl SchemeMut for ShmScheme { self.next_id += 1; Ok(id) } - fn fmap(&mut self, id: usize, map: &Map) -> Result { - let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; - let total_size = map.offset + map.size; - match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { - Some(ref mut buf) => { - if total_size != buf.len() { - return Err(Error::new(ERANGE)); - } - Ok(buf[map.offset..].as_mut_ptr() as usize) - }, - ref mut buf @ None => { - *buf = Some(vec![0; total_size].into_boxed_slice()); - Ok(buf.as_mut().unwrap()[map.offset..].as_mut_ptr() as usize) - } - } - } fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { // Write scheme name const PREFIX: &[u8] = b"shm:"; @@ -92,3 +111,33 @@ impl SchemeMut for ShmScheme { Ok(0) } } + +pub struct MmapGuard { + base: usize, + size: usize, +} +impl MmapGuard { + pub fn alloc(page_count: usize) -> Result { + let size = page_count * PAGE_SIZE; + let base = unsafe { syscall::fmap(!0, &Map { offset: 0, size, flags: MAP_PRIVATE, address: 0 }) }?; + + Ok(Self { + base, + size, + }) + } + pub fn len(&self) -> usize { + self.size + } + pub fn as_ptr(&self) -> usize { + self.base + } +} +impl Drop for MmapGuard { + fn drop(&mut self) { + if self.size == 0 { + return; + } + let _ = unsafe { syscall::funmap(self.base, self.size) }; + } +} From 0d8a24d50ae51114587b60087ad2d7ca6d775619 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 2 Aug 2023 14:18:58 +0200 Subject: [PATCH 49/57] Use updated syscall crate. --- Cargo.lock | 1 + Cargo.toml | 2 +- src/main.rs | 4 +++- src/shm.rs | 51 ++++++++++++++++----------------------------------- 4 files changed, 21 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bffb936bd..95af9bb638 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "redox_syscall" version = "0.3.5" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=better_fmap#f0976e1d6020e6854ca30ed1f6d61b10dfc4fa58" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 846a17fd2b..05de5520a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ name = "ipcd" version = "0.1.0" [dependencies] libc = "0.2" -redox_syscall = { path = "../../kernel/source/syscall" } +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "better_fmap" } diff --git a/src/main.rs b/src/main.rs index 0c45d7d102..bde573ea2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(int_roundings)] + use std::{ collections::VecDeque, fs::File, @@ -113,7 +115,7 @@ fn main() -> Result<(), Box> { Ok(0) => break true, Ok(_) => { unsafe { - shm.do_handle(&mut packet); + shm.handle(&mut packet); } shm.socket.write(&packet)?; }, diff --git a/src/shm.rs b/src/shm.rs index f1855e1ee9..db7c8a1ce7 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -33,41 +33,6 @@ impl ShmScheme { .open(":shm")? }) } - pub unsafe fn do_handle(&mut self, packet: &mut Packet) { - match packet.a { - KSMSG_MMAP_PREP | KSMSG_MMAP => { - let req_file = packet.b; - let req_flags = MapFlags::from_bits_truncate(packet.c); - let req_page_count = packet.d; - let req_offset = u64::from(packet.uid) | (u64::from(packet.gid) << 32); - - let res = self.ksmsg_mmap(req_file, req_flags, req_offset, req_page_count); - - *packet = Packet { - id: packet.id, - a: syscall::Error::mux(res), - ..Packet::default() - }; - } - _ => self.handle(packet), - } - } - pub fn ksmsg_mmap(&mut self, id: usize, _flags: MapFlags, offset: u64, page_count: usize) -> Result { - let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; - let total_size = offset as usize + page_count * PAGE_SIZE; - match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { - Some(ref mut buf) => { - if total_size > buf.len() { - return Err(Error::new(ERANGE)); - } - Ok(buf.as_ptr() + offset as usize) - }, - ref mut buf @ None => { - *buf = Some(MmapGuard::alloc(page_count)?); - Ok(buf.as_mut().unwrap().as_ptr() + offset as usize) - } - } - } } impl SchemeMut for ShmScheme { @@ -110,6 +75,22 @@ impl SchemeMut for ShmScheme { } Ok(0) } + fn mmap_prep(&mut self, id: usize, offset: u64, size: usize, flags: MapFlags) -> Result { + let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let total_size = offset as usize + size; + match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { + Some(ref mut buf) => { + if total_size > buf.len() { + return Err(Error::new(ERANGE)); + } + Ok(buf.as_ptr() + offset as usize) + }, + ref mut buf @ None => { + *buf = Some(MmapGuard::alloc(size.div_ceil(PAGE_SIZE))?); + Ok(buf.as_mut().unwrap().as_ptr() + offset as usize) + } + } + } } pub struct MmapGuard { From 866c7d73a7b82095f15a1f251638eb7ac592ae3e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 2 Aug 2023 15:31:22 +0200 Subject: [PATCH 50/57] Update syscall. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 05de5520a5..51ce678bc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ edition = "2018" name = "ipcd" version = "0.1.0" + [dependencies] libc = "0.2" -redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "better_fmap" } +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } From 771fffbfd6087a7a94121cf8da3f061539b48c45 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 20 Dec 2023 19:12:51 +0100 Subject: [PATCH 51/57] Migrate to libredox and redox-scheme. --- Cargo.lock | 74 ++++++++++++++++++++++-- Cargo.toml | 7 ++- src/chan.rs | 29 ++++------ src/main.rs | 159 ++++++++++++++++++++++------------------------------ src/shm.rs | 19 ++----- 5 files changed, 156 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95af9bb638..d7b714e559 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,24 +8,86 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "ipcd" version = "0.1.0" dependencies = [ - "libc", + "libredox 0.0.3", + "redox-daemon", + "redox-scheme", + "redox_event", "redox_syscall", ] [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "libredox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962b650e6b934f95cec493e1464e0f5cba98dd9ba9ef2b1c42cdfdb7864cab5a" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "redox-daemon" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353178b5771a301b9d2b318914ab5dfeb21dd6e65930faedde19248cfceca5a7" +dependencies = [ + "libc", + "libredox 0.0.1", +] + +[[package]] +name = "redox-scheme" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#265e056726066f319e47eedd5f20f9549fb4c581" +dependencies = [ + "libredox 0.0.3", + "redox_syscall", +] + +[[package]] +name = "redox_event" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b78fedc17aea72caee38ac1208e0f709fa1641af1bbe0205e7d434413a60621d" +dependencies = [ + "bitflags 2.4.1", + "libredox 0.0.1", + "redox_syscall", +] [[package]] name = "redox_syscall" -version = "0.3.5" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=better_fmap#f0976e1d6020e6854ca30ed1f6d61b10dfc4fa58" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] diff --git a/Cargo.toml b/Cargo.toml index 51ce678bc1..1cec1ae4ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,8 @@ name = "ipcd" version = "0.1.0" [dependencies] -libc = "0.2" -redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } +libredox = "0.0.3" +redox-daemon = "0.1.2" +redox_syscall = "0.4.1" +redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +redox_event = "0.2.0" diff --git a/src/chan.rs b/src/chan.rs index 717e59257f..13a79dcc91 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -1,12 +1,9 @@ -use crate::post_fevent; use std::{ cmp, collections::{HashMap, VecDeque}, - fs::{File, OpenOptions}, - io, - os::unix::fs::OpenOptionsExt, }; -use syscall::{flag::*, error::*, Error, SchemeBlockMut, Result}; +use syscall::{flag::*, error::*, Error}; +use redox_scheme::SchemeBlockMut; #[derive(Debug, Default)] pub struct Client { @@ -42,7 +39,6 @@ impl Default for Connection { #[derive(Debug, Default)] pub struct Handle { - id: usize, flags: usize, extra: Extra, path: Option, @@ -96,20 +92,15 @@ pub struct ChanScheme { handles: HashMap, listeners: HashMap, next_id: usize, - pub socket: File + pub socket: redox_scheme::Socket, } impl ChanScheme { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(Self { handles: HashMap::new(), listeners: HashMap::new(), next_id: 0, - socket: OpenOptions::new() - .read(true) - .write(true) - .create(true) - .custom_flags(O_NONBLOCK as i32) - .open(":chan")? + socket: redox_scheme::Socket::nonblock("chan")?, }) } } @@ -147,7 +138,7 @@ impl SchemeBlockMut for ChanScheme { // smoltcp sends writeable whenever a listener gets a // client, we'll do the same too (but also readable, why // not) - post_fevent(&mut self.socket, listener_id, EVENT_READ | EVENT_WRITE)?; + self.socket.post_fevent(listener_id, (EVENT_READ | EVENT_WRITE).bits())?; } self.handles.insert(new_id, new); @@ -179,7 +170,7 @@ impl SchemeBlockMut for ChanScheme { }, Extra::Listener(_) => panic!("newly created handle can't possibly be a listener") } - post_fevent(&mut self.socket, remote_id, EVENT_WRITE)?; + self.socket.post_fevent(remote_id, EVENT_WRITE.bits())?; new.path = listener_path; @@ -204,7 +195,7 @@ impl SchemeBlockMut for ChanScheme { // smoltcp sends writeable whenever a listener gets a // client, we'll do the same too (but also readable, // why not) - post_fevent(&mut self.socket, id, EVENT_READ | EVENT_WRITE)?; + self.socket.post_fevent(id, (EVENT_READ | EVENT_WRITE).bits())?; self.handles.insert(new_id, new); self.next_id += 1; @@ -249,7 +240,7 @@ impl SchemeBlockMut for ChanScheme { if client.buffer.len() == buf.len() { // Send readable only if it wasn't readable // before - post_fevent(&mut self.socket, remote_id, EVENT_READ)?; + self.socket.post_fevent(remote_id, EVENT_READ.bits())?; } Ok(Some(buf.len())) }, @@ -316,7 +307,7 @@ impl SchemeBlockMut for ChanScheme { if client.buffer.is_empty() { // Post readable on EOF only if it wasn't // readable before - post_fevent(&mut self.socket, remote_id, EVENT_READ)?; + self.socket.post_fevent(remote_id, EVENT_READ.bits())?; } }, Extra::Listener(_) => panic!("a client can't be connected to a listener!") diff --git a/src/main.rs b/src/main.rs index bde573ea2a..d91c06f750 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,9 @@ -#![feature(int_roundings)] +#![feature(int_roundings, let_chains)] -use std::{ - collections::VecDeque, - fs::File, - io::{self, prelude::*}, - os::unix::io::AsRawFd -}; -use syscall::{flag::*, error, Error, Event, Packet, SchemeBlockMut, SchemeMut}; +use std::collections::VecDeque; +use event::{EventQueue, EventFlags}; +use redox_scheme::{SignalBehavior, Response}; +use syscall::{Error, Result, EAGAIN, EWOULDBLOCK, ENODEV}; mod chan; mod shm; @@ -14,77 +11,76 @@ mod shm; 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() { + redox_daemon::Daemon::new(move |daemon| { + // TODO: Better error handling + match inner(daemon) { + Ok(()) => std::process::exit(0), + Err(error) => { + println!("ipcd failed: {error}"); + std::process::exit(1); + } + } + }).expect("ipcd: failed to daemonize"); } -const TOKEN_CHAN: usize = 0; -const TOKEN_SHM: usize = 1; - -fn main() -> Result<(), Box> { - match unsafe { libc::fork() } { - 0 => (), - -1 => return Err(Box::new(io::Error::last_os_error())), - _child => return Ok(()), +fn inner(daemon: redox_daemon::Daemon) -> Result<()> { + event::user_data! { + enum EventSource { + ChanSocket, + ShmSocket, + } } + let chan = ChanScheme::new()?; + let shm = ShmScheme::new()?; + daemon.ready().unwrap(); // Create event listener for both files - let mut event_file = File::open("event:")?; + let mut event_queue = EventQueue::::new()?; - let chan = ChanScheme::new()?; - event_file.write(&Event { - id: chan.socket.as_raw_fd() as usize, - flags: EVENT_READ, - data: TOKEN_CHAN - })?; - let shm = ShmScheme::new()?; - event_file.write(&Event { - id: shm.socket.as_raw_fd() as usize, - flags: EVENT_READ, - data: TOKEN_SHM - })?; + event_queue.subscribe(chan.socket.inner().raw(), EventSource::ChanSocket, EventFlags::READ)?; + event_queue.subscribe(shm.socket.inner().raw(), EventSource::ShmSocket, EventFlags::READ)?; let mut todo = VecDeque::with_capacity(16); - syscall::setrens(0, 0).map_err(from_syscall_error)?; + libredox::call::setrens(0, 0)?; let mut chan_opt = Some(chan); let mut shm_opt = Some(shm); while chan_opt.is_some() || shm_opt.is_some() { - let mut event = Event::default(); - event_file.read(&mut event)?; + let Some(event_res) = event_queue.next() else { + break; + }; + let event = event_res?; - match event.data { - TOKEN_CHAN => { - let mut error: Option = None; + match event.user_data { + EventSource::ChanSocket => { + let mut error: Option = None; let unmount = if let Some(ref mut chan) = chan_opt { let eof = loop { - let mut packet = Packet::default(); - match chan.socket.read(&mut packet) { - Ok(0) => break true, - Ok(_) => todo.push_front(packet), - Err(err) => if err.kind() == io::ErrorKind::WouldBlock { - break false; - } else { - return Err(Box::new(err)); - } - }; + match chan.socket.next_request(SignalBehavior::Restart) { + Ok(None) => break true, + Ok(Some(request)) => todo.push_front(Some(request)), + Err(Error { errno: EAGAIN | EWOULDBLOCK }) => break false, + Err(error) => return Err(error), + } }; // 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.socket.write(&packet) { - error = Some(err); + todo.retain_mut(|request_opt| { + match request_opt.take().unwrap().handle_scheme_block_mut(chan) { + Ok(res) => { + if let Err(err) = chan.socket.write_response(res, SignalBehavior::Restart) { + error = Some(err); + } + return false; + } + Err(req) => { + *request_opt = Some(req); + true } - return false; } - - true }); eof @@ -92,38 +88,29 @@ fn main() -> Result<(), Box> { false }; - if unmount { - if let Some(mut chan) = chan_opt.take() { - for mut packet in todo.drain(..) { - packet.a = Error::mux(Err(Error::new(error::ENODEV))); - if let Err(err) = chan.socket.write(&packet) { - error = Some(err); - } + if unmount && let Some(chan) = chan_opt.take() { + for req in todo.drain(..) { + if let Err(err) = chan.socket.write_response(Response::new(&req.unwrap(), Err(Error::new(ENODEV))), SignalBehavior::Restart) { + error = Some(err); } } } if let Some(err) = error { - return Err(Box::new(err)); + return Err(err); } }, - TOKEN_SHM => { + EventSource::ShmSocket => { let unmount = if let Some(ref mut shm) = shm_opt { let eof = loop { - let mut packet = Packet::default(); - match shm.socket.read(&mut packet) { - Ok(0) => break true, - Ok(_) => { - unsafe { - shm.handle(&mut packet); - } - shm.socket.write(&packet)?; + match shm.socket.next_request(SignalBehavior::Restart) { + Ok(None) => break true, + Ok(Some(request)) => { + let response = request.handle_scheme_mut(shm); + shm.socket.write_response(response, SignalBehavior::Restart)?; }, - Err(err) => if err.kind() == io::ErrorKind::WouldBlock { - break false; - } else { - return Err(Box::new(err)); - } + Err(Error { errno: EAGAIN | EWOULDBLOCK }) => break false, + Err(err) => return Err(err), }; }; @@ -135,21 +122,9 @@ fn main() -> Result<(), Box> { if unmount { shm_opt.take(); } - }, - _ => () + } } } Ok(()) } -fn post_fevent(file: &mut File, id: usize, flag: EventFlags) -> syscall::Result<()> { - file.write(&syscall::Packet { - a: syscall::SYS_FEVENT, - b: id, - c: flag.bits(), - d: 1, - ..Default::default() - }) - .map(|_| ()) - .map_err(|_| Error::new(error::EIO)) -} diff --git a/src/shm.rs b/src/shm.rs index db7c8a1ce7..732e519f7e 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -1,12 +1,10 @@ use std::{ cmp, collections::{HashMap, hash_map::Entry}, - fs::{File, OpenOptions}, - io, - os::unix::fs::OpenOptionsExt, rc::Rc, }; -use syscall::{error::*, flag::O_NONBLOCK, Error, Map, SchemeMut, Result, Packet, KSMSG_MMAP_PREP, MapFlags, KSMSG_MMAP, PAGE_SIZE, MAP_PRIVATE}; +use syscall::{error::*, Error, Map, Result, MapFlags, PAGE_SIZE, MAP_PRIVATE}; +use redox_scheme::SchemeMut; #[derive(Default)] pub struct ShmHandle { @@ -17,20 +15,15 @@ pub struct ShmScheme { maps: HashMap, ShmHandle>, handles: HashMap>, next_id: usize, - pub socket: File + pub socket: redox_scheme::Socket, } impl ShmScheme { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(Self { maps: HashMap::new(), handles: HashMap::new(), next_id: 0, - socket: OpenOptions::new() - .read(true) - .write(true) - .create(true) - .custom_flags(O_NONBLOCK as i32) - .open(":shm")? + socket: redox_scheme::Socket::nonblock("shm")?, }) } } @@ -75,7 +68,7 @@ impl SchemeMut for ShmScheme { } Ok(0) } - fn mmap_prep(&mut self, id: usize, offset: u64, size: usize, flags: MapFlags) -> Result { + fn mmap_prep(&mut self, id: usize, offset: u64, size: usize, _: MapFlags) -> Result { let path = self.handles.get(&id).ok_or(Error::new(EBADF))?; let total_size = offset as usize + size; match self.maps.get_mut(path).expect("handle pointing to nothing").buffer { From 92d45336ff5ea70c698541f89df1182f4a6023f1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 25 Dec 2023 11:37:02 +0100 Subject: [PATCH 52/57] Fix tests. --- .gitlab-ci.yml | 3 ++- examples/logserver.rs | 4 ++-- examples/shm.rs | 8 ++++++-- examples/shm_demo.rs | 8 ++++---- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ecd54e59f9..d019614988 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,4 +22,5 @@ test:redox: event main unnamed - #TODO: shm + shm_demo + # FIXME: shm is currently broken diff --git a/examples/logserver.rs b/examples/logserver.rs index 1a2159a519..a060ea30a4 100644 --- a/examples/logserver.rs +++ b/examples/logserver.rs @@ -31,7 +31,7 @@ fn main() -> io::Result<()> { event_file.read(&mut event)?; if event.data == 0 { - println!("Listener recevied flags: {}", event.flags); + println!("Listener recevied flags: {:?}", event.flags); if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE { loop { let stream = match syscall::dup(server.as_raw_fd() as usize, b"listen").map_err(from_syscall_error) { @@ -52,7 +52,7 @@ fn main() -> io::Result<()> { } } } else { - println!("Client #{} received flags: {}", event.data, event.flags); + println!("Client #{} received flags: {:?}", event.data, event.flags); let client = clients.get_mut(&event.data).unwrap(); if event.flags & syscall::EVENT_READ == syscall::EVENT_READ { diff --git a/examples/shm.rs b/examples/shm.rs index 55bbcdfb1a..51cd984716 100644 --- a/examples/shm.rs +++ b/examples/shm.rs @@ -17,17 +17,21 @@ fn main() -> Result<(), io::Error> { syscall::fmap(file1.as_raw_fd() as usize, &syscall::Map { offset: 0, size: 128, - flags: syscall::PROT_READ | syscall::PROT_WRITE + flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED, + address: 0, }).map_err(from_syscall_error)? as *mut u8, 128 ) }; + // FIXME: While the length can be unaligned, the offset cannot. This test is incorrectly + // written. let two = unsafe { slice::from_raw_parts_mut( syscall::fmap(file2.as_raw_fd() as usize, &syscall::Map { offset: 64, size: 64, - flags: syscall::PROT_READ | syscall::PROT_WRITE + flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED, + address: 0, }).map_err(from_syscall_error)? as *mut u8, 64 ) diff --git a/examples/shm_demo.rs b/examples/shm_demo.rs index 369a7ffbde..b3f47515c4 100644 --- a/examples/shm_demo.rs +++ b/examples/shm_demo.rs @@ -16,15 +16,15 @@ fn main() -> Result<(), io::Error> { let counter = unsafe { &mut *(syscall::fmap(file.as_raw_fd() as usize, &syscall::Map { offset: 0, + address: 0, size: mem::size_of::(), - flags: syscall::PROT_READ | syscall::PROT_WRITE + flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED, }).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)); - } + thread::sleep(Duration::from_secs(1)); + Ok(()) } From 55cfb330d272d78f9d07a87423c7feed02ded422 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 8 Mar 2024 12:22:57 +0100 Subject: [PATCH 53/57] Implement scheme cancellation. --- Cargo.lock | 60 ++++++++++++++++++++--------------------------------- Cargo.toml | 8 +++---- src/main.rs | 54 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 67 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7b714e559..1cbc2fced3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,21 +4,15 @@ version = 3 [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "ipcd" version = "0.1.0" dependencies = [ - "libredox 0.0.3", + "libredox", "redox-daemon", "redox-scheme", "redox_event", @@ -27,67 +21,57 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "309ba69985d5170852ebbe8fbb33850d20794d0aed5b210dfea2db798df64e10" dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall", -] - -[[package]] -name = "libredox" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962b650e6b934f95cec493e1464e0f5cba98dd9ba9ef2b1c42cdfdb7864cab5a" -dependencies = [ - "bitflags 2.4.1", + "bitflags", "libc", "redox_syscall", ] [[package]] name = "redox-daemon" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353178b5771a301b9d2b318914ab5dfeb21dd6e65930faedde19248cfceca5a7" +checksum = "7fbdeffdb03cf2961b211a2023e683d71f2c225ea93404da5dc34b0dc94f0341" dependencies = [ "libc", - "libredox 0.0.1", + "libredox", ] [[package]] name = "redox-scheme" -version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#265e056726066f319e47eedd5f20f9549fb4c581" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a4d4161697395d7cc9b182a14a753b2ba0243dcf622e278e644c6e7f74904c1" dependencies = [ - "libredox 0.0.3", + "libredox", "redox_syscall", ] [[package]] name = "redox_event" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b78fedc17aea72caee38ac1208e0f709fa1641af1bbe0205e7d434413a60621d" +checksum = "0fe498288c246de6b2df9be919cffe7d144480b2f1829f3fa987aa3ee067a04a" dependencies = [ - "bitflags 2.4.1", - "libredox 0.0.1", + "bitflags", + "libredox", "redox_syscall", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "13c178f952cc7eac391f3124bd9851d1ac0bdbc4c9de2d892ccd5f0d8b160e96" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 1cec1ae4ac..ae758ca5fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ name = "ipcd" version = "0.1.0" [dependencies] -libredox = "0.0.3" +libredox = "0.1.2" redox-daemon = "0.1.2" -redox_syscall = "0.4.1" -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } -redox_event = "0.2.0" +redox_syscall = "0.5" +redox-scheme = "0.2.0" +redox_event = "0.4.0" diff --git a/src/main.rs b/src/main.rs index d91c06f750..d3c767bb45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,8 @@ use std::collections::VecDeque; use event::{EventQueue, EventFlags}; -use redox_scheme::{SignalBehavior, Response}; -use syscall::{Error, Result, EAGAIN, EWOULDBLOCK, ENODEV}; +use redox_scheme::{CallRequest, RequestKind, Response, SignalBehavior}; +use syscall::{Error, Result, EAGAIN, EWOULDBLOCK, ENODEV, EINTR, KSMSG_CANCEL}; mod chan; mod shm; @@ -41,7 +41,11 @@ fn inner(daemon: redox_daemon::Daemon) -> Result<()> { event_queue.subscribe(chan.socket.inner().raw(), EventSource::ChanSocket, EventFlags::READ)?; event_queue.subscribe(shm.socket.inner().raw(), EventSource::ShmSocket, EventFlags::READ)?; - let mut todo = VecDeque::with_capacity(16); + struct Todo { + req: Option, + canceling: bool, + } + let mut todo = VecDeque::::with_capacity(16); libredox::call::setrens(0, 0)?; @@ -61,23 +65,39 @@ fn inner(daemon: redox_daemon::Daemon) -> Result<()> { let eof = loop { match chan.socket.next_request(SignalBehavior::Restart) { Ok(None) => break true, - Ok(Some(request)) => todo.push_front(Some(request)), + Ok(Some(request)) => match request.kind() { + RequestKind::Call(request) => todo.push_front(Todo { req: Some(request), canceling: false }), + RequestKind::Cancellation(request) => { + if let Some(affected_packet) = todo.iter_mut().find(|t| t.req.as_ref().map_or(false, |r| r.request().request_id() == request.id)) { + affected_packet.canceling = true; + } + } + _ => (), + } Err(Error { errno: EAGAIN | EWOULDBLOCK }) => break false, Err(error) => return Err(error), } }; // Process queue, delete finished items - todo.retain_mut(|request_opt| { - match request_opt.take().unwrap().handle_scheme_block_mut(chan) { + todo.retain_mut(|slot| { + let req = slot.req.take().unwrap(); + + match req.handle_scheme_block_mut(chan) { Ok(res) => { if let Err(err) = chan.socket.write_response(res, SignalBehavior::Restart) { error = Some(err); } - return false; + false + } + Err(req) if slot.canceling => { + if let Err(err) = chan.socket.write_response(Response::new(&req, Err(Error::new(EINTR))), SignalBehavior::Restart) { + error = Some(err); + } + false } Err(req) => { - *request_opt = Some(req); + slot.req = Some(req); true } } @@ -89,8 +109,13 @@ fn inner(daemon: redox_daemon::Daemon) -> Result<()> { }; if unmount && let Some(chan) = chan_opt.take() { - for req in todo.drain(..) { - if let Err(err) = chan.socket.write_response(Response::new(&req.unwrap(), Err(Error::new(ENODEV))), SignalBehavior::Restart) { + for slot in todo.drain(..) { + let res = if slot.canceling { + Err(Error::new(EINTR)) + } else { + Err(Error::new(ENODEV)) + }; + if let Err(err) = chan.socket.write_response(Response::new(&slot.req.unwrap(), res), SignalBehavior::Restart) { error = Some(err); } } @@ -105,9 +130,12 @@ fn inner(daemon: redox_daemon::Daemon) -> Result<()> { let eof = loop { match shm.socket.next_request(SignalBehavior::Restart) { Ok(None) => break true, - Ok(Some(request)) => { - let response = request.handle_scheme_mut(shm); - shm.socket.write_response(response, SignalBehavior::Restart)?; + Ok(Some(request)) => match request.kind() { + RequestKind::Call(request) => { + let response = request.handle_scheme_mut(shm); + shm.socket.write_response(response, SignalBehavior::Restart)?; + } + _ => (), }, Err(Error { errno: EAGAIN | EWOULDBLOCK }) => break false, Err(err) => return Err(err), From fa97ec8a3bb8c5ed970fcab907af89150e635a41 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:45:08 +0200 Subject: [PATCH 54/57] Update redox_event This remove all usages of the legacy scheme syntax --- Cargo.lock | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1cbc2fced3..86d3255d5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,13 +58,12 @@ dependencies = [ [[package]] name = "redox_event" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe498288c246de6b2df9be919cffe7d144480b2f1829f3fa987aa3ee067a04a" +checksum = "69609faa5d5992247a4ef379917bb3e39be281405d6a0ccd4f942429400b956f" dependencies = [ "bitflags", "libredox", - "redox_syscall", ] [[package]] From db2322c95c8a86ca5104d2438fb16a048c2078f6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 7 Sep 2024 02:01:53 +0200 Subject: [PATCH 55/57] V2 scheme. --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 4 ++-- src/chan.rs | 12 ++++++------ src/main.rs | 8 ++++---- src/shm.rs | 4 ++-- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86d3255d5d..2fc04f2524 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "ipcd" @@ -21,15 +21,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libredox" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309ba69985d5170852ebbe8fbb33850d20794d0aed5b210dfea2db798df64e10" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags", "libc", @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a4d4161697395d7cc9b182a14a753b2ba0243dcf622e278e644c6e7f74904c1" +checksum = "2148c23b7ed5bf1cc3919b4830e4bf2a674a5d3691c619317624ccd78d13a3ac" dependencies = [ "libredox", "redox_syscall", @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c178f952cc7eac391f3124bd9851d1ac0bdbc4c9de2d892ccd5f0d8b160e96" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index ae758ca5fe..89199359fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ name = "ipcd" version = "0.1.0" [dependencies] -libredox = "0.1.2" +libredox = "0.1.3" redox-daemon = "0.1.2" redox_syscall = "0.5" -redox-scheme = "0.2.0" +redox-scheme = "0.2.1" redox_event = "0.4.0" diff --git a/src/chan.rs b/src/chan.rs index 13a79dcc91..087bbca90a 100644 --- a/src/chan.rs +++ b/src/chan.rs @@ -3,7 +3,7 @@ use std::{ collections::{HashMap, VecDeque}, }; use syscall::{flag::*, error::*, Error}; -use redox_scheme::SchemeBlockMut; +use redox_scheme::{SchemeBlockMut, V2}; #[derive(Debug, Default)] pub struct Client { @@ -100,7 +100,7 @@ impl ChanScheme { handles: HashMap::new(), listeners: HashMap::new(), next_id: 0, - socket: redox_scheme::Socket::nonblock("chan")?, + socket: redox_scheme::Socket::::nonblock("chan")?, }) } } @@ -228,7 +228,7 @@ impl SchemeBlockMut for ChanScheme { // | | |_| | | (_> < | |___| |__| |_| |___) | |___ // |___\___/ \___/\/ \____|_____\___/|____/|_____| - fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + fn write(&mut self, id: usize, buf: &[u8], _offset: u64, flags: u32) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; let client = handle.require_client()?; @@ -248,7 +248,7 @@ impl SchemeBlockMut for ChanScheme { } } else if client.remote == Connection::Closed { Err(Error::new(EPIPE)) - } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + } else if (flags as usize) & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) } else { Ok(None) @@ -276,7 +276,7 @@ impl SchemeBlockMut for ChanScheme { .ok_or(Error::new(EBADF)) .and(Ok(Some(id))) } - fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { + fn read(&mut self, id: usize, buf: &mut [u8], _offset: u64, flags: u32) -> Result> { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; let client = handle.require_client()?; @@ -288,7 +288,7 @@ impl SchemeBlockMut for ChanScheme { } else if client.remote == Connection::Closed { // Remote dropped, send EOF Ok(Some(0)) - } else if handle.flags & O_NONBLOCK == O_NONBLOCK { + } else if (flags as usize) & O_NONBLOCK == O_NONBLOCK { Err(Error::new(EAGAIN)) } else { Ok(None) diff --git a/src/main.rs b/src/main.rs index d3c767bb45..710a784439 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use event::{EventQueue, EventFlags}; use redox_scheme::{CallRequest, RequestKind, Response, SignalBehavior}; -use syscall::{Error, Result, EAGAIN, EWOULDBLOCK, ENODEV, EINTR, KSMSG_CANCEL}; +use syscall::{Error, Result, EAGAIN, EWOULDBLOCK, ENODEV, EINTR}; mod chan; mod shm; @@ -84,19 +84,19 @@ fn inner(daemon: redox_daemon::Daemon) -> Result<()> { let req = slot.req.take().unwrap(); match req.handle_scheme_block_mut(chan) { - Ok(res) => { + Some(res) => { if let Err(err) = chan.socket.write_response(res, SignalBehavior::Restart) { error = Some(err); } false } - Err(req) if slot.canceling => { + None if slot.canceling => { if let Err(err) = chan.socket.write_response(Response::new(&req, Err(Error::new(EINTR))), SignalBehavior::Restart) { error = Some(err); } false } - Err(req) => { + None => { slot.req = Some(req); true } diff --git a/src/shm.rs b/src/shm.rs index 732e519f7e..ac2b87ff0b 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -4,7 +4,7 @@ use std::{ rc::Rc, }; use syscall::{error::*, Error, Map, Result, MapFlags, PAGE_SIZE, MAP_PRIVATE}; -use redox_scheme::SchemeMut; +use redox_scheme::{SchemeMut, V2}; #[derive(Default)] pub struct ShmHandle { @@ -23,7 +23,7 @@ impl ShmScheme { maps: HashMap::new(), handles: HashMap::new(), next_id: 0, - socket: redox_scheme::Socket::nonblock("shm")?, + socket: redox_scheme::Socket::::nonblock("shm")?, }) } } From bbd282f480c72ea81d068d4a9bc85635bab90d39 Mon Sep 17 00:00:00 2001 From: Andrey Turkin Date: Thu, 17 Oct 2024 09:07:44 +0300 Subject: [PATCH 56/57] Bump dependencies --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fc04f2524..bcf353548b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" [[package]] name = "libredox" @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2148c23b7ed5bf1cc3919b4830e4bf2a674a5d3691c619317624ccd78d13a3ac" +checksum = "95624e20d2c1808f7ca0820720d30aad9f5d2fc404e1ef379431ad7a790c3f7e" dependencies = [ "libredox", "redox_syscall", @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] From e6f672bd48f9ac65a194f804bd0ed64c307f7b67 Mon Sep 17 00:00:00 2001 From: Ribbon Date: Thu, 9 Jan 2025 12:05:13 +0000 Subject: [PATCH 57/57] Document how to contribute and do development in the README --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 553034bdf4..8f726664b9 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,19 @@ Simply open `chan:` with O_CREAT where `` is any name you'd like to This listener can accept clients by calling `dup("listen")`. Open `chan:` without O_CREAT to connect. Now you can read and write between both streams. + +## How To Contribute + +To learn how to contribute to this system component you need to read the following document: + +- [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md) + +## Development + +To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages. + +### How To Build + +To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. + +This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux.