From cb642ff75eb1fdfa5ec0d944ef4a148eed54f761 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Thu, 18 Apr 2019 15:44:17 +0200 Subject: Fixup async event infrastructure + allow receiving log events --- examples/cli.rs | 23 ++++++++++++++++++++-- src/err.rs | 2 ++ src/sock.rs | 61 ++++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/examples/cli.rs b/examples/cli.rs index 39c4b0f..2995bac 100644 --- a/examples/cli.rs +++ b/examples/cli.rs @@ -1,8 +1,27 @@ +use std::sync::Arc; + fn main() { - let s = torctl::spawn("/usr/bin/tor", "tor-data", "").unwrap(); + let s = Arc::new( torctl::spawn("/usr/bin/tor", "tor-data", "").unwrap() ); + + let slisten = s.clone(); + std::thread::spawn(move || { + loop { + match slisten.read_event() { + Ok(ev) => println!("Event: {:?}", ev), + Err(e) => { + println!("Error reading event: {}", e); + break; + }, + } + } + }); + s.setevents(&["NOTICE", "INFO", "WARN", "ERR"]).unwrap(); + s.setconf(&[("SocksPort", Some("10245"))]).unwrap(); dbg!(s.getconf(&["ContactInfo", "DataDirectory", "HiddenServiceOptions"]).unwrap()); - dbg!(s.getinfo(&["version", "config-text", "md/all"]).unwrap()); + dbg!(s.getinfo(&["version", "config-text"]).unwrap()); + + std::thread::sleep(std::time::Duration::from_secs(5)); s.quit().unwrap(); std::thread::sleep(std::time::Duration::from_secs(500)); diff --git a/src/err.rs b/src/err.rs index aa39ede..473dd2f 100644 --- a/src/err.rs +++ b/src/err.rs @@ -14,6 +14,7 @@ pub(crate) enum ErrorImpl { ProcTimeout, Status(crate::sock::ReplyLine), Parse, // This could be more specific + UnknownEvent(String), Keyword(String), } @@ -26,6 +27,7 @@ impl std::fmt::Display for Error { ProcTimeout => write!(f, "Timeout while waiting for Tor process to start"), Status(s) => write!(f, "Unexpected reply ({})", s), Parse => write!(f, "Error parsing reply"), + UnknownEvent(s) => write!(f, "Unknown event: {}", s), Keyword(s) => write!(f, "Invalid keyword '{}'", s), } } diff --git a/src/sock.rs b/src/sock.rs index 8102ebc..1724509 100644 --- a/src/sock.rs +++ b/src/sock.rs @@ -123,6 +123,16 @@ pub enum Auth { } +#[derive(Debug)] +pub enum Event { + // Log messages + Debug(String), + Info(String), + Notice(String), + Warn(String), + Err(String), +} + // A double-quoted string where only \ and " are escaped. @@ -356,8 +366,6 @@ impl Sock { // XXX: This IntoIterator works with &[..] and &vec![..]. Haven't tested HashMap/BTreeMap yet, // but I suspect their signature doesn't match. - // Warning: There's no validation on the key string format, so this command allows protocol - // injection. fn setresetconf<'a,T>(&self, mut msg: String, settings: T) -> Result<()> where T: IntoIterator)>, { @@ -451,24 +459,45 @@ impl Sock { Ok(res) } - pub fn events(&'a self, list: ..) -> Result> { + // TODO: Create an enum for supported event types, rather than this string thing. We don't + // support reading all types of events anyway. + pub fn setevents<'a,T: IntoIterator>(&self, events: T) -> Result<()> { + let mut msg = "SETEVENTS".to_string(); + for e in events { + is_keyword(e)?; + msg.push(' '); + msg.push_str(e); + } + msg.push_str("\r\n"); + self.cmd(msg).map(|_|()) } -} + /// Read an event from the socket. This method blocks until an event has been received. + pub fn read_event(&self) -> Result { + let mut ret = self.get_reply(true)?; + let ev = ret.remove(0); -pub struct Events<'a> { - sock: &'a Sock -} - -impl<'a> Drop for Events<'a> { - fn drop(&mut self) { - self.sock.cmd("SETEVENTS\r\n").is_ok(); - } -} + fn logmsg(r: ReplyLine, skip: usize) -> String { + if r.data.is_empty() { + (&r.text[skip..]).trim().to_owned() + } else { + String::from_utf8_lossy(&r.data).trim().to_owned() + } + } -impl<'a> Iterator for Events<'a> { - type Item = Result; - fn next(&mut self) -> Option { + if ev.status == 650 && ev.text.starts_with("DEBUG") { + Ok(Event::Debug(logmsg(ev, 6))) + } else if ev.status == 650 && ev.text.starts_with("NOTICE") { + Ok(Event::Notice(logmsg(ev, 7))) + } else if ev.status == 650 && ev.text.starts_with("INFO") { + Ok(Event::Info(logmsg(ev, 5))) + } else if ev.status == 650 && ev.text.starts_with("WARN") { + Ok(Event::Warn(logmsg(ev, 5))) + } else if ev.status == 650 && ev.text.starts_with("ERR") { + Ok(Event::Err(logmsg(ev, 4))) + } else { + Err(err!(UnknownEvent, ev.text)) + } } } -- cgit v1.2.3