blob: 0182674eb170560c64380b29140177f08d79c717 [file] [log] [blame]
// Copyright 2018 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.
#![feature(async_await, await_macro)]
use failure::{err_msg, Error, ResultExt};
use fidl::endpoints::create_proxy;
use fidl_fuchsia_mem;
use fuchsia_async as fasync;
use fuchsia_component::client::connect_to_service;
use fuchsia_zircon as zx;
use futures::FutureExt;
use std::convert::{TryFrom, TryInto};
use std::env;
use std::str;
use fidl_fuchsia_stash::{KeyValue, ListItem, StoreMarker, SecureStoreMarker, Value};
fn main() -> Result<(), Error> {
let cfg: StashCtlConfig = env::args().try_into()?;
let mut executor = fasync::Executor::new().context("Error creating executor")?;
let acc = if cfg.secure {
let stashserver = connect_to_service::<SecureStoreMarker>()?;
// Identify
stashserver.identify("stash_ctl")?;
// Create an accessor
let (acc, server_end) = create_proxy()?;
stashserver.create_accessor(false, server_end)?;
acc
} else {
let stashserver = connect_to_service::<StoreMarker>()?;
// Identify
stashserver.identify("stash_ctl")?;
// Create an accessor
let (acc, server_end) = create_proxy()?;
stashserver.create_accessor(false, server_end)?;
acc
};
// Perform the operation
match cfg.op {
StashOperation::Get(k) => {
let fut = acc.get_value(&k)
.map(|res| match res {
Ok(Some(val)) => print_val(&val),
Ok(None) => println!("no such key: {}", k),
Err(e) => println!("fidl error encountered: {:?}", e),
});
executor.run_singlethreaded(fut);
}
StashOperation::Set(k, mut v) => {
acc.set_value(&k, &mut v)?;
acc.commit()?;
println!("{} set successfully", k);
}
StashOperation::Delete(k) => {
acc.delete_value(&k)?;
acc.commit()?;
println!("{} deleted successfully", k);
}
StashOperation::ListPrefix(k) => {
let (list_iterator, server_end) = create_proxy()?;
acc.list_prefix(&k, server_end)?;
let resp: Result<(), Error> = executor.run_singlethreaded(async {
loop {
let res = await!(list_iterator.get_next())?;
if res.len() == 0 {
return Ok(());
}
print_listitems(res);
}
});
resp?;
}
StashOperation::GetPrefix(k) => {
let (get_iterator, server_end) = create_proxy()?;
acc.get_prefix(&k, server_end)?;
let resp: Result<(), Error> = executor.run_singlethreaded(async {
loop {
let res = await!(get_iterator.get_next())?;
if res.len() == 0 {
return Ok(());
}
print_keyvalues(res);
}
});
resp?;
}
StashOperation::DeletePrefix(k) => {
acc.delete_prefix(&k)
.map(|_| println!("{} prefix deleted successfully", k))?;
acc.commit()?;
}
};
Ok(())
}
struct StashCtlConfig {
secure: bool,
op: StashOperation,
}
enum StashOperation {
Get(String),
Set(String, Value),
Delete(String),
ListPrefix(String),
GetPrefix(String),
DeletePrefix(String),
}
impl TryFrom<env::Args> for StashCtlConfig {
type Error = Error;
fn try_from(args: env::Args) -> Result<StashCtlConfig, Error> {
let mut args = args.peekable();
// ignore arg[0]
let _ = args.next();
let secure = args.peek() == Some(&"--secure".to_string());
if secure {
let _ = args.next();
}
// take advantage of the fact that `next()` will keep returning `None`
let op = args.next();
let key = args.next();
let ty = args.next();
let val = args.next();
let op = match (
op.as_ref().map(|s| s.as_str()),
key,
ty.as_ref().map(|s| s.as_str()),
val.as_ref().map(|s| s.as_str()),
) {
(Some("get"), Some(key), None, None) =>
StashOperation::Get(key),
(Some("set"), Some(key), Some(type_), Some(val)) =>
StashOperation::Set(key, to_val(type_, val)?),
(Some("delete"), Some(key), None, None) =>
StashOperation::Delete(key),
(Some("list-prefix"), Some(key), None, None) =>
StashOperation::ListPrefix(key),
(Some("get-prefix"), Some(key), None, None) =>
StashOperation::GetPrefix(key),
(Some("delete-prefix"), Some(key), None, None) =>
StashOperation::DeletePrefix(key),
_ => {
help();
return Err(err_msg("unable to parse args"));
}
};
Ok(StashCtlConfig{ secure, op })
}
}
fn to_val(typ: &str, input: &str) -> Result<Value, Error> {
match typ {
"int" => Ok(Value::Intval(input.parse()?)),
"float" => Ok(Value::Floatval(input.parse()?)),
"text" => Ok(Value::Stringval(input.to_string())),
"bool" => Ok(Value::Boolval(input.parse()?)),
"bytes" => {
let bytes = input.as_bytes();
let vmo = zx::Vmo::create(bytes.len() as u64)
.map_err(|s| err_msg(format!("error creating bytes buffer, zx status: {}", s)))?;
vmo.write(&bytes, 0)
.map_err(|s| err_msg(format!("error writing bytes buffer, zx status: {}", s)))?;
Ok(Value::Bytesval(fidl_fuchsia_mem::Buffer {
vmo: vmo,
size: bytes.len() as u64,
}))
}
_ => Err(err_msg(format!("unknown type: {}", typ))),
}
}
fn help() {
println!(
r"Usage: stash_ctl [--secure] get NAME
stash_ctl [--secure] set NAME [int|float|text|bool|bytes] VALUE
stash_ctl [--secure] delete NAME
stash_ctl [--secure] list-prefix PREFIX
stash_ctl [--secure] get-prefix PREFIX
stash_ctl [--secure] delete-prefix PREFIX
NOTE: --secure will access a version of the store where the bytes type is disabled"
)
}
fn print_val(val: &Value) {
match val {
Value::Intval(i) => println!("{}", i),
Value::Floatval(f) => println!("{}", f),
Value::Boolval(b) => println!("{}", b),
Value::Stringval(s) => println!("{}", s),
Value::Bytesval(b) => {
let mut bytes_buffer = vec![0; b.size as usize]; // TODO: make sure b.size is reasonable
match b.vmo.read(&mut bytes_buffer, 0) {
Ok(_) => match str::from_utf8(&bytes_buffer) {
Ok(s) => println!("{}", s),
Err(e) => println!("error decoding response: {}", e),
},
Err(e) => println!("error reading json buffer, zx status: {}", e),
}
}
}
}
fn print_listitems(list: Vec<ListItem>) {
if list.is_empty() {
println!("no keys found");
return;
}
list.iter().for_each(|item| println!("{}", item.key));
}
fn print_keyvalues(list: Vec<KeyValue>) {
if list.is_empty() {
println!("no keys found");
return;
}
list.iter().for_each(|item| {
print!("{}: ", item.key);
print_val(&item.val);
});
}