Skip to content

Commit eacf53d

Browse files
committed
feat: uninstall apps
Signed-off-by: Adrian Lungu <lunguadrian30@gmail.com>
1 parent 156db21 commit eacf53d

7 files changed

Lines changed: 180 additions & 3 deletions

File tree

tockloader-cli/src/cli.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ fn get_subcommands() -> Vec<Command> {
5858
.args(get_app_args())
5959
.args(get_channel_args())
6060
.arg_required_else_help(false),
61+
Command::new("uninstall")
62+
.about("Uninstall apps")
63+
.arg(arg!(--"name" <APPNAME>).required(false))
64+
.args(get_app_args())
65+
.args(get_channel_args())
66+
.arg_required_else_help(false),
6167
]
6268
}
6369

tockloader-cli/src/main.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use anyhow::{Context, Result};
1010
use clap::ArgMatches;
1111
use cli::make_cli;
1212
use known_boards::KnownBoardNames;
13+
use tockloader_lib::attributes::app_attributes::AppOption;
1314
use tockloader_lib::board_settings::BoardSettings;
1415
use tockloader_lib::connection::{
1516
Connection, ProbeRSConnection, ProbeTargetInfo, SerialConnection, SerialTargetInfo,
@@ -19,7 +20,7 @@ use tockloader_lib::known_boards::KnownBoard;
1920
use tockloader_lib::tabs::tab::Tab;
2021
use tockloader_lib::{
2122
list_debug_probes, list_serial_ports, CommandEraseApps, CommandInfo, CommandInstall,
22-
CommandList,
23+
CommandList, CommandUninstall,
2324
};
2425

2526
fn get_serial_target_info(user_options: &ArgMatches) -> SerialTargetInfo {
@@ -240,6 +241,63 @@ async fn main() -> Result<()> {
240241

241242
conn.erase_apps().await.context("Failed to erase apps.")?;
242243
}
244+
Some(("uninstall", sub_matches)) => {
245+
cli::validate(&mut cmd, sub_matches);
246+
let mut conn = open_connection(sub_matches).await?;
247+
let installed_apps = conn.list().await.context("Failed to list apps.")?;
248+
let app_name = sub_matches.get_one::<String>("name").map(String::as_str);
249+
250+
if installed_apps.is_empty() {
251+
println!("No apps installed");
252+
return Ok(());
253+
}
254+
match app_name {
255+
Some(app_name) => {
256+
installed_apps
257+
.iter()
258+
.find(|iter| iter.tbf_header.get_package_name().unwrap_or("") == app_name)
259+
.expect("Specified app is not installed");
260+
conn.uninstall_app(Some(app_name.to_string()), None)
261+
.await
262+
.context("Failed to uninstall app.")?;
263+
}
264+
None => loop {
265+
let mut options: Vec<AppOption> = installed_apps
266+
.iter()
267+
.enumerate()
268+
.map(|(i, app)| AppOption { index: i + 1, app })
269+
.collect();
270+
271+
// Delete all option
272+
options.insert(
273+
0,
274+
AppOption {
275+
index: 0,
276+
app: &installed_apps[0],
277+
},
278+
);
279+
let selected =
280+
inquire::Select::new("Which app do you want to uninstall?", options)
281+
.prompt()
282+
.context("No apps installed")
283+
.unwrap();
284+
285+
if inquire::Select::new(
286+
format!("You chose {selected}",).as_str(),
287+
["Cancel", "Confirm"].to_vec(),
288+
)
289+
.prompt()
290+
.unwrap()
291+
== "Confirm"
292+
{
293+
conn.uninstall_app(None, Some(selected.index))
294+
.await
295+
.context("Failed to uninstall app")?;
296+
break;
297+
}
298+
},
299+
}
300+
}
243301
_ => {
244302
println!("Could not run the provided subcommand.");
245303
_ = make_cli().print_help();

tockloader-lib/src/attributes/app_attributes.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,35 @@ pub struct AppAttributes {
2121
pub tbf_footers: Vec<TbfFooter>,
2222
}
2323

24+
/// This structure is used for displaying installed apps in the CLI
25+
#[derive(Debug)]
26+
pub struct AppOption<'a> {
27+
pub index: usize,
28+
pub app: &'a AppAttributes,
29+
}
30+
31+
impl<'a> std::fmt::Display for AppOption<'a> {
32+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33+
if self.index == 0 {
34+
write!(f, "Delete all")
35+
} else {
36+
write!(
37+
f,
38+
"{}. {} - start: {:#x}, size: {}, type: {}",
39+
self.index,
40+
self.app.tbf_header.get_package_name().unwrap_or(""),
41+
self.app.address,
42+
self.app.tbf_header.total_size(),
43+
if self.app.tbf_header.get_fixed_address_flash().is_none() {
44+
"C (flexible)"
45+
} else {
46+
"Rust (fixed)"
47+
}
48+
)
49+
}
50+
}
51+
}
52+
2453
/// This structure represents a footer of a Tock application. Currently, footers
2554
/// only contain credentials, which are used to verify the integrity of the
2655
/// application.

tockloader-lib/src/command_impl/install.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@ impl CommandInstall for TockloaderConnection {
2121
// obtain the binaries in a vector
2222
let mut app_binaries: Vec<Vec<u8>> = Vec::new();
2323

24-
let mut address = settings.start_address;
2524
for app in app_attributes_list.iter() {
25+
let address = app.address;
2626
app_binaries.push(
2727
self.read(address, app.tbf_header.total_size() as usize)
2828
.await
2929
.unwrap(),
3030
);
31-
address += app.tbf_header.total_size() as u64;
3231
}
3332

3433
let app = TockApp::from_tab(&tab, &settings).unwrap();

tockloader-lib/src/command_impl/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pub mod list;
66
pub mod probers;
77
pub mod reshuffle_apps;
88
pub mod serial;
9+
pub mod uninstall;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use async_trait::async_trait;
2+
3+
use crate::{
4+
attributes::app_attributes::AppAttributes,
5+
command_impl::reshuffle_apps::{create_pkt, reshuffle_apps, TockApp},
6+
connection::{Connection, TockloaderConnection},
7+
errors::{InternalError, TockloaderError},
8+
CommandEraseApps, CommandList, CommandUninstall, IO,
9+
};
10+
11+
#[async_trait]
12+
impl CommandUninstall for TockloaderConnection {
13+
async fn uninstall_app(
14+
&mut self,
15+
app_name: Option<String>,
16+
app_index: Option<usize>,
17+
) -> Result<(), TockloaderError> {
18+
let settings = self.get_settings();
19+
20+
let mut app_attributes_list: Vec<AppAttributes> = self.list().await?;
21+
22+
// Remove all apps with given name
23+
if let Some(name) = app_name {
24+
let _ = app_attributes_list
25+
.retain(|app| app.tbf_header.get_package_name().unwrap_or("") != name);
26+
} else if let Some(index) = app_index {
27+
// Delete all apps, call erase
28+
if index == 0 {
29+
self.erase_apps().await?;
30+
return Ok(());
31+
}
32+
// Remove the selected index
33+
app_attributes_list.remove(index - 1);
34+
} else {
35+
panic!("Called uninstall with wrong parameters!");
36+
}
37+
38+
let tock_app_list = app_attributes_list
39+
.iter()
40+
.map(TockApp::from_app_attributes)
41+
.collect::<Vec<TockApp>>();
42+
43+
// obtain the binaries in a vector
44+
let mut app_binaries: Vec<Vec<u8>> = Vec::new();
45+
46+
for app in app_attributes_list.iter() {
47+
let address = app.address;
48+
app_binaries.push(
49+
self.read(address, app.tbf_header.total_size() as usize)
50+
.await
51+
.unwrap(),
52+
);
53+
}
54+
55+
let configuration =
56+
reshuffle_apps(&settings, tock_app_list).ok_or(TockloaderError::Internal(
57+
InternalError::MisconfiguredBoardSettings("Can't fit new app".to_string()),
58+
))?;
59+
60+
// create the pkt, this contains all the binaries in a vec
61+
let mut pkt = create_pkt(configuration, app_binaries, None, &settings);
62+
63+
pkt.append(&mut [0u8; 512].to_vec());
64+
65+
log::debug!("pkt len {}", pkt.len());
66+
// write the pkt
67+
let _ = self.write(settings.start_address, &pkt).await?;
68+
Ok(())
69+
}
70+
}

tockloader-lib/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ pub trait CommandInstall {
5050
async fn install_app(&mut self, tab_file: Tab) -> Result<(), TockloaderError>;
5151
}
5252

53+
#[async_trait]
54+
pub trait CommandUninstall {
55+
/// This function is used for uninstalling apps
56+
/// - app_name is Some(value) if --name is used, otherwise it is None
57+
/// - app_index is Some(value) if the app is chosen from the list, None otherwise
58+
///
59+
/// There is no scenario in which both are Some(value) or None
60+
async fn uninstall_app(
61+
&mut self,
62+
app_name: Option<String>,
63+
app_index: Option<usize>,
64+
) -> Result<(), TockloaderError>;
65+
}
66+
5367
#[async_trait]
5468
pub trait CommandEraseApps {
5569
async fn erase_apps(&mut self) -> Result<(), TockloaderError>;

0 commit comments

Comments
 (0)