blob: 2ef4575a2ea058bf85a5bbd23d7e8e2d26415e3f [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![deny(warnings)]
extern crate bytes;
extern crate fidl;
extern crate fuchsia_zircon;
extern crate futures;
extern crate mxruntime;
extern crate mxruntime_sys;
extern crate tokio_core;
extern crate tokio_fuchsia;
use bytes::ByteOrder;
use fidl::{InterfacePtr, ClientEnd};
use futures::{Future, future};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::rc::Rc;
use tokio_core::reactor;
// Include the generated FIDL bindings for the `DeviceSetting` service.
extern crate garnet_public_lib_device_settings_fidl;
use garnet_public_lib_device_settings_fidl::{DeviceSettingsManager, Status, DeviceSettingsWatcher,
ValueType};
type FidlImmediate<T> = future::FutureResult<T, fidl::CloseChannel>;
struct DeviceSettingsManagerServer {
setting_file_map: HashMap<String, String>,
watchers: Rc<RefCell<HashMap<String, Vec<DeviceSettingsWatcher::Proxy>>>>,
handle: reactor::Handle,
}
impl DeviceSettingsManagerServer {
fn initialize_keys(&mut self, data_dir: String, keys: &[&str]) {
self.setting_file_map = HashMap::with_capacity(keys.len());
for k in keys {
self.setting_file_map.insert(
k.to_string(),
format!(
"{}/{}",
data_dir,
k.to_lowercase()
),
);
}
}
fn run_watchers(&mut self, key: &String, t: ValueType) {
let mut map = self.watchers.borrow_mut();
if let Some(m) = map.get_mut(key) {
m.retain(|w| {
if let Err(e) = w.on_change_settings(t) {
match e {
fidl::Error::IoError(ref ie)
if ie.kind() == io::ErrorKind::ConnectionAborted => {
return false;
}
e => {
eprintln!("Error call watcher: {:?}", e);
}
}
}
return true;
});
}
}
fn set_key(&mut self, key: &String, buf: &[u8], t: ValueType) -> io::Result<bool> {
{
let file = if let Some(f) = self.setting_file_map.get(key) {
f
} else {
return Ok(false);
};
if let Err(e) = write_to_file(file, buf) {
return Err(e);
}
}
self.run_watchers(&key, t);
Ok(true)
}
}
static DATA_DIR: &'static str = "/data/device-settings";
fn write_to_file(file: &str, buf: &[u8]) -> io::Result<()> {
let mut f = File::create(file)?;
f.write_all(buf)
}
fn read_file(file: &str) -> io::Result<String> {
let mut f = File::open(file)?;
let mut contents = String::new();
if let Err(e) = f.read_to_string(&mut contents) {
return Err(e);
}
Ok(contents)
}
impl DeviceSettingsManager::Server for DeviceSettingsManagerServer {
type GetInteger = FidlImmediate<(i64, Status)>;
fn get_integer(&mut self, key: String) -> Self::GetInteger {
let file = if let Some(f) = self.setting_file_map.get(&key) {
f
} else {
return future::ok((0, Status::ErrInvalidSetting));
};
match read_file(file) {
Err(e) => {
if e.kind() == io::ErrorKind::NotFound {
return future::ok((0, Status::ErrNotSet));
}
eprintln!("DeviceSetting: Error reading integer: {:?}", e);
return future::ok((0, Status::ErrRead));
}
Ok(str) => {
match str.parse::<i64>() {
Err(_e) => future::ok((0, Status::ErrIncorrectType)),
Ok(i) => future::ok((i, Status::Ok)),
}
}
}
}
type SetInteger = FidlImmediate<bool>;
fn set_integer(&mut self, key: String, val: i64) -> Self::SetInteger {
match self.set_key(&key, val.to_string().as_bytes(), ValueType::Int) {
Ok(r) => return future::ok(r),
Err(e) => {
eprintln!("DeviceSetting: Error setting integer: {:?}", e);
return future::ok(false);
}
}
}
type GetString = FidlImmediate<(String, Status)>;
fn get_string(&mut self, key: String) -> Self::GetString {
let file = if let Some(f) = self.setting_file_map.get(&key) {
f
} else {
return future::ok(("".to_string(), Status::ErrInvalidSetting));
};
match read_file(file) {
Err(e) => {
if e.kind() == io::ErrorKind::NotFound {
return future::ok(("".to_string(), Status::ErrNotSet));
}
eprintln!("DeviceSetting: Error reading string: {:?}", e);
return future::ok(("".to_string(), Status::ErrRead));
}
Ok(s) => future::ok((s, Status::Ok)),
}
}
type SetString = FidlImmediate<bool>;
fn set_string(&mut self, key: String, val: String) -> Self::SetString {
match self.set_key(&key, val.as_bytes(), ValueType::String) {
Ok(r) => return future::ok(r),
Err(e) => {
eprintln!("DeviceSetting: Error setting string: {:?}", e);
return future::ok(false);
}
}
}
type Watch = FidlImmediate<Status>;
fn watch(
&mut self,
key: String,
watcher: InterfacePtr<ClientEnd<DeviceSettingsWatcher::Service>>,
) -> Self::Watch {
if !self.setting_file_map.contains_key(&key) {
return future::ok(Status::ErrInvalidSetting);
}
match DeviceSettingsWatcher::new_proxy(watcher.inner, &self.handle) {
Err(e) => {
eprintln!("DeviceSetting: Error getting watcher proxy: {:?}", e);
return future::ok(Status::ErrUnknown);
}
Ok(w) => {
let mut map = self.watchers.borrow_mut();
let mv = map.entry(key).or_insert(Vec::new());
mv.push(w);
future::ok(Status::Ok)
}
}
}
}
// TODO(anmittal): Use log crate and use that for logging
fn main() {
let mut core = reactor::Core::new().expect("Unable to create core");
let handle = core.handle();
// TODO(raggi): clean this up, should check for handle_invalid etc.
// We should update mxruntime to provide a safe version of this returning result or option.
let fdio_handle = unsafe {
mxruntime_sys::zx_get_startup_handle(mxruntime::HandleType::ServiceRequest as u32)
};
let fdio_channel =
tokio_fuchsia::Channel::from_channel(
fuchsia_zircon::Channel::from(unsafe { fuchsia_zircon::Handle::from_raw(fdio_handle) }),
&handle,
).unwrap();
let watchers = Rc::new(RefCell::new(HashMap::new()));
let server = fdio_channel.repeat_server(|_chan, ref mut buf| {
let fdio_op = bytes::LittleEndian::read_u32(&buf.bytes()[4..8]);
let path = std::ffi::OsStr::from_bytes(&buf.bytes()[48..]).to_owned();
if fdio_op != 259 {
eprintln!(
"service request channel received unknown op: {:?}",
&fdio_op
);
}
println!(
"service request channel received open request for path: {:?}",
&path
);
if buf.n_handles() != 1 {
eprintln!(
"service request channel received invalid handle count: {}",
buf.n_handles()
);
// TODO(raggi): repeat_server should actually be doing this:
for i in 0..buf.n_handles() {
std::mem::drop(buf.take_handle(i).unwrap());
}
return;
}
let service_channel = fuchsia_zircon::Channel::from(buf.take_handle(0).unwrap());
let mut d = DeviceSettingsManagerServer {
setting_file_map: HashMap::new(),
watchers: watchers.clone(),
handle: handle.clone(),
};
d.initialize_keys(DATA_DIR.to_owned(), &["Timezone"]);
// Create data directory
let _ = fs::create_dir_all(DATA_DIR);
match fidl::Server::new(
DeviceSettingsManager::Dispatcher(d),
service_channel,
&handle,
) {
Ok(server) => {
handle.spawn(server.map_err(move |e| match e {
fidl::Error::IoError(ref ie)
if ie.kind() == io::ErrorKind::ConnectionAborted => {}
e => eprintln!("runtime fidl server error for {:?}: {:?}", path, e),
}))
}
Err(e) => eprintln!("service spawn for {:?} failed: {:?}", path, e),
}
});
if let Err(e) = core.run(server) {
if e.kind() != io::ErrorKind::ConnectionAborted {
println!("Error running server: {:?}", e);
}
}
}
#[cfg(test)]
mod tests {
extern crate tempdir;
use self::tempdir::TempDir;
use super::*;
use garnet_public_lib_device_settings_fidl::DeviceSettingsManager::Server;
fn setup(
keys: &[&str],
) -> Result<(tokio_core::reactor::Core, DeviceSettingsManagerServer, TempDir), ()> {
let core = reactor::Core::new().unwrap();
let mut device_settings = DeviceSettingsManagerServer {
setting_file_map: HashMap::new(),
watchers: Rc::new(RefCell::new(HashMap::new())),
handle: core.handle(),
};
let tmp_dir = TempDir::new("ds_test").unwrap();
device_settings.initialize_keys(tmp_dir.path().to_str().unwrap().to_string(), keys);
// return tmp_dir to keep it in scope
return Ok((core, device_settings, tmp_dir));
}
#[test]
fn test_int() {
let (mut core, mut device_settings, _t) =
setup(&["TestKey"]).expect("Setup should not have failed");
let response_fut = device_settings.set_integer("TestKey".to_string(), 18);
let response = core.run(response_fut).unwrap();
assert!(response, "set_integer failed");
let response_fut = device_settings.get_integer("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, (18, Status::Ok));
}
#[test]
fn test_string() {
let (mut core, mut device_settings, _t) =
setup(&["TestKey"]).expect("Setup should not have failed");
let response_fut =
device_settings.set_string("TestKey".to_string(), "mystring".to_string());
let response = core.run(response_fut).unwrap();
assert!(response, "set_string failed");
let response_fut = device_settings.get_string("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, ("mystring".to_string(), Status::Ok));
}
#[test]
fn test_invalid_key() {
let (mut core, mut device_settings, _t) = setup(&[]).expect("Setup should not have failed");
let response_fut = device_settings.get_string("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, ("".to_string(), Status::ErrInvalidSetting));
let response_fut = device_settings.get_integer("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, (0, Status::ErrInvalidSetting));
}
#[test]
fn test_incorrect_type() {
let (mut core, mut device_settings, _t) =
setup(&["TestKey"]).expect("Setup should not have failed");
let response_fut =
device_settings.set_string("TestKey".to_string(), "mystring".to_string());
let response = core.run(response_fut).unwrap();
assert!(response, "set_string failed");
let response_fut = device_settings.get_integer("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, (0, Status::ErrIncorrectType));
}
#[test]
fn test_not_set_err() {
let (mut core, mut device_settings, _t) =
setup(&["TestKey"]).expect("Setup should not have failed");
let response_fut = device_settings.get_integer("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, (0, Status::ErrNotSet));
let response_fut = device_settings.get_string("TestKey".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, ("".to_string(), Status::ErrNotSet));
}
#[test]
fn test_multiple_keys() {
let (mut core, mut device_settings, _t) =
setup(&["TestKey1", "TestKey2"]).expect("Setup should not have failed");
let response_fut = device_settings.set_integer("TestKey1".to_string(), 18);
let response = core.run(response_fut).unwrap();
assert!(response, "set_integer failed");
let response_fut =
device_settings.set_string("TestKey2".to_string(), "mystring".to_string());
let response = core.run(response_fut).unwrap();
assert!(response, "set_string failed");
let response_fut = device_settings.get_integer("TestKey1".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, (18, Status::Ok));
let response_fut = device_settings.get_string("TestKey2".to_string());
let response = core.run(response_fut).expect(
"core.run should not have failed",
);
assert_eq!(response, ("mystring".to_string(), Status::Ok));
}
}