From 2f061b5aa4701ed660411e32976f61868af47982 Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Mon, 11 May 2020 08:30:58 +0300 Subject: [PATCH 01/15] Implement GetPlatformInfo action --- Cargo.toml | 1 + src/action/platform_info.rs | 106 ++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/action/platform_info.rs diff --git a/Cargo.toml b/Cargo.toml index 5513ba1d..cf4e57d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ rrg-proto = { path = "proto/" } simplelog = { version = "0.7.6" } structopt = { version = "0.3.12" } sys-info = { version = "0.5.1" } +libc = { version = "0.2" } diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs new file mode 100644 index 00000000..19d40fed --- /dev/null +++ b/src/action/platform_info.rs @@ -0,0 +1,106 @@ +// Copyright 2020 Google LLC +// +// Use of this source code is governed by an MIT-style license that can be found +// in the LICENSE file or at https://opensource.org/licenses/MIT. + +extern crate sys_info; +extern crate libc; +// extern crate sysinfo; + +use sys_info::{linux_os_release, os_type, hostname}; +use libc::{uname, utsname, c_char}; +use std::ffi::CStr; +use std::option::Option; +use crate::session::{self, Session}; + +use rrg_proto::Uname; + +#[derive(Default)] +pub struct PlatformInfo { + system: Option, + release_name: Option, + version_id: Option, + machine: Option, + kernel_release: Option, + fqdn: Option, + architecture: Option, + node: Option +} + +pub struct Response { + platform_information: PlatformInfo, +} + +#[inline(always)] +fn convert_raw_string(c_string: &[c_char]) -> String { + unsafe { + String::from(CStr::from_ptr(c_string.as_ptr()).to_string_lossy().into_owned()) + } +} + +pub fn handle(session:&mut S, _: ()) -> session::Result<()> { + let os_type = os_type().expect("Can't get os type"); + match os_type.as_str() { + "Linux" => { + let linux_release_info = linux_os_release() + .expect("Can't get info about linux system"); + let mut system_info: utsname; + + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } + + session.reply(Response { + platform_information: PlatformInfo { + system: Some(os_type.to_string()), + release_name: linux_release_info.name, + version_id: linux_release_info.version_id, + machine: Some(convert_raw_string(&system_info.machine)), + kernel_release: Some(convert_raw_string(&system_info.release)), + fqdn: hostname().ok(), + architecture: Some(convert_raw_string(&system_info.machine)), + node: Some(convert_raw_string(&system_info.nodename)) + }, + })?; + }, + + _ => { + session.reply(Response { + platform_information: PlatformInfo { + system: Some(os_type.to_string()), + fqdn: hostname().ok(), + ..Default::default() // TODO: Add other systems + } + })?; + } + } + + Ok(()) +} + +impl super::Response for Response { + const RDF_NAME: Option<&'static str> = Some("Uname"); + + type Proto = Uname; + + fn into_proto(self) -> Uname { + Uname { + system: self.platform_information.system.clone(), + release: self.platform_information.release_name.clone(), + version: self.platform_information.version_id, + machine: self.platform_information.machine, + kernel: self.platform_information.kernel_release, + fqdn: self.platform_information.fqdn, + architecture: self.platform_information.architecture.clone(), + node: self.platform_information.node, + pep425tag: Some( + format!("{}_{}_{}", + self.platform_information.system.unwrap_or(String::from("")), + self.platform_information.release_name.unwrap_or(String::from("")), + self.platform_information.architecture.unwrap_or(String::from("")) + )), + ..Default::default() + } + } +} From 9ff796250309d3ff29aaeeb11a1e639f3e4f9ecd Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Mon, 11 May 2020 08:31:20 +0300 Subject: [PATCH 02/15] Integrate action to mod.rs --- src/action/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/action/mod.rs b/src/action/mod.rs index 66d12d3b..c4c2b0db 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -5,6 +5,7 @@ pub mod metadata; pub mod startup; +pub mod platform_info; use crate::session::{self, Session, Task}; @@ -51,6 +52,7 @@ where match action { "SendStartupInfo" => task.execute(self::startup::handle), "GetClientInfo" => task.execute(self::metadata::handle), + "GetPlatformInfo" => task.execute(self::platform_info::handle), action => return Err(session::Error::Dispatch(String::from(action))), } } From b4e0ecbbea7d0c07d038d857071a815148665fca Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Mon, 11 May 2020 08:37:19 +0300 Subject: [PATCH 03/15] Merge with upstream --- src/action/metadata.rs | 39 ++++++ src/action/startup.rs | 33 +++++ src/session/mod.rs | 284 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 353 insertions(+), 3 deletions(-) diff --git a/src/action/metadata.rs b/src/action/metadata.rs index b1a46735..ce7eae4d 100644 --- a/src/action/metadata.rs +++ b/src/action/metadata.rs @@ -36,3 +36,42 @@ impl super::Response for Response { self.metadata.into() } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_name() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + + let metadata = &session.reply::(0).metadata; + assert_eq!(metadata.name, "rrg"); + } + + #[test] + fn test_description() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + + let metadata = &session.reply::(0).metadata; + assert!(!metadata.description.is_empty()); + } + + #[test] + fn test_version() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + + let metadata = &session.reply::(0).metadata; + assert!(metadata.version.as_numeric() > 0); + } +} diff --git a/src/action/startup.rs b/src/action/startup.rs index 246ec9e2..2635ff42 100644 --- a/src/action/startup.rs +++ b/src/action/startup.rs @@ -18,6 +18,7 @@ use log::error; use crate::metadata::{Metadata}; use crate::session::{self, Session}; +/// An error type for failures that can occur when collecting startup data. #[derive(Debug)] struct Error { boot_time_error: sys_info::Error, @@ -101,3 +102,35 @@ impl super::Response for Response { } } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_boot_time() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 0); + assert_eq!(session.response_count(session::Sink::STARTUP), 1); + + let response = session.response::(session::Sink::STARTUP, 0); + assert!(response.boot_time > std::time::UNIX_EPOCH); + assert!(response.boot_time < std::time::SystemTime::now()); + } + + #[test] + fn test_metadata() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 0); + assert_eq!(session.response_count(session::Sink::STARTUP), 1); + + let response = session.response::(session::Sink::STARTUP, 0); + assert!(response.metadata.version.as_numeric() > 0); + assert_eq!(response.metadata.name, "rrg"); + } +} diff --git a/src/session/mod.rs b/src/session/mod.rs index 2ab65f72..96d4d3c2 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -26,7 +26,7 @@ use log::{error, info}; use crate::action; use crate::message; pub use self::demand::{Demand, Header, Payload}; -pub use self::error::{Error, ParseError}; +pub use self::error::{Error, ParseError, MissingFieldError}; use self::response::{Response, Status}; pub use self::sink::{Sink}; @@ -118,11 +118,16 @@ where pub trait Session { /// Sends a reply to the flow that call the action. fn reply(&mut self, response: R) -> Result<()> - where R: action::Response; + where R: action::Response + 'static; /// Sends a message to a particular sink. fn send(&mut self, sink: Sink, response: R) -> Result<()> - where R: action::Response; + where R: action::Response + 'static; + + /// Sends a heartbeat signal to the Fleetspeak process. + fn heartbeat(&mut self) { + // TODO: Create a real implementation. + } } /// A session type for unrequested action executions. @@ -243,3 +248,276 @@ where Ok(()) } + +#[cfg(test)] +pub mod test { + use std::any::Any; + use std::collections::HashMap; + + use super::*; + + /// A session type intended to be used in tests. + /// + /// Testing actions with normal session objects can be quite hard, since + /// they communicate with the outside world (through Fleetspeak). Since we + /// want to keep the tests minimal and not waste resources on unneeded I/O, + /// using real sessions is not an option. + /// + /// Instead, one can use a `Fake` session. It simply accumulates responses + /// that the action sends and lets the creator inspect them later. + pub struct Fake { + replies: Vec>, + responses: HashMap>>, + } + + impl Fake { + + /// Constructs a new fake session. + pub fn new() -> Fake { + Fake { + replies: Vec::new(), + responses: std::collections::HashMap::new(), + } + } + + /// Yields the number of replies that this session sent so far. + pub fn reply_count(&self) -> usize { + self.replies.len() + } + + /// Retrieves a reply corresponding to the given id. + /// + /// The identifier corresponding to the first response is 0, the second + /// one is 1 and so on. + /// + /// This method will panic if a reply with the specified `id` does not + /// exist or if it exists but has a wrong type. + pub fn reply(&self, id: usize) -> &R + where + R: action::Response + 'static, + { + let reply = match self.replies.get(id) { + Some(reply) => reply, + None => panic!("no reply #{}", id), + }; + + reply.downcast_ref().expect("unexpected reply type") + } + + /// Yields the number of responses sent so far to the specified sink. + pub fn response_count(&self, sink: Sink) -> usize { + match self.responses.get(&sink) { + Some(responses) => responses.len(), + None => 0, + } + } + + /// Retrieves a response with the given id sent to a particular sink. + /// + /// The identifier corresponding to the first response to the particular + /// sink is 0, to the second one (to the same sink) is 1 and so on. + /// + /// This method will panic if a reply with the specified `id` to the + /// given `sink` does not exist or if it exists but has wrong type. + pub fn response(&self, sink: Sink, id: usize) -> &R + where + R: action::Response + 'static, + { + let responses = match self.responses.get(&sink) { + Some(responses) => responses, + None => panic!("no responses for sink '{:?}'", sink), + }; + + let response = match responses.get(id) { + Some(response) => response, + None => panic!("no response #{} for sink '{:?}'", id, sink), + }; + + match response.downcast_ref() { + Some(response) => response, + None => panic!("unexpected response type in sink '{:?}'", sink), + } + } + } + + impl Session for Fake { + + fn reply(&mut self, response: R) -> Result<()> + where + R: action::Response + 'static, + { + self.replies.push(Box::new(response)); + + Ok(()) + } + + fn send(&mut self, sink: Sink, response: R) -> Result<()> + where + R: action::Response + 'static, + { + let responses = self.responses.entry(sink).or_insert_with(Vec::new); + responses.push(Box::new(response)); + + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_fake_reply_count() { + + fn handle(session: &mut S, _: ()) { + session.reply(()).unwrap(); + session.reply(()).unwrap(); + session.reply(()).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + assert_eq!(session.reply_count(), 3); + } + + #[test] + fn test_fake_response_count() { + + // TODO: Extend this test with more sinks (once we have some more sinks + // defined). + + fn handle(session: &mut S, _: ()) { + session.send(Sink::STARTUP, ()).unwrap(); + session.send(Sink::STARTUP, ()).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + assert_eq!(session.response_count(Sink::STARTUP), 2); + } + + #[test] + fn test_fake_reply_correct_response() { + + fn handle(session: &mut S, _: ()) { + session.reply(StringResponse::from("foo")).unwrap(); + session.reply(StringResponse::from("bar")).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + assert_eq!(session.reply::(0).0, "foo"); + assert_eq!(session.reply::(1).0, "bar"); + } + + #[test] + #[should_panic(expected = "no reply #0")] + fn test_fake_reply_incorrect_response_id() { + + fn handle(_: &mut S, _: ()) { + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + session.reply::<()>(0); + } + + #[test] + #[should_panic(expected = "unexpected reply type")] + fn test_fake_reply_incorrect_response_type() { + + fn handle(session: &mut S, _: ()) { + session.reply(StringResponse::from("quux")).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + session.reply::<()>(0); + } + + #[test] + fn test_fake_response_correct_response() { + + fn handle(session: &mut S, _: ()) { + session.send(Sink::STARTUP, StringResponse::from("foo")).unwrap(); + session.send(Sink::STARTUP, StringResponse::from("bar")).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + let response_foo = session.response::(Sink::STARTUP, 0); + let response_bar = session.response::(Sink::STARTUP, 1); + assert_eq!(response_foo.0, "foo"); + assert_eq!(response_bar.0, "bar"); + } + + #[test] + #[should_panic(expected = "no responses")] + fn test_fake_response_empty_sink() { + + fn handle(_: &mut S, _: ()) { + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + session.response::<()>(Sink::STARTUP, 0); + } + + #[test] + #[should_panic(expected = "no response #42")] + fn test_fake_response_incorrect_response_id() { + + fn handle(session: &mut S, _: ()) { + session.send(Sink::STARTUP, ()).unwrap(); + session.send(Sink::STARTUP, ()).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + session.response::<()>(Sink::STARTUP, 42); + } + + #[test] + #[should_panic(expected = "unexpected response type")] + fn test_fake_response_incorrect_response_type() { + + fn handle(session: &mut S, _: ()) { + session.send(Sink::STARTUP, StringResponse::from("quux")).unwrap(); + } + + let mut session = test::Fake::new(); + handle(&mut session, ()); + + session.response::<()>(Sink::STARTUP, 0); + } + + struct StringResponse(String); + + impl> From for StringResponse { + + fn from(string: S) -> StringResponse { + StringResponse(string.into()) + } + } + + impl action::Response for StringResponse { + + const RDF_NAME: Option<&'static str> = Some("RDFString"); + + type Proto = String; + + fn into_proto(self) -> String { + self.0 + } + } +} From bc441d62f06e3b6844a5b190eea6a7efb70bf6ea Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Mon, 11 May 2020 08:45:21 +0300 Subject: [PATCH 04/15] Finish merge --- src/action/mod.rs | 50 +++++++++++++++++++++++++++++++--- src/session/demand.rs | 10 +++---- src/session/error.rs | 62 +++++++++++++++++++++++++++++++++++++++---- src/session/sink.rs | 2 +- 4 files changed, 109 insertions(+), 15 deletions(-) diff --git a/src/action/mod.rs b/src/action/mod.rs index c4c2b0db..699a4e61 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -3,20 +3,61 @@ // Use of this source code is governed by an MIT-style license that can be found // in the LICENSE file or at https://opensource.org/licenses/MIT. +//! Handlers and types for agent's actions. +//! +//! The basic functionality that a GRR agent exposes is called an _action_. +//! Actions are invoked by the server (when running a _flow_), should gather +//! requested information and report back to the server. +//! +//! In RRG each action consists of three components: a request type, a response +//! type and an action handler. Request and response types wrap lower-level +//! Protocol Buffer messages sent by and to the GRR server. Handlers accept one +//! instance of the corresponding request type and send some (zero or more) +//! instances of the corresponding response type. + pub mod metadata; pub mod startup; pub mod platform_info; use crate::session::{self, Session, Task}; -pub trait Request { +/// Abstraction for action-specific requests. +/// +/// Protocol Buffer messages received from the GRR server are not necessarily +/// easy to work with and are hardly idiomatic to Rust. For this reason, actions +/// should define more structured data types to represent their input and should +/// be able to parse raw messages into them. +pub trait Request: Sized { + + /// A type of the corresponding raw proto message. type Proto: prost::Message + Default; - fn from_proto(proto: Self::Proto) -> Self; + + /// A method for converting raw proto messages into structured requests. + fn from_proto(proto: Self::Proto) -> Result; } -pub trait Response { +/// Abstraction for action-specific responses. +/// +/// Like with the [`Request`] type, Protocol Buffer messages sent to the GRR +/// server are very idiomatic to Rust. For this reason, actions should define +/// more structured data types to represent responses and provide a way to +/// convert them into the wire format. +/// +/// Note that because of the design flaws in the protocol, actions also need to +/// specify a name of the wrapper RDF class from the Python implementation. +/// Hopefully, one day this issue would be fixed and class names will not leak +/// into the specification. +/// +/// [`Request`]: trait.Request.html +pub trait Response: Sized { + + /// A name of the corresponding RDF class. const RDF_NAME: Option<&'static str>; + + /// A type of the corresponding raw proto message. type Proto: prost::Message + Default; + + /// A method for converting structured responses into raw proto messages. fn into_proto(self) -> Self::Proto; } @@ -24,7 +65,8 @@ impl Request for () { type Proto = (); - fn from_proto(_: ()) { + fn from_proto(unit: ()) -> Result<(), session::ParseError> { + Ok(unit) } } diff --git a/src/session/demand.rs b/src/session/demand.rs index d013820b..7c0cafd2 100644 --- a/src/session/demand.rs +++ b/src/session/demand.rs @@ -60,7 +60,7 @@ impl Payload { None => Default::default(), }; - Ok(R::from_proto(proto)) + R::from_proto(proto) } } @@ -69,15 +69,15 @@ impl TryFrom for Demand { type Error = session::ParseError; fn try_from(message: rrg_proto::GrrMessage) -> Result { - use session::ParseError::*; + let missing = session::MissingFieldError::new; let header = Header { - session_id: message.session_id.ok_or(MissingField("session id"))?, - request_id: message.request_id.ok_or(MissingField("request id"))?, + session_id: message.session_id.ok_or(missing("session id"))?, + request_id: message.request_id.ok_or(missing("request id"))?, }; Ok(Demand { - action: message.name.ok_or(MissingField("action name"))?, + action: message.name.ok_or(missing("action name"))?, header: header, payload: Payload { data: message.args, diff --git a/src/session/error.rs b/src/session/error.rs index bc0a4291..56b15026 100644 --- a/src/session/error.rs +++ b/src/session/error.rs @@ -87,20 +87,34 @@ impl From for Error { /// An error type for failures that can occur when parsing proto messages. #[derive(Debug)] pub enum ParseError { - /// A required field of a proto message is missing. - MissingField(&'static str), + /// An error occurred because the decoded proto message was malformed. + Malformed(Box), /// An error occurred when decoding bytes of a proto message. Decode(prost::DecodeError), } +impl ParseError { + + /// Converts a detailed error indicating a malformed proto to `ParseError`. + /// + /// This is just a convenience function for lifting custom error types that + /// contain more specific information to generic `ParseError`. + pub fn malformed(error: E) -> ParseError + where + E: Into>, + { + ParseError::Malformed(error.into()) + } +} + impl Display for ParseError { fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result { use ParseError::*; match *self { - MissingField(name) => { - write!(fmt, "required field is missing: {}", name) + Malformed(ref error) => { + write!(fmt, "invalid proto message: {}", error) } Decode(ref error) => { write!(fmt, "failed to decode proto message: {}", error) @@ -115,7 +129,7 @@ impl std::error::Error for ParseError { use ParseError::*; match *self { - MissingField(_) => None, + Malformed(ref error) => Some(error.as_ref()), Decode(ref error) => Some(error), } } @@ -127,3 +141,41 @@ impl From for ParseError { ParseError::Decode(error) } } + +/// An error type for situations where required proto field is missing. +#[derive(Debug)] +pub struct MissingFieldError { + /// A name of the missing field. + name: &'static str, +} + +impl MissingFieldError { + + /// Creates a new error indicating that required field `name` is missing. + pub fn new(name: &'static str) -> MissingFieldError { + MissingFieldError { + name: name, + } + } +} + +impl Display for MissingFieldError { + + fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result { + write!(fmt, "required field '{}' is missing", self.name) + } +} + +impl std::error::Error for MissingFieldError { + + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +impl From for ParseError { + + fn from(error: MissingFieldError) -> ParseError { + ParseError::malformed(error) + } +} diff --git a/src/session/sink.rs b/src/session/sink.rs index 78889439..ff195647 100644 --- a/src/session/sink.rs +++ b/src/session/sink.rs @@ -17,7 +17,7 @@ use crate::action; use crate::session; /// Handle to a specific sink. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct Sink { /// An underlying identifier of the sink. id: &'static str, From c3d9cfadb4adb778020c177c1e09394d57ce0e71 Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Thu, 14 May 2020 10:23:09 +0300 Subject: [PATCH 05/15] Add tests and some comments --- src/action/platform_info.rs | 132 +++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 19d40fed..0bfdb2a3 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -4,8 +4,9 @@ // in the LICENSE file or at https://opensource.org/licenses/MIT. extern crate sys_info; + +/// Using for `uname` syscall. extern crate libc; -// extern crate sysinfo; use sys_info::{linux_os_release, os_type, hostname}; use libc::{uname, utsname, c_char}; @@ -15,6 +16,7 @@ use crate::session::{self, Session}; use rrg_proto::Uname; +/// Structure that contains information about client platform. #[derive(Default)] pub struct PlatformInfo { system: Option, @@ -27,10 +29,13 @@ pub struct PlatformInfo { node: Option } +/// A Response type for `GetPlatformInfo` action pub struct Response { + /// Client platform information platform_information: PlatformInfo, } +/// Funcion that converts raw C-strings into `String` type #[inline(always)] fn convert_raw_string(c_string: &[c_char]) -> String { unsafe { @@ -38,6 +43,8 @@ fn convert_raw_string(c_string: &[c_char]) -> String { } } +/// Handles requests for `GetPlatformInfo` action. +/// Currently works fine only for Linux OS. pub fn handle(session:&mut S, _: ()) -> session::Result<()> { let os_type = os_type().expect("Can't get os type"); match os_type.as_str() { @@ -82,9 +89,10 @@ pub fn handle(session:&mut S, _: ()) -> session::Result<()> { impl super::Response for Response { const RDF_NAME: Option<&'static str> = Some("Uname"); - type Proto = Uname; + type Proto = rrg_proto::Uname; - fn into_proto(self) -> Uname { + /// Convert PlatformInformation struct to protobuf message Uname + fn into_proto(self) -> rrg_proto::Uname { Uname { system: self.platform_information.system.clone(), release: self.platform_information.release_name.clone(), @@ -104,3 +112,121 @@ impl super::Response for Response { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_system() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + assert_eq!(platform_info.system.as_ref().unwrap(), sys_info::os_type().as_ref().unwrap()); + } + + #[test] + fn test_linux_release() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + if sys_info::os_type().unwrap() == "Linux" { + assert_eq!(platform_info.release_name.as_ref().unwrap(), sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); + } + } + + #[test] + fn test_linux_version() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + if sys_info::os_type().unwrap() == "Linux" { + assert_eq!(platform_info.version_id.as_ref().unwrap(), sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); + } + } + + #[test] + fn test_linux_machine() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + if sys_info::os_type().unwrap() == "Linux" { + let mut system_info: utsname; + + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } + assert_eq!(platform_info.machine.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); + } + } + + #[test] + fn test_linux_kernel_release() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + if sys_info::os_type().unwrap() == "Linux" { + let mut system_info: utsname; + + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } + assert_eq!(platform_info.kernel_release.as_ref().unwrap(), &convert_raw_string(&system_info.release)); + } + } + + #[test] + fn test_linux_architecture() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + if sys_info::os_type().unwrap() == "Linux" { + let mut system_info: utsname; + + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } + assert_eq!(platform_info.architecture.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); + } + } + + #[test] + fn test_linux_node() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0).platform_information; + + if sys_info::os_type().unwrap() == "Linux" { + let mut system_info: utsname; + + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } + assert_eq!(platform_info.node.as_ref().unwrap(), &convert_raw_string(&system_info.nodename)); + } + } +} From 670195dc8ebbc18524ca53ca63630b3e9b5a47ff Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Thu, 14 May 2020 11:27:48 +0300 Subject: [PATCH 06/15] Add error handling --- src/action/platform_info.rs | 48 ++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 0bfdb2a3..af25c398 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -10,12 +10,52 @@ extern crate libc; use sys_info::{linux_os_release, os_type, hostname}; use libc::{uname, utsname, c_char}; +use crate::session::{self, Session}; use std::ffi::CStr; use std::option::Option; -use crate::session::{self, Session}; +use std::fmt::{Display, Formatter}; use rrg_proto::Uname; +#[derive(Debug)] +enum Error { + CannotGetOSType(sys_info::Error), + CannotGetLinuxRelease(sys_info::Error) +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use Error::*; + + match *self { + CannotGetOSType(ref error) => Some(error), + CannotGetLinuxRelease(ref error) => Some(error) + } + } +} + +impl Display for Error { + fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result { + use Error::*; + + match *self { + CannotGetOSType(ref error) => { + write!(fmt, "can't get OS type error: {}", error) + }, + + CannotGetLinuxRelease(ref error) => { + write!(fmt, "can't get Linux release info error: {}", error) + } + } + } +} + +impl From for session::Error { + fn from(error: Error) -> session::Error { + session::Error::action(error) + } +} + /// Structure that contains information about client platform. #[derive(Default)] pub struct PlatformInfo { @@ -44,13 +84,15 @@ fn convert_raw_string(c_string: &[c_char]) -> String { } /// Handles requests for `GetPlatformInfo` action. +/// /// Currently works fine only for Linux OS. pub fn handle(session:&mut S, _: ()) -> session::Result<()> { - let os_type = os_type().expect("Can't get os type"); + let os_type = os_type().map_err(Error::CannotGetOSType)?; match os_type.as_str() { "Linux" => { let linux_release_info = linux_os_release() - .expect("Can't get info about linux system"); + .map_err(Error::CannotGetLinuxRelease)?; + let mut system_info: utsname; unsafe { From a30c6baaa5c385afa2e8787220d1e4d8fca99fea Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Sat, 30 May 2020 15:36:49 +0300 Subject: [PATCH 07/15] Some fixes --- src/action/platform_info.rs | 59 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 11ab3cd2..869bdb6f 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -39,7 +39,6 @@ impl Display for Error { CannotGetOSType(ref error) => { write!(fmt, "can't get OS type error: {}", error) }, - CannotGetLinuxRelease(ref error) => { write!(fmt, "can't get Linux release info error: {}", error) } @@ -54,9 +53,10 @@ impl From for session::Error { } } -/// Structure that contains information about client platform. +/// A Response type for `GetPlatformInfo` action #[derive(Default)] -pub struct PlatformInfo { +pub struct Response { + /// Client platform information system: Option, release_name: Option, version_id: Option, @@ -67,12 +67,6 @@ pub struct PlatformInfo { node: Option } -/// A Response type for `GetPlatformInfo` action -pub struct Response { - /// Client platform information - platform_information: PlatformInfo, -} - /// Funcion that converts raw C-strings into `String` type #[inline] fn convert_raw_string(c_string: &[c_char]) -> String { @@ -99,7 +93,6 @@ pub fn handle(session:&mut S, _: ()) -> session::Result<()> { } session.reply(Response { - platform_information: PlatformInfo { system: Some(os_type.to_string()), release_name: linux_release_info.name, version_id: linux_release_info.version_id, @@ -108,18 +101,14 @@ pub fn handle(session:&mut S, _: ()) -> session::Result<()> { fqdn: hostname().ok(), architecture: Some(convert_raw_string(&system_info.machine)), node: Some(convert_raw_string(&system_info.nodename)) - }, - })?; + })?; }, - _ => { session.reply(Response { - platform_information: PlatformInfo { system: Some(os_type.to_string()), fqdn: hostname().ok(), ..Default::default() // TODO: Add other systems - } - })?; + })?; } } @@ -127,6 +116,7 @@ pub fn handle(session:&mut S, _: ()) -> session::Result<()> { } impl super::Response for Response { + const RDF_NAME: Option<&'static str> = Some("Uname"); type Proto = rrg_proto::Uname; @@ -134,19 +124,19 @@ impl super::Response for Response { /// Convert PlatformInformation struct to protobuf message Uname fn into_proto(self) -> rrg_proto::Uname { Uname { - system: self.platform_information.system.clone(), - release: self.platform_information.release_name.clone(), - version: self.platform_information.version_id, - machine: self.platform_information.machine, - kernel: self.platform_information.kernel_release, - fqdn: self.platform_information.fqdn, - architecture: self.platform_information.architecture.clone(), - node: self.platform_information.node, + system: self.system.clone(), + release: self.release_name.clone(), + version: self.version_id, + machine: self.machine, + kernel: self.kernel_release, + fqdn: self.fqdn, + architecture: self.architecture.clone(), + node: self.node, pep425tag: Some( format!("{}_{}_{}", - self.platform_information.system.unwrap_or(String::from("")), - self.platform_information.release_name.unwrap_or(String::from("")), - self.platform_information.architecture.unwrap_or(String::from("")) + self.system.unwrap_or(String::from("")), + self.release_name.unwrap_or(String::from("")), + self.architecture.unwrap_or(String::from("")) )), ..Default::default() } @@ -155,6 +145,7 @@ impl super::Response for Response { #[cfg(test)] mod test { + use super::*; #[test] @@ -163,7 +154,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); assert_eq!(platform_info.system.as_ref().unwrap(), sys_info::os_type().as_ref().unwrap()); } @@ -174,7 +165,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); if sys_info::os_type().unwrap() == "Linux" { assert_eq!(platform_info.release_name.as_ref().unwrap(), sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); @@ -187,7 +178,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); if sys_info::os_type().unwrap() == "Linux" { assert_eq!(platform_info.version_id.as_ref().unwrap(), sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); @@ -200,7 +191,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); if sys_info::os_type().unwrap() == "Linux" { let mut system_info: utsname; @@ -219,7 +210,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); if sys_info::os_type().unwrap() == "Linux" { let mut system_info: utsname; @@ -238,7 +229,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); if sys_info::os_type().unwrap() == "Linux" { let mut system_info: utsname; @@ -257,7 +248,7 @@ mod test { assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); - let platform_info = &session.reply::(0).platform_information; + let platform_info = &session.reply::(0); if sys_info::os_type().unwrap() == "Linux" { let mut system_info: utsname; From b001985e42f4b505ea9d30986efaa289608855ac Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Sat, 30 May 2020 18:29:40 +0300 Subject: [PATCH 08/15] Remove inline attribute --- src/action/platform_info.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 869bdb6f..cdb5a813 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -68,7 +68,6 @@ pub struct Response { } /// Funcion that converts raw C-strings into `String` type -#[inline] fn convert_raw_string(c_string: &[c_char]) -> String { unsafe { String::from(CStr::from_ptr(c_string.as_ptr()).to_string_lossy().into_owned()) From d5ae5b2c3902167159e9420191757f0b314e09c5 Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Sat, 30 May 2020 22:18:46 +0300 Subject: [PATCH 09/15] Update sys_info version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index eef2d1ef..c406d686 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ prost-types = { version = "0.6.1" } rrg-proto = { path = "proto/" } simplelog = { version = "0.7.6" } structopt = { version = "0.3.12" } -sys-info = { version = "0.5.1" } +sys-info = { version = "0.7.0" } libc = { version = "0.2" } flate2 = { version = "1.0.14" } byteorder = { version = "1.3.4" } From 2aacbabd59464f7d18be9cf8dfe08d2a27c0d146 Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Sun, 31 May 2020 19:09:22 +0300 Subject: [PATCH 10/15] Fix compilation under Windows --- src/action/platform_info.rs | 64 ++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index cdb5a813..af4e11a7 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -4,7 +4,9 @@ // in the LICENSE file or at https://opensource.org/licenses/MIT. use sys_info::{linux_os_release, os_type, hostname}; -use libc::{uname, utsname, c_char}; +#[cfg(target_os = "linux")] +use libc::{uname, utsname}; +use libc::c_char; use crate::session::{self, Session}; use std::ffi::CStr; use std::option::Option; @@ -67,44 +69,56 @@ pub struct Response { node: Option } -/// Funcion that converts raw C-strings into `String` type +/// Function that converts raw C-strings into `String` type fn convert_raw_string(c_string: &[c_char]) -> String { unsafe { String::from(CStr::from_ptr(c_string.as_ptr()).to_string_lossy().into_owned()) } } +/// Function that returns `Response` for unix operating systems +#[cfg(target_os = "linux")] +fn get_linux_response(session: &mut S, os_type: String) -> session::Result<()> { + let linux_release_info = linux_os_release() + .map_err(Error::CannotGetLinuxRelease)?; + + let mut system_info: utsname; + + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } + + session.reply(Response { + system: Some(os_type), + release_name: linux_release_info.name, + version_id: linux_release_info.version_id, + machine: Some(convert_raw_string(&system_info.machine)), + kernel_release: Some(convert_raw_string(&system_info.release)), + fqdn: hostname().ok(), + architecture: Some(convert_raw_string(&system_info.machine)), + node: Some(convert_raw_string(&system_info.nodename)) + })?; + + Ok(()) +} + +/// Fake realization for non-linux OS. +#[cfg(not(target_os = "linux"))] +fn get_linux_response(_: &mut S, _: String) -> session::Result<()> { + Ok(()) +} + /// Handles requests for `GetPlatformInfo` action. -/// -/// Currently works fine only for Linux OS. pub fn handle(session:&mut S, _: ()) -> session::Result<()> { let os_type = os_type().map_err(Error::CannotGetOSType)?; match os_type.as_str() { "Linux" => { - let linux_release_info = linux_os_release() - .map_err(Error::CannotGetLinuxRelease)?; - - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - - session.reply(Response { - system: Some(os_type.to_string()), - release_name: linux_release_info.name, - version_id: linux_release_info.version_id, - machine: Some(convert_raw_string(&system_info.machine)), - kernel_release: Some(convert_raw_string(&system_info.release)), - fqdn: hostname().ok(), - architecture: Some(convert_raw_string(&system_info.machine)), - node: Some(convert_raw_string(&system_info.nodename)) - })?; + get_linux_response(session, os_type)?; }, _ => { session.reply(Response { - system: Some(os_type.to_string()), + system: Some(os_type), fqdn: hostname().ok(), ..Default::default() // TODO: Add other systems })?; From c3483a29e9bcc9c98e20d7ae67ccb2d53bcb49bc Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Sun, 31 May 2020 20:00:10 +0300 Subject: [PATCH 11/15] Fix tests --- src/action/platform_info.rs | 63 +++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index af4e11a7..42933b39 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -173,6 +173,7 @@ mod test { } #[test] + #[cfg(target_os = "linux")] fn test_linux_release() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -180,12 +181,11 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - if sys_info::os_type().unwrap() == "Linux" { - assert_eq!(platform_info.release_name.as_ref().unwrap(), sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); - } + assert_eq!(platform_info.release_name.as_ref().unwrap(), sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); } #[test] + #[cfg(target_os = "linux")] fn test_linux_version() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -193,12 +193,11 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - if sys_info::os_type().unwrap() == "Linux" { - assert_eq!(platform_info.version_id.as_ref().unwrap(), sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); - } + assert_eq!(platform_info.version_id.as_ref().unwrap(), sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); } #[test] + #[cfg(target_os = "linux")] fn test_linux_machine() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -206,37 +205,34 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - if sys_info::os_type().unwrap() == "Linux" { - let mut system_info: utsname; + let mut system_info: utsname; - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.machine.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); } + assert_eq!(platform_info.machine.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); } #[test] + #[cfg(target_os = "linux")] fn test_linux_kernel_release() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); + let mut system_info: utsname; - if sys_info::os_type().unwrap() == "Linux" { - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.kernel_release.as_ref().unwrap(), &convert_raw_string(&system_info.release)); + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); } + assert_eq!(platform_info.kernel_release.as_ref().unwrap(), &convert_raw_string(&system_info.release)); } #[test] + #[cfg(target_os = "linux")] fn test_linux_architecture() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -244,18 +240,17 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - if sys_info::os_type().unwrap() == "Linux" { - let mut system_info: utsname; + let mut system_info: utsname; - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.architecture.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); } + assert_eq!(platform_info.architecture.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); } #[test] + #[cfg(target_os = "linux")] fn test_linux_node() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -263,14 +258,12 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - if sys_info::os_type().unwrap() == "Linux" { - let mut system_info: utsname; + let mut system_info: utsname; - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.node.as_ref().unwrap(), &convert_raw_string(&system_info.nodename)); + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); } + assert_eq!(platform_info.node.as_ref().unwrap(), &convert_raw_string(&system_info.nodename)); } } From df047b68311a42dfc8d656ba445be6f94f7d8dcc Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Sun, 31 May 2020 22:11:41 +0300 Subject: [PATCH 12/15] Fix comments and improve get_linux_response --- src/action/platform_info.rs | 68 +++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 42933b39..63351294 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -76,36 +76,31 @@ fn convert_raw_string(c_string: &[c_char]) -> String { } } -/// Function that returns `Response` for unix operating systems -#[cfg(target_os = "linux")] +/// Function that returns `Response` for Linux operating systems fn get_linux_response(session: &mut S, os_type: String) -> session::Result<()> { - let linux_release_info = linux_os_release() + #[cfg(target_os = "linux")] + { + let linux_release_info = linux_os_release() .map_err(Error::CannotGetLinuxRelease)?; - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - - session.reply(Response { - system: Some(os_type), - release_name: linux_release_info.name, - version_id: linux_release_info.version_id, - machine: Some(convert_raw_string(&system_info.machine)), - kernel_release: Some(convert_raw_string(&system_info.release)), - fqdn: hostname().ok(), - architecture: Some(convert_raw_string(&system_info.machine)), - node: Some(convert_raw_string(&system_info.nodename)) - })?; + let mut system_info: utsname; - Ok(()) -} + unsafe { + system_info = std::mem::zeroed(); + uname(&mut system_info); + } -/// Fake realization for non-linux OS. -#[cfg(not(target_os = "linux"))] -fn get_linux_response(_: &mut S, _: String) -> session::Result<()> { + session.reply(Response { + system: Some(os_type), + release_name: linux_release_info.name, + version_id: linux_release_info.version_id, + machine: Some(convert_raw_string(&system_info.machine)), + kernel_release: Some(convert_raw_string(&system_info.release)), + fqdn: hostname().ok(), + architecture: Some(convert_raw_string(&system_info.machine)), + node: Some(convert_raw_string(&system_info.nodename)) + })?; + } Ok(()) } @@ -134,7 +129,7 @@ impl super::Response for Response { type Proto = rrg_proto::Uname; - /// Convert PlatformInformation struct to protobuf message Uname + /// Convert `Response` struct to protobuf message `Uname` fn into_proto(self) -> rrg_proto::Uname { Uname { system: self.system.clone(), @@ -169,7 +164,8 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - assert_eq!(platform_info.system.as_ref().unwrap(), sys_info::os_type().as_ref().unwrap()); + assert_eq!(platform_info.system.as_ref().unwrap(), + sys_info::os_type().as_ref().unwrap()); } #[test] @@ -181,7 +177,8 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - assert_eq!(platform_info.release_name.as_ref().unwrap(), sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); + assert_eq!(platform_info.release_name.as_ref().unwrap(), + sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); } #[test] @@ -193,7 +190,8 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - assert_eq!(platform_info.version_id.as_ref().unwrap(), sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); + assert_eq!(platform_info.version_id.as_ref().unwrap(), + sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); } #[test] @@ -211,7 +209,8 @@ mod test { system_info = std::mem::zeroed(); uname(&mut system_info); } - assert_eq!(platform_info.machine.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); + assert_eq!(platform_info.machine.as_ref().unwrap(), + &convert_raw_string(&system_info.machine)); } #[test] @@ -228,7 +227,8 @@ mod test { system_info = std::mem::zeroed(); uname(&mut system_info); } - assert_eq!(platform_info.kernel_release.as_ref().unwrap(), &convert_raw_string(&system_info.release)); + assert_eq!(platform_info.kernel_release.as_ref().unwrap(), + &convert_raw_string(&system_info.release)); } #[test] @@ -246,7 +246,8 @@ mod test { system_info = std::mem::zeroed(); uname(&mut system_info); } - assert_eq!(platform_info.architecture.as_ref().unwrap(), &convert_raw_string(&system_info.machine)); + assert_eq!(platform_info.architecture.as_ref().unwrap(), + &convert_raw_string(&system_info.machine)); } #[test] @@ -264,6 +265,7 @@ mod test { system_info = std::mem::zeroed(); uname(&mut system_info); } - assert_eq!(platform_info.node.as_ref().unwrap(), &convert_raw_string(&system_info.nodename)); + assert_eq!(platform_info.node.as_ref().unwrap(), + &convert_raw_string(&system_info.nodename)); } } From 283a416773aa9b90ca9ee0aa9adf88b30189c120 Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Mon, 1 Jun 2020 09:59:17 +0300 Subject: [PATCH 13/15] Change tests and pep425tag in into_proto function --- src/action/platform_info.rs | 79 +++++++++---------------------------- 1 file changed, 19 insertions(+), 60 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 63351294..42eaaa1e 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -4,8 +4,6 @@ // in the LICENSE file or at https://opensource.org/licenses/MIT. use sys_info::{linux_os_release, os_type, hostname}; -#[cfg(target_os = "linux")] -use libc::{uname, utsname}; use libc::c_char; use crate::session::{self, Session}; use std::ffi::CStr; @@ -76,6 +74,9 @@ fn convert_raw_string(c_string: &[c_char]) -> String { } } +#[cfg(target_os = "linux")] +use libc::{uname, utsname}; + /// Function that returns `Response` for Linux operating systems fn get_linux_response(session: &mut S, os_type: String) -> session::Result<()> { #[cfg(target_os = "linux")] @@ -142,9 +143,9 @@ impl super::Response for Response { node: self.node, pep425tag: Some( format!("{}_{}_{}", - self.system.unwrap_or(String::from("")), - self.release_name.unwrap_or(String::from("")), - self.architecture.unwrap_or(String::from("")) + self.system.unwrap_or(String::from("None")), + self.release_name.unwrap_or(String::from("None")), + self.architecture.unwrap_or(String::from("None")) )), ..Default::default() } @@ -164,108 +165,66 @@ mod test { assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - assert_eq!(platform_info.system.as_ref().unwrap(), - sys_info::os_type().as_ref().unwrap()); + assert!(platform_info.system.is_some()); } #[test] - #[cfg(target_os = "linux")] - fn test_linux_release() { + fn test_release() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - - assert_eq!(platform_info.release_name.as_ref().unwrap(), - sys_info::linux_os_release().unwrap().name.as_ref().unwrap()); + assert!(platform_info.release_name.is_some()); } #[test] - #[cfg(target_os = "linux")] - fn test_linux_version() { + fn test_version() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - - assert_eq!(platform_info.version_id.as_ref().unwrap(), - sys_info::linux_os_release().unwrap().version_id.as_ref().unwrap()); + assert!(platform_info.version_id.is_some()); } #[test] - #[cfg(target_os = "linux")] - fn test_linux_machine() { + fn test_machine() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.machine.as_ref().unwrap(), - &convert_raw_string(&system_info.machine)); + assert!(platform_info.machine.is_some()); } #[test] - #[cfg(target_os = "linux")] - fn test_linux_kernel_release() { + fn test_kernel_release() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.kernel_release.as_ref().unwrap(), - &convert_raw_string(&system_info.release)); + assert!(platform_info.kernel_release.is_some()); } #[test] - #[cfg(target_os = "linux")] - fn test_linux_architecture() { + fn test_architecture() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.architecture.as_ref().unwrap(), - &convert_raw_string(&system_info.machine)); + assert!(platform_info.architecture.is_some()); } #[test] - #[cfg(target_os = "linux")] - fn test_linux_node() { + fn test_node() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - - let mut system_info: utsname; - - unsafe { - system_info = std::mem::zeroed(); - uname(&mut system_info); - } - assert_eq!(platform_info.node.as_ref().unwrap(), - &convert_raw_string(&system_info.nodename)); + assert!(platform_info.node.is_some()); } } From 43000ba91cff31205779247f8117aee99602104c Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Mon, 1 Jun 2020 10:09:41 +0300 Subject: [PATCH 14/15] Fix tests --- src/action/platform_info.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/action/platform_info.rs b/src/action/platform_info.rs index 42eaaa1e..17fdb3a1 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform_info.rs @@ -169,7 +169,8 @@ mod test { } #[test] - fn test_release() { + #[cfg(target_os = "linux")] + fn test_linux_release() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -179,7 +180,8 @@ mod test { } #[test] - fn test_version() { + #[cfg(target_os = "linux")] + fn test_linux_version() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -189,7 +191,8 @@ mod test { } #[test] - fn test_machine() { + #[cfg(target_os = "linux")] + fn test_linux_machine() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -199,7 +202,8 @@ mod test { } #[test] - fn test_kernel_release() { + #[cfg(target_os = "linux")] + fn test_linux_kernel_release() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -209,7 +213,8 @@ mod test { } #[test] - fn test_architecture() { + #[cfg(target_os = "linux")] + fn test_linux_architecture() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); @@ -219,7 +224,8 @@ mod test { } #[test] - fn test_node() { + #[cfg(target_os = "linux")] + fn test_linux_node() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); From 49b0fd958012fba310a900507fcf1a91ef3748f5 Mon Sep 17 00:00:00 2001 From: nikkita1267 Date: Fri, 28 Aug 2020 20:17:32 +0300 Subject: [PATCH 15/15] Some minor fixes --- src/action/mod.rs | 4 +- src/action/{platform_info.rs => platform.rs} | 78 +++++++++++++------- 2 files changed, 54 insertions(+), 28 deletions(-) rename src/action/{platform_info.rs => platform.rs} (76%) diff --git a/src/action/mod.rs b/src/action/mod.rs index 93ac8e97..aeec9775 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -15,7 +15,7 @@ //! instance of the corresponding request type and send some (zero or more) //! instances of the corresponding response type. -pub mod platform_info; +pub mod platform; #[cfg(target_os = "linux")] pub mod filesystems; @@ -101,7 +101,7 @@ where match action { "SendStartupInfo" => task.execute(self::startup::handle), "GetClientInfo" => task.execute(self::metadata::handle), - "GetPlatformInfo" => task.execute(self::platform_info::handle), + "GetPlatformInfo" => task.execute(self::platform::handle), "ListNetworkConnections" => task.execute(self::network::handle), #[cfg(target_family = "unix")] diff --git a/src/action/platform_info.rs b/src/action/platform.rs similarity index 76% rename from src/action/platform_info.rs rename to src/action/platform.rs index 17fdb3a1..b9d8c775 100644 --- a/src/action/platform_info.rs +++ b/src/action/platform.rs @@ -53,10 +53,11 @@ impl From for session::Error { } } -/// A Response type for `GetPlatformInfo` action +/// A response type for `GetPlatformInfo` action. +/// Gives client platform information. #[derive(Default)] pub struct Response { - /// Client platform information + system: Option, release_name: Option, version_id: Option, @@ -108,17 +109,14 @@ fn get_linux_response(session: &mut S, os_type: String) -> session:: /// Handles requests for `GetPlatformInfo` action. pub fn handle(session:&mut S, _: ()) -> session::Result<()> { let os_type = os_type().map_err(Error::CannotGetOSType)?; - match os_type.as_str() { - "Linux" => { - get_linux_response(session, os_type)?; - }, - _ => { - session.reply(Response { - system: Some(os_type), - fqdn: hostname().ok(), - ..Default::default() // TODO: Add other systems - })?; - } + if cfg!(target_os = "linux") { + get_linux_response(session, os_type)?; + } else { + session.reply(Response { + system: Some(os_type), + fqdn: hostname().ok(), + ..Default::default() // TODO: Add other systems + })?; } Ok(()) @@ -130,23 +128,27 @@ impl super::Response for Response { type Proto = rrg_proto::Uname; - /// Convert `Response` struct to protobuf message `Uname` + /// Convert `Response` struct to protobuf message `Uname`. fn into_proto(self) -> rrg_proto::Uname { + let system = self.system.unwrap_or(String::from("None")); + let release_name = self.release_name.unwrap_or(String::from("None")); + let architecture = self.architecture.unwrap_or(String::from("None")); + let pep425tag = Some( + format!("{}_{}_{}", + system, + release_name, + architecture + )); Uname { - system: self.system.clone(), - release: self.release_name.clone(), + system: Some(system), + release: Some(release_name), version: self.version_id, machine: self.machine, kernel: self.kernel_release, fqdn: self.fqdn, - architecture: self.architecture.clone(), + architecture: Some(architecture), node: self.node, - pep425tag: Some( - format!("{}_{}_{}", - self.system.unwrap_or(String::from("None")), - self.release_name.unwrap_or(String::from("None")), - self.architecture.unwrap_or(String::from("None")) - )), + pep425tag: pep425tag, ..Default::default() } } @@ -157,15 +159,39 @@ mod test { use super::*; + // #[test] + // fn test_system() { + // let mut session = session::test::Fake::new(); + // assert!(handle(&mut session, ()).is_ok()); + + // assert_eq!(session.reply_count(), 1); + // let platform_info = &session.reply::(0); + + // assert!(platform_info.system.is_some()); + // } + #[test] - fn test_system() { + #[cfg(target_os = "linux")] + fn test_system_linux() { let mut session = session::test::Fake::new(); assert!(handle(&mut session, ()).is_ok()); - + assert_eq!(session.reply_count(), 1); let platform_info = &session.reply::(0); - assert!(platform_info.system.is_some()); + assert_eq!(platform_info.system.as_ref().unwrap(), "Linux"); + } + + #[test] + #[cfg(target_os = "windows")] + fn test_system_windows() { + let mut session = session::test::Fake::new(); + assert!(handle(&mut session, ()).is_ok()); + + assert_eq!(session.reply_count(), 1); + let platform_info = &session.reply::(0); + + assert_eq!(platform_info.system.as_ref().unwrap(), "Windows"); } #[test]