diff options
author | Shaun Taheri <shaun@advancedtelematic.com> | 2016-10-19 16:15:31 +0200 |
---|---|---|
committer | Shaun Taheri <shaun@advancedtelematic.com> | 2016-10-19 16:35:56 +0200 |
commit | 41db1050631cfe0aaca8922ec4a58a0f2109ac5d (patch) | |
tree | 1c9497eb1c4cb09b8f0125d9fbb1852340fa5213 | |
parent | e50ec3ba87a744e3b581311efede49e21e9cb018 (diff) | |
download | rvi_sota_client-41db1050631cfe0aaca8922ec4a58a0f2109ac5d.tar.gz |
Fix generated SOTA.toml files
-rw-r--r-- | README.md | 3 | ||||
-rwxr-xr-x | run/run.sh | 30 | ||||
-rw-r--r-- | run/sota.toml.env | 6 | ||||
-rw-r--r-- | src/datatype/config.rs | 14 | ||||
-rw-r--r-- | src/datatype/system_info.rs | 21 | ||||
-rw-r--r-- | src/gateway/dbus.rs | 23 | ||||
-rw-r--r-- | src/gateway/websocket.rs | 38 | ||||
-rw-r--r-- | src/interpreter.rs | 20 | ||||
-rw-r--r-- | src/main.rs | 30 | ||||
-rw-r--r-- | src/sota.rs | 3 | ||||
-rw-r--r-- | tests/sota.toml | 6 |
11 files changed, 95 insertions, 99 deletions
@@ -94,11 +94,10 @@ See `tests/genivi.sota.toml` for a sample config. See full documentation for det Now you can run the `sota_client`: make client - ./run/sota_client --config tests/genivi.sota.toml + RUST_LOG=debug ./run/sota_client --config tests/genivi.sota.toml ### GENIVI Software Loading Manager See [genivi_swm](https://github.com/GENIVI/genivi_swm) on how to run the Software Loading Manager demo. It also contains instructions for creating an update image, which can be uploaded as a package to the SOTA Server. Now you can create an update campaign on the SOTA Server, using the same update_id as the uuid in the update image you created. Also, as the genivi_swm demo runs as root, remember to run the `sota_client` as root as well so that they can communicate on the same system bus. - @@ -25,25 +25,27 @@ if [[ -z "${DEVICE_UUID}" ]]; then fi export DEVICE_UUID -# create or use existing device credentials -if [[ -z "${AUTH_CLIENT_ID}" ]]; then - CREDENTIALS=$(http post "${AUTH_SERVER}/clients" \ - client_name="${DEVICE_VIN}" \ - grant_types:='["client_credentials"]' \ - --check-status --print=b) - AUTH_CLIENT_ID=$(echo "${CREDENTIALS}" | jq -r .client_id) - AUTH_CLIENT_SECRET=$(echo "${CREDENTIALS}" | jq -r .client_secret) -fi -export AUTH_CLIENT_ID -export AUTH_CLIENT_SECRET +[[ "${AUTH_SECTION}" = true ]] && { + # create or use existing device credentials + if [[ -z "${AUTH_CLIENT_ID}" ]]; then + CREDENTIALS=$(http post "${AUTH_SERVER}/clients" \ + client_name="${DEVICE_VIN}" \ + grant_types:='["client_credentials"]' \ + --check-status --print=b) + AUTH_CLIENT_ID=$(echo "${CREDENTIALS}" | jq -r .client_id) + AUTH_CLIENT_SECRET=$(echo "${CREDENTIALS}" | jq -r .client_secret) + fi + export AUTH_CLIENT_ID + export AUTH_CLIENT_SECRET +} || { + # remove [auth] section + sed -i '/\[core\]/,$!d' "${TEMPLATE_PATH}" +} # generate sota.toml config echo "---START CONFIG---" envsubst < "${TEMPLATE_PATH}" | tee "${OUTPUT_PATH}" echo "---END CONFIG---" - -# optionally remove auth section and/or quit -[[ "${AUTH_SECTION}" = false ]] && sed -i '/\[core\]/,$!d' "${OUTPUT_PATH}" [[ "${CONFIG_ONLY}" = true ]] && exit 0 # set up dbus diff --git a/run/sota.toml.env b/run/sota.toml.env index ecf15b5..cd7d460 100644 --- a/run/sota.toml.env +++ b/run/sota.toml.env @@ -14,7 +14,7 @@ DEVICE_PACKAGES_DIR=/tmp/ DEVICE_PACKAGE_MANAGER=deb DEVICE_POLLING_INTERVAL=10 DEVICE_CERTIFICATES_PATH=/etc/sota_certificates -DEVICE_SYSTEM_INFO=system_info.sh +DEVICE_SYSTEM_INFO=./system_info.sh GATEWAY_CONSOLE=false GATEWAY_DBUS=false @@ -23,8 +23,8 @@ GATEWAY_RVI=false GATEWAY_SOCKET=false GATEWAY_WEBSOCKET=true -NETWORK_HTTP_SERVER=127.0.0.1:8888 -NETWORK_RVI_EDGE_SERVER=127.0.0.1:9080 +NETWORK_HTTP_SERVER=http://127.0.0.1:8888 +NETWORK_RVI_EDGE_SERVER=http://127.0.0.1:9080 NETWORK_SOCKET_COMMANDS_PATH=/tmp/sota-commands.socket NETWORK_SOCKET_EVENTS_PATH=/tmp/sota-events.socket NETWORK_WEBSOCKET_SERVER=127.0.0.1:3012 diff --git a/src/datatype/config.rs b/src/datatype/config.rs index c9c7708..c9905f5 100644 --- a/src/datatype/config.rs +++ b/src/datatype/config.rs @@ -204,7 +204,7 @@ pub struct DeviceConfig { pub vin: String, pub packages_dir: String, pub package_manager: PackageManager, - pub system_info: SystemInfo, + pub system_info: Option<SystemInfo>, pub polling_interval: u64, pub certificates_path: String, } @@ -216,7 +216,7 @@ impl Default for DeviceConfig { vin: "V1234567890123456".to_string(), packages_dir: "/tmp/".to_string(), package_manager: PackageManager::Deb, - system_info: SystemInfo::default(), + system_info: Some(SystemInfo::default()), polling_interval: 10, certificates_path: "/tmp/sota_certificates".to_string() } @@ -262,8 +262,8 @@ pub struct NetworkConfig { impl Default for NetworkConfig { fn default() -> NetworkConfig { NetworkConfig { - http_server: "127.0.0.1:8888".to_string(), - rvi_edge_server: "127.0.0.1:9080".to_string(), + http_server: "http://127.0.0.1:8888".to_string(), + rvi_edge_server: "http://127.0.0.1:9080".to_string(), socket_commands_path: "/tmp/sota-commands.socket".to_string(), socket_events_path: "/tmp/sota-events.socket".to_string(), websocket_server: "127.0.0.1:3012".to_string() @@ -327,7 +327,7 @@ mod tests { [device] uuid = "123e4567-e89b-12d3-a456-426655440000" vin = "V1234567890123456" - system_info = "system_info.sh" + system_info = "./system_info.sh" polling_interval = 10 packages_dir = "/tmp/" package_manager = "deb" @@ -348,8 +348,8 @@ mod tests { const NETWORK_CONFIG: &'static str = r#" [network] - http_server = "127.0.0.1:8888" - rvi_edge_server = "127.0.0.1:9080" + http_server = "http://127.0.0.1:8888" + rvi_edge_server = "http://127.0.0.1:9080" socket_commands_path = "/tmp/sota-commands.socket" socket_events_path = "/tmp/sota-events.socket" websocket_server = "127.0.0.1:3012" diff --git a/src/datatype/system_info.rs b/src/datatype/system_info.rs index 2d8fff2..987da3b 100644 --- a/src/datatype/system_info.rs +++ b/src/datatype/system_info.rs @@ -1,6 +1,5 @@ use rustc_serialize::{Decoder, Decodable}; use std::process::Command; -use std::str::FromStr; use datatype::Error; @@ -13,8 +12,12 @@ pub struct SystemInfo { impl SystemInfo { /// Instantiate a new type to report on the system information. - pub fn new(command: String) -> SystemInfo { - SystemInfo { command: command } + pub fn new(command: &str) -> Option<SystemInfo> { + if command == "" { + None + } else { + Some(SystemInfo { command: command.to_string() }) + } } /// Generate a new report of the system information. @@ -27,20 +30,12 @@ impl SystemInfo { impl Default for SystemInfo { fn default() -> SystemInfo { - SystemInfo::new("system_info.sh".to_string()) - } -} - -impl FromStr for SystemInfo { - type Err = Error; - - fn from_str(s: &str) -> Result<SystemInfo, Error> { - Ok(SystemInfo::new(s.to_string())) + SystemInfo::new("./system_info.sh").expect("couldn't build command") } } impl Decodable for SystemInfo { fn decode<D: Decoder>(d: &mut D) -> Result<SystemInfo, D::Error> { - d.read_str().and_then(|s| Ok(s.parse::<SystemInfo>().unwrap())) + d.read_str().and_then(|s| SystemInfo::new(&s).ok_or(d.error("bad SystemInfo command path"))) } } diff --git a/src/gateway/dbus.rs b/src/gateway/dbus.rs index 07a3b9c..321e262 100644 --- a/src/gateway/dbus.rs +++ b/src/gateway/dbus.rs @@ -24,7 +24,7 @@ impl Gateway for DBus { thread::spawn(move || { let conn = Connection::get_private(BusType::Session).expect("couldn't get dbus session"); - conn.register_name(&dbus_cfg.name, NameFlag::ReplaceExisting as u32).unwrap(); + conn.register_name(&dbus_cfg.name, NameFlag::ReplaceExisting as u32).expect("couldn't register name"); let mut obj_path = ObjectPath::new(&conn, &dbus_cfg.path, true); obj_path.insert_interface(&dbus_cfg.interface, default_interface(itx)); @@ -33,10 +33,11 @@ impl Gateway for DBus { loop { for item in conn.iter(1000) { if let ConnectionItem::MethodCall(mut msg) = item { - info!("DBus method call: {:?}", msg); - obj_path.handle_message(&mut msg).map(|result| { - let _ = result.map_err(|_| error!("dbus method call failed: {:?}", msg)); - }); + match obj_path.handle_message(&mut msg) { + Some(Ok(())) => info!("DBus message sent: {:?}", msg), + Some(Err(())) => error!("DBus message send failed: {:?}", msg), + None => debug!("unhandled dbus message: {:?}", msg) + } } } } @@ -48,7 +49,7 @@ impl Gateway for DBus { fn pulse(&self, event: Event) { match event { Event::UpdateAvailable(avail) => { - let msg = self.new_message("updateAvailable", &[ + let msg = self.new_swm_message("updateAvailable", &[ MessageItem::from(avail.update_id), MessageItem::from(avail.signature), MessageItem::from(avail.description), @@ -59,7 +60,7 @@ impl Gateway for DBus { } Event::DownloadComplete(comp) => { - let msg = self.new_message("downloadComplete", &[ + let msg = self.new_swm_message("downloadComplete", &[ MessageItem::from(comp.update_image), MessageItem::from(comp.signature) ]); @@ -68,7 +69,7 @@ impl Gateway for DBus { } Event::InstalledSoftwareNeeded => { - let msg = self.new_message("getInstalledPackages", &[ + let msg = self.new_swm_message("getInstalledPackages", &[ MessageItem::from(true), // include packages? MessageItem::from(false) // include firmware? ]); @@ -103,7 +104,7 @@ impl Gateway for DBus { } impl DBus { - fn new_message(&self, method: &str, args: &[MessageItem]) -> Message { + fn new_swm_message(&self, method: &str, args: &[MessageItem]) -> Message { let mgr = self.dbus_cfg.software_manager.clone(); let path = self.dbus_cfg.software_manager_path.clone(); let result = Message::new_method_call(&mgr, &path, &mgr, method); @@ -139,7 +140,7 @@ fn send(itx: &Sender<Interpret>, cmd: Command) { fn handle_initiate_download(itx: &Sender<Interpret>, msg: &mut Message) -> MethodResult { let sender = try!(msg.sender().map(|s| s.to_string()).ok_or(dbus::missing_arg())); - debug!("handle_initiate_download: sender={:?}, msg={:?}", sender, msg); + debug!("dbus handle_initiate_download: sender={:?}, msg={:?}", sender, msg); let mut args = msg.get_items().into_iter(); let arg_id = try!(args.next().ok_or(dbus::missing_arg())); @@ -151,7 +152,7 @@ fn handle_initiate_download(itx: &Sender<Interpret>, msg: &mut Message) -> Metho fn handle_update_report(itx: &Sender<Interpret>, msg: &mut Message) -> MethodResult { let sender = try!(msg.sender().map(|s| s.to_string()).ok_or(dbus::missing_arg())); - debug!("handle_update_report: sender ={:?}, msg ={:?}", sender, msg); + debug!("dbus handle_update_report: sender={:?}, msg={:?}", sender, msg); let mut args = msg.get_items().into_iter(); let id_arg = try!(args.next().ok_or(dbus::missing_arg())); diff --git a/src/gateway/websocket.rs b/src/gateway/websocket.rs index 72e0889..f63a763 100644 --- a/src/gateway/websocket.rs +++ b/src/gateway/websocket.rs @@ -1,12 +1,10 @@ use chan; use chan::Sender; use rustc_serialize::json; -use std::thread; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use std::time::Duration; use ws; -use ws::{listen, CloseCode, Handler, Handshake, Message, Sender as WsSender}; +use ws::{CloseCode, Handler, Handshake, Message, Sender as WsSender}; use ws::util::Token; use datatype::{Command, Error, Event}; @@ -22,22 +20,15 @@ pub struct Websocket { impl Gateway for Websocket { fn initialize(&mut self, itx: Sender<Interpret>) -> Result<(), String> { - let clients = self.clients.clone(); - let addr = self.server.clone(); - info!("Opening websocket listener at {}", addr); - - thread::spawn(move || { - listen(&addr as &str, |out| { - WebsocketHandler { - out: out, - itx: itx.clone(), - clients: clients.clone() - } - }).expect("couldn't start websocket listener"); - }); + ws::listen(&self.server.clone() as &str, |out| { + WebsocketHandler { + out: out, + itx: itx.clone(), + clients: self.clients.clone() + } + }).expect("couldn't start websocket listener"); - thread::sleep(Duration::from_secs(1)); // FIXME: ugly hack for blocking listen call - Ok(info!("Websocket gateway started.")) + Ok(info!("Websocket gateway started at {}.", self.server)) } fn pulse(&self, event: Event) { @@ -69,7 +60,7 @@ impl Handler for WebsocketHandler { Err(err) } - Err(_) => unreachable!() + Err(err) => panic!("unexpected websocket on_message error: {}", err) }) } @@ -117,7 +108,7 @@ mod tests { use std::collections::HashMap; use std::sync::{Arc, Mutex}; use ws; - use ws::{connect, CloseCode}; + use ws::CloseCode; use datatype::{Command, Event}; use gateway::{Gateway, Interpret}; @@ -125,6 +116,7 @@ mod tests { #[test] + #[ignore] // FIXME: wait for https://github.com/housleyjk/ws-rs/issues/64 fn websocket_connections() { let (etx, erx) = chan::sync::<Event>(0); let (itx, irx) = chan::sync::<Interpret>(0); @@ -152,9 +144,9 @@ mod tests { crossbeam::scope(|scope| { for id in 0..10 { scope.spawn(move || { - connect("ws://localhost:3012", |out| { - out.send(format!(r#"{{ "variant": "StartDownload", "fields": [["{}"]] }}"#, id)) - .expect("couldn't write to websocket"); + ws::connect("ws://localhost:3012", |out| { + let msg = format!(r#"{{ "variant": "StartDownload", "fields": [["{}"]] }}"#, id); + out.send(msg).expect("couldn't write to websocket"); move |msg: ws::Message| { let ev: Event = json::decode(&format!("{}", msg)).unwrap(); diff --git a/src/interpreter.rs b/src/interpreter.rs index 7f401de..b15c37e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -28,11 +28,11 @@ pub trait Interpreter<I, O> { let started = time::precise_time_ns(); wg.add(1); - debug!("interpreter starting: {}", started); + trace!("interpreter starting: {}", started); self.interpret(input, &otx); thread::sleep(cooldown); // let any further work commence - debug!("interpreter stopping: {}", started); + trace!("interpreter stopping: {}", started); wg.done(); } } @@ -42,7 +42,8 @@ pub trait Interpreter<I, O> { /// The `EventInterpreter` listens for `Event`s and optionally responds with /// `Command`s that may be sent to the `CommandInterpreter`. pub struct EventInterpreter { - pub pacman: PackageManager + pub pacman: PackageManager, + pub send_sysinfo: bool, } impl Interpreter<Event, Command> for EventInterpreter { @@ -56,7 +57,10 @@ impl Interpreter<Event, Command> for EventInterpreter { ctx.send(Command::SendInstalledPackages(packages)); }).unwrap_or_else(|err| error!("couldn't send a list of packages: {}", err)); } - ctx.send(Command::SendSystemInfo); + + if self.send_sysinfo { + ctx.send(Command::SendSystemInfo); + } } Event::NotAuthenticated => { @@ -201,8 +205,8 @@ impl<'t> GlobalInterpreter<'t> { } Command::ListSystemInfo => { - let info = try!(self.config.device.system_info.report()); - etx.send(Event::FoundSystemInfo(info)); + let sysinfo = self.config.device.system_info.as_ref().expect("SystemInfo command not set"); + etx.send(Event::FoundSystemInfo(try!(sysinfo.report()))); } Command::SendInstalledPackages(packages) => { @@ -218,8 +222,8 @@ impl<'t> GlobalInterpreter<'t> { } Command::SendSystemInfo => { - let info = try!(self.config.device.system_info.report()); - try!(sota.send_system_info(&info)); + let sysinfo = self.config.device.system_info.as_ref().expect("SystemInfo command not set"); + try!(sota.send_system_info(&try!(sysinfo.report()))); etx.send(Event::SystemInfoSent); } diff --git a/src/main.rs b/src/main.rs index 2caa411..fc001b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,7 @@ fn start_signal_handler(signals: Receiver<Signal>) { } fn start_update_poller(interval: u64, itx: Sender<Interpret>, wg: WaitGroup) { + info!("Polling for new updates every {} seconds.", interval); let (etx, erx) = chan::async::<Event>(); let wait = Duration::from_secs(interval); loop { @@ -54,14 +55,13 @@ fn start_update_poller(interval: u64, itx: Sender<Interpret>, wg: WaitGroup) { itx.send(Interpret { command: Command::GetUpdateRequests, response_tx: Some(Arc::new(Mutex::new(etx.clone()))) - }); + }); // then request new updates let _ = erx.recv(); // then wait for the response } } fn main() { setup_logging(); - let config = build_config(); set_ca_certificates(Path::new(&config.device.certificates_path)); @@ -78,11 +78,6 @@ fn main() { let signals = chan_signal::notify(&[Signal::INT, Signal::TERM]); scope.spawn(move || start_signal_handler(signals)); - let poll_tick = config.device.polling_interval; - let poll_itx = itx.clone(); - let poll_wg = wg.clone(); - scope.spawn(move || start_update_poller(poll_tick, poll_itx, poll_wg)); - if config.gateway.console { let cons_itx = itx.clone(); let cons_sub = broadcast.subscribe(); @@ -104,16 +99,21 @@ fn main() { scope.spawn(move || http.start(http_itx, http_sub)); } - let mut rvi = None; - if config.gateway.rvi { + let rvi_services = if config.gateway.rvi { let _ = config.dbus.as_ref().unwrap_or_else(|| exit!("{}", "dbus config required for rvi gateway")); let rvi_cfg = config.rvi.as_ref().unwrap_or_else(|| exit!("{}", "rvi config required for rvi gateway")); let rvi_edge = config.network.rvi_edge_server.clone(); let services = Services::new(rvi_cfg.clone(), config.device.uuid.clone(), etx.clone()); let mut edge = Edge::new(services.clone(), rvi_edge, rvi_cfg.client.clone()); scope.spawn(move || edge.start()); - rvi = Some(services); - } + Some(services) + } else { + let poll_tick = config.device.polling_interval; + let poll_itx = itx.clone(); + let poll_wg = wg.clone(); + scope.spawn(move || start_update_poller(poll_tick, poll_itx, poll_wg)); + None + }; if config.gateway.socket { let socket_itx = itx.clone(); @@ -136,9 +136,11 @@ fn main() { let event_sub = broadcast.subscribe(); let event_ctx = ctx.clone(); let event_mgr = config.device.package_manager.clone(); + let event_sys = config.device.system_info.is_some(); let event_wg = wg.clone(); scope.spawn(move || EventInterpreter { - pacman: event_mgr + pacman: event_mgr, + send_sysinfo: event_sys, }.run(event_sub, event_ctx, event_wg)); let cmd_itx = itx.clone(); @@ -149,7 +151,7 @@ fn main() { config: config, token: None, http_client: Box::new(AuthClient::default()), - rvi: rvi, + rvi: rvi_services, }.run(irx, etx, wg)); scope.spawn(move || broadcast.start()); @@ -259,7 +261,7 @@ fn build_config() -> Config { config.device.polling_interval = interval.parse().unwrap_or_else(|err| exit!("Invalid device polling interval: {}", err)); }); matches.opt_str("device-certificates-path").map(|certs| config.device.certificates_path = certs); - matches.opt_str("device-system-info").map(|cmd| config.device.system_info = SystemInfo::new(cmd)); + matches.opt_str("device-system-info").map(|cmd| config.device.system_info = SystemInfo::new(&cmd)); matches.opt_str("gateway-console").map(|console| { config.gateway.console = console.parse().unwrap_or_else(|err| exit!("Invalid console gateway boolean: {}", err)); diff --git a/src/sota.rs b/src/sota.rs index 9ea615e..57f9308 100644 --- a/src/sota.rs +++ b/src/sota.rs @@ -1,6 +1,6 @@ use rustc_serialize::json; +use std::{fs, io}; use std::fs::File; -use std::io; use std::path::PathBuf; use datatype::{Config, DeviceReport, DownloadComplete, Error, Package, @@ -75,6 +75,7 @@ impl<'c, 'h> Sota<'c, 'h> { let ref pacman = self.config.device.package_manager; let path = self.package_path(id.clone()).expect("install_update expects a valid path"); pacman.install_package(&path).and_then(|(code, output)| { + let _ = fs::remove_file(&path).unwrap_or_else(|err| error!("couldn't remove installed package: {}", err)); Ok(UpdateReport::single(id.clone(), code, output)) }).or_else(|(code, output)| { Err(UpdateReport::single(id.clone(), code, output)) diff --git a/tests/sota.toml b/tests/sota.toml index 0444a71..1c58c16 100644 --- a/tests/sota.toml +++ b/tests/sota.toml @@ -20,7 +20,7 @@ uuid = "123e4567-e89b-12d3-a456-426655440000" vin = "V1234567890123456" packages_dir = "/tmp/" package_manager = "deb" -system_info = "system_info.sh" +system_info = "./system_info.sh" polling_interval = 10 certificates_path = "/tmp/sota_certificates" @@ -33,8 +33,8 @@ socket = false websocket = true [network] -http_server = "127.0.0.1:8888" -rvi_edge_server = "127.0.0.1:9080" +http_server = "http://127.0.0.1:8888" +rvi_edge_server = "http://127.0.0.1:9080" socket_commands_path = "/tmp/sota-commands.socket" socket_events_path = "/tmp/sota-events.socket" websocket_server = "127.0.0.1:3012" |