Few bug fixes, minor improvements and cleanup.
- Container creation in sync module now only syncs targets as required - More robust validation for --target parameters - Targets marked for reinitialization can now accept a configuration from a file if specified. - Cleaned up process module - Prompt module no longer emboldens listed targets for confirmation
This commit is contained in:
parent
68594a28e7
commit
264e9cec1f
11 changed files with 148 additions and 128 deletions
|
@ -56,17 +56,18 @@ If a feature you see here is not completed, feel free to submit a PR; or submit
|
|||
| Launch within existing namespace | Not yet implemented | ❌ |
|
||||
| Container Configuration | Functional | ✅ |
|
||||
| Container Creation | Functional | ✅ |
|
||||
| Container Composition | Functional | ✅ |
|
||||
| Container Runtime | Embedded runtime environment | ✅ |
|
||||
| Container Schema | Container filesystem schema with version tracking | ✅ |
|
||||
| Filesystem Deduplication | Retains filesystem state across containers with hardlinks | ✅ |
|
||||
| Seccomp Filters | Application of seccomp filters to instances via libseccomp bindings | ✅ |
|
||||
| Dbus Isolation | Functional - provided by xdg-dbus-proxy | ✅ |
|
||||
| Networking Isolation | Not yet implemented | ❌ |
|
||||
| Port to Rust | Script: pacwrap-utils | ⚠ |
|
||||
| Configuration CLI (user friendly) | Not yet implemented | ❌ |
|
||||
| Port to Rust | Completed | ✅ |
|
||||
| Config CLI (user friendly) | Not yet implemented | ❌ |
|
||||
| Process API | Container process enumeration | ✅ |
|
||||
| Process CLI | Functional | ✅ |
|
||||
| Utility CLI (native) | Not yet implemented | ❌ |
|
||||
| Utility CLI | Functional | ✅ |
|
||||
| Localization | Not yet implemented | ❌ |
|
||||
|
||||
## Manual
|
||||
|
|
|
@ -22,7 +22,7 @@ use std::{
|
|||
io::{BufRead, BufReader, Read, Seek, SeekFrom},
|
||||
};
|
||||
|
||||
use crate::{config::ContainerCache, constants::CONTAINER_DIR, err, utils::print_warning, Error, ErrorKind};
|
||||
use crate::{config::ContainerCache, constants::CONTAINER_DIR, utils::print_warning, Error, ErrorGeneric};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub struct ProcessList {
|
||||
|
@ -95,18 +95,9 @@ impl Process {
|
|||
}
|
||||
|
||||
pub fn exec(&self) -> &str {
|
||||
let mut index = 0;
|
||||
|
||||
for char in self.cmd[0].char_indices().rev() {
|
||||
if char.1 == '/' {
|
||||
index = char.0 + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match index {
|
||||
0 => &self.cmd[0],
|
||||
_ => &self.cmd[0].split_at(index).1,
|
||||
match self.cmd[0].char_indices().filter(|c| c.1 == '/').last() {
|
||||
Some((index, ..)) => &self.cmd[0].split_at(index + 1).1,
|
||||
None => &self.cmd[0],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,10 +148,7 @@ impl ProcStat {
|
|||
|
||||
Some(Self {
|
||||
thread_name: stat[1].into(),
|
||||
parent: match stat[3].parse() {
|
||||
Ok(val) => val,
|
||||
Err(_) => 1,
|
||||
},
|
||||
parent: stat[3].parse().unwrap_or(1),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -192,13 +180,10 @@ pub fn list<'a>(cache: &'a ContainerCache<'a>) -> Result<ProcessList, Error> {
|
|||
}
|
||||
|
||||
let check = qualify_process(&cmdlist, stat.parent(), &map);
|
||||
let check = match check {
|
||||
let (ins, depth, fork) = match check {
|
||||
Some(instance) => instance,
|
||||
None => continue,
|
||||
};
|
||||
let ins = check.0;
|
||||
let depth = check.1;
|
||||
let fork = check.2;
|
||||
|
||||
match groups.get_mut(&ins) {
|
||||
Some(vec) => vec.push(pid),
|
||||
|
@ -218,21 +203,14 @@ pub fn list<'a>(cache: &'a ContainerCache<'a>) -> Result<ProcessList, Error> {
|
|||
}
|
||||
|
||||
fn procfs() -> Result<Vec<i32>, Error> {
|
||||
match read_dir("/proc/") {
|
||||
Ok(dir) => Ok(dir
|
||||
.filter_map(|s| match s {
|
||||
Ok(f) => match f.file_name().to_str() {
|
||||
Some(str) => match str.parse() {
|
||||
Ok(i) => Some(i),
|
||||
Err(_) => None,
|
||||
},
|
||||
None => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect()),
|
||||
Err(error) => err!(ErrorKind::IOError("/proc/".into(), error.kind())),
|
||||
}
|
||||
Ok(read_dir("/proc/")
|
||||
.prepend_io(|| "/proc/".into())?
|
||||
.filter(|s| s.as_ref().is_ok_and(|s| s.metadata().is_ok_and(|m| m.is_dir())))
|
||||
.filter_map(|e| match e.unwrap().file_name().to_str().unwrap().parse() {
|
||||
Ok(val) => Some(val),
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn cmdlist(pid: i32) -> Option<Vec<String>> {
|
||||
|
@ -251,10 +229,7 @@ fn cmdlist(pid: i32) -> Option<Vec<String>> {
|
|||
}
|
||||
|
||||
data.remove(len - 1);
|
||||
cmdlist.push(match String::from_utf8(data) {
|
||||
Ok(string) => string,
|
||||
Err(_) => "".into(),
|
||||
});
|
||||
cmdlist.push(String::from_utf8(data).unwrap_or_default());
|
||||
index += len;
|
||||
|
||||
match list.seek(SeekFrom::Start(index as u64)) {
|
||||
|
|
|
@ -424,6 +424,27 @@ impl SyncType {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn validate_fs_states<'a>(vec: &'a Vec<&'a str>) -> bool {
|
||||
for instance in vec {
|
||||
match check(instance) {
|
||||
Ok(bool) =>
|
||||
if bool {
|
||||
return false;
|
||||
},
|
||||
Err(err) => {
|
||||
err.warn();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn create_blank_state(container: &str) -> Result<(), Error> {
|
||||
serialize(container.into(), FileSystemState::new())
|
||||
}
|
||||
|
||||
fn deserialize<R: Read, T: for<'de> Deserialize<'de>>(instance: &str, reader: R) -> Result<T, Error> {
|
||||
match bincode::options()
|
||||
.with_fixint_encoding()
|
||||
|
@ -511,23 +532,6 @@ fn check(instance: &str) -> Result<bool, Error> {
|
|||
Ok(magic != MAGIC_NUMBER || version != VERSION)
|
||||
}
|
||||
|
||||
pub fn invalid_fs_states<'a>(vec: &'a Vec<&'a str>) -> bool {
|
||||
for instance in vec {
|
||||
match check(instance) {
|
||||
Ok(bool) =>
|
||||
if bool {
|
||||
return true;
|
||||
},
|
||||
Err(err) => {
|
||||
err.warn();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn previous_state(map: Vec<Option<FileSystemState>>) -> FileSystemState {
|
||||
let mut state = FileSystemState::new();
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ use crate::{
|
|||
log::Logger,
|
||||
sync::{
|
||||
self,
|
||||
filesystem::{invalid_fs_states, FileSystemStateSync},
|
||||
filesystem::{validate_fs_states, FileSystemStateSync},
|
||||
transaction::{Transaction, TransactionFlags, TransactionHandle, TransactionMetadata, TransactionState, TransactionType},
|
||||
SyncError,
|
||||
},
|
||||
|
@ -106,7 +106,6 @@ impl<'a> TransactionAggregator<'a> {
|
|||
}
|
||||
TransactionType::Remove(..) => self.targets.is_some(),
|
||||
};
|
||||
|
||||
let upstream = match self.targets.as_ref() {
|
||||
Some(targets) => self.cache.filter_target(targets, vec![ContainerType::Base, ContainerType::Slice]),
|
||||
None => self.cache.filter(vec![ContainerType::Base, ContainerType::Slice]),
|
||||
|
@ -117,7 +116,7 @@ impl<'a> TransactionAggregator<'a> {
|
|||
};
|
||||
let are_downstream = self.cache.count(vec![ContainerType::Aggregate]) > 0;
|
||||
|
||||
if invalid_fs_states(&upstream) && are_downstream {
|
||||
if !validate_fs_states(&upstream) && are_downstream {
|
||||
let linker = self.fs_sync().unwrap();
|
||||
|
||||
linker.refresh_state();
|
||||
|
|
|
@ -58,13 +58,13 @@ fn create_prompt(message: String, prefix: &str, yn_prompt: bool) -> Result<Strin
|
|||
return Input::with_theme(&theme).with_prompt(message).allow_empty(true).interact_text();
|
||||
}
|
||||
|
||||
pub fn prompt_targets(targets: &Vec<&str>, ins_prompt: impl Into<String>, yn_prompt: bool) -> Result<(), ()> {
|
||||
pub fn prompt_targets(targets: &Vec<&str>, ins_prompt: &str, yn_prompt: bool) -> Result<(), ()> {
|
||||
eprintln!("{} {}Container{}{}\n", *BAR_RED, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET);
|
||||
|
||||
for target in targets.iter() {
|
||||
eprint!("{}{}{} ", *BOLD, target, *RESET);
|
||||
eprint!("{} ", target);
|
||||
}
|
||||
|
||||
eprintln!("\n");
|
||||
prompt("::", ins_prompt, yn_prompt)
|
||||
prompt("::", format!("{}{}", *BOLD, ins_prompt), yn_prompt)
|
||||
}
|
||||
|
|
|
@ -77,9 +77,9 @@ fn delete_containers<'a>(
|
|||
let message = format!("Deleting existing container{}?", if delete.len() > 1 { "s" } else { "" });
|
||||
|
||||
if flags.contains(TransactionFlags::NO_CONFIRM) {
|
||||
println!("{} {}{}...{}", *BAR_GREEN, *BOLD, message, *RESET);
|
||||
println!("{} {}{}...{}", *BAR_GREEN, *BOLD, &message, *RESET);
|
||||
delete_roots(cache, logger, delete, force)?;
|
||||
} else if let Ok(_) = prompt_targets(&delete, message, false) {
|
||||
} else if let Ok(_) = prompt_targets(&delete, &message, false) {
|
||||
delete_roots(cache, logger, delete, force)?;
|
||||
}
|
||||
|
||||
|
@ -153,9 +153,8 @@ fn engage_aggregator<'a>(args: &mut Arguments) -> Result<()> {
|
|||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Op::Short('t') | Op::Long("target") | Op::Long("from-config") => continue,
|
||||
Op::Long("from-config") => continue,
|
||||
Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM,
|
||||
Op::Long("force") => force = true,
|
||||
Op::Long("reinitialize-all") =>
|
||||
for instance in cache.registered() {
|
||||
if let Some(handle) = cache.get_instance_option(instance) {
|
||||
|
@ -166,32 +165,43 @@ fn engage_aggregator<'a>(args: &mut Arguments) -> Result<()> {
|
|||
compose.insert(instance, None);
|
||||
}
|
||||
},
|
||||
Op::Short('f') | Op::Long("force") => force = true,
|
||||
Op::Short('r') | Op::Long("reinitialize") => reinitialize = true,
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) =>
|
||||
if !reinitialize {
|
||||
current_target = Some(target);
|
||||
} else {
|
||||
Op::Short('t') | Op::Long("target") => match args.next() {
|
||||
Some(arg) => match arg {
|
||||
Op::ShortPos('t', t) | Op::LongPos("target", t) => current_target = Some(t),
|
||||
_ => args.invalid_operand()?,
|
||||
},
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
Op::LongPos(_, config) | Op::ShortPos(_, config) | Op::Value(config) => {
|
||||
let target = match current_target {
|
||||
Some(target) => target,
|
||||
None => match config.char_indices().filter(|a| a.1 == '.').last() {
|
||||
Some((index, ..)) => config.split_at(index).0,
|
||||
None => config,
|
||||
},
|
||||
};
|
||||
let config = if reinitialize {
|
||||
let handle = cache.get_instance(target)?;
|
||||
|
||||
if Path::new(handle.vars().root()).exists() {
|
||||
delete.push(target);
|
||||
}
|
||||
|
||||
compose.insert(target, None);
|
||||
reinitialize = false;
|
||||
},
|
||||
Op::LongPos(_, target) | Op::ShortPos(_, target) | Op::Value(target) => {
|
||||
if !target.ends_with(".yml") {
|
||||
err!(ErrorKind::Message("Unsupported file extension."))?;
|
||||
}
|
||||
|
||||
if let Some(cur_target) = current_target {
|
||||
compose.insert(cur_target, Some(target));
|
||||
current_target = None;
|
||||
} else {
|
||||
Path::new(target).try_exists().prepend_io(|| target.into())?;
|
||||
compose.insert(target.split_at(target.len() - 4).0, Some(target));
|
||||
}
|
||||
|
||||
match current_target {
|
||||
Some(_) => Some(config),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
Some(config)
|
||||
};
|
||||
|
||||
compose.insert(target, config);
|
||||
current_target = None;
|
||||
reinitialize = false;
|
||||
}
|
||||
_ => args.invalid_operand()?,
|
||||
}
|
||||
|
|
|
@ -358,7 +358,7 @@ fn create_placeholder(path: &str) -> Result<()> {
|
|||
fn cleanup() -> Result<()> {
|
||||
if Path::new(&*DBUS_SOCKET).exists() {
|
||||
remove_file(&*DBUS_SOCKET).prepend_io(|| DBUS_SOCKET.to_string())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@ use std::{
|
|||
str::FromStr,
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use nix::{
|
||||
sys::signal::{kill, Signal},
|
||||
unistd::Pid,
|
||||
};
|
||||
use pacwrap_core::{
|
||||
config::cache,
|
||||
constants::{ARROW_GREEN, BOLD, RESET},
|
||||
constants::{ARROW_GREEN, BOLD, DIM, RESET},
|
||||
err,
|
||||
impl_error,
|
||||
process::{self, Process},
|
||||
|
@ -243,6 +244,7 @@ fn process_kill(args: &mut Arguments) -> Result<()> {
|
|||
err!(InvalidArgument::TargetUnspecified)?
|
||||
}
|
||||
|
||||
let mut instances = IndexMap::new();
|
||||
let cache = cache::populate()?;
|
||||
let list = process::list(&cache)?;
|
||||
let list = match all {
|
||||
|
@ -265,11 +267,18 @@ fn process_kill(args: &mut Arguments) -> Result<()> {
|
|||
if process.fork() {
|
||||
fork_warn(process);
|
||||
}
|
||||
|
||||
match instances.get(process.instance()) {
|
||||
Some(value) => instances.insert(process.instance(), value + 1),
|
||||
None => instances.insert(process.instance(), 1),
|
||||
};
|
||||
}
|
||||
|
||||
let instances: Vec<String> = instances.iter().map(|a| format!("{} ({}{}{})", a.0, *DIM, a.1, *RESET)).collect();
|
||||
|
||||
if no_confirm {
|
||||
kill_processes(&list, sigint)
|
||||
} else if let Ok(_) = prompt_targets(&list.iter().map(|a| a.instance()).collect(), "Kill processes?", false) {
|
||||
} else if let Ok(_) = prompt_targets(&instances.iter().map(|a| a.as_ref()).collect(), "Kill container processes?", false) {
|
||||
kill_processes(&list, sigint)
|
||||
} else {
|
||||
Ok(())
|
||||
|
|
|
@ -26,7 +26,7 @@ use pacwrap_core::{
|
|||
log::Logger,
|
||||
sync::transaction::{TransactionAggregator, TransactionFlags, TransactionType},
|
||||
utils::{
|
||||
arguments::{Arguments, InvalidArgument, Operand as Op},
|
||||
arguments::{Arguments, InvalidArgument::*, Operand as Op},
|
||||
check_root,
|
||||
},
|
||||
};
|
||||
|
@ -62,7 +62,7 @@ fn engage_aggregator<'a>(action_type: TransactionType, args: &'a mut Arguments,
|
|||
let mut current_target = None;
|
||||
|
||||
if let Op::Nothing = args.next().unwrap_or_default() {
|
||||
err!(InvalidArgument::OperationUnspecified)?
|
||||
err!(OperationUnspecified)?
|
||||
}
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
|
@ -72,18 +72,23 @@ fn engage_aggregator<'a>(action_type: TransactionType, args: &'a mut Arguments,
|
|||
| Op::Long("recursive")
|
||||
| Op::Short('R')
|
||||
| Op::Short('c')
|
||||
| Op::Short('s')
|
||||
| Op::Short('t') => continue,
|
||||
| Op::Short('s') => continue,
|
||||
Op::Long("dbonly") => flags = flags | TransactionFlags::DATABASE_ONLY,
|
||||
Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM,
|
||||
Op::Long("force-foreign") => flags = flags | TransactionFlags::FORCE_DATABASE,
|
||||
Op::Short('p') | Op::Long("preview") => flags = flags | TransactionFlags::PREVIEW,
|
||||
Op::Short('f') | Op::Long("filesystem") => flags = flags | TransactionFlags::FILESYSTEM_SYNC,
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => {
|
||||
cache.get_instance(target)?;
|
||||
current_target = Some(target);
|
||||
targets.push(target);
|
||||
}
|
||||
Op::Short('t') | Op::Long("target") => match args.next() {
|
||||
Some(arg) => match arg {
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => {
|
||||
cache.get_instance(target)?;
|
||||
current_target = Some(target);
|
||||
targets.push(target);
|
||||
}
|
||||
_ => args.invalid_operand()?,
|
||||
},
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
Op::LongPos(_, package) | Op::ShortPos(_, package) | Op::Value(package) =>
|
||||
if let Some(target) = current_target {
|
||||
match queue.get_mut(target) {
|
||||
|
@ -98,7 +103,7 @@ fn engage_aggregator<'a>(action_type: TransactionType, args: &'a mut Arguments,
|
|||
}
|
||||
|
||||
if let None = current_target {
|
||||
err!(InvalidArgument::TargetUnspecified)?
|
||||
err!(TargetUnspecified)?
|
||||
}
|
||||
|
||||
Ok(TransactionAggregator::new(&cache, log, action_type)
|
||||
|
|
|
@ -117,12 +117,18 @@ fn acquire_depends<'a>(args: &mut Arguments<'a>) -> Result<IndexMap<&'a str, (Co
|
|||
}
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => match instype {
|
||||
Some(instype) => {
|
||||
current_target = target;
|
||||
deps.insert(current_target, (instype, vec![]));
|
||||
}
|
||||
None => err!(ErrorKind::Message("Container type not specified."))?,
|
||||
Op::Short('t') | Op::Long("target") => match args.next() {
|
||||
Some(arg) => match arg {
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => match instype {
|
||||
Some(instype) => {
|
||||
current_target = target;
|
||||
deps.insert(current_target, (instype, vec![]));
|
||||
}
|
||||
None => err!(ErrorKind::Message("Container type not specified."))?,
|
||||
},
|
||||
_ => args.invalid_operand()?,
|
||||
},
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
|
@ -224,14 +230,12 @@ fn engage_aggregator<'a>(
|
|||
Op::Short('a')
|
||||
| Op::Short('s')
|
||||
| Op::Short('d')
|
||||
| Op::Short('t')
|
||||
| Op::Short('y')
|
||||
| Op::Short('u')
|
||||
| Op::Short('c')
|
||||
| Op::Long("aggregate")
|
||||
| Op::Long("slice")
|
||||
| Op::Long("dep")
|
||||
| Op::Long("target")
|
||||
| Op::Long("refresh")
|
||||
| Op::Long("upgrade")
|
||||
| Op::Long("create")
|
||||
|
@ -243,16 +247,22 @@ fn engage_aggregator<'a>(
|
|||
Op::Long("dbonly") => flags = flags | TransactionFlags::DATABASE_ONLY,
|
||||
Op::Long("force-foreign") => flags = flags | TransactionFlags::FORCE_DATABASE,
|
||||
Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM,
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => {
|
||||
cache.get_instance(target)?;
|
||||
current_target = target;
|
||||
targets.insert(target);
|
||||
Op::Short('t') | Op::Long("target") => match args.next() {
|
||||
Some(arg) => match arg {
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => {
|
||||
cache.get_instance(target)?;
|
||||
current_target = target;
|
||||
targets.insert(target);
|
||||
|
||||
if base {
|
||||
queue.insert(current_target.into(), vec!["base"]);
|
||||
base = false;
|
||||
}
|
||||
}
|
||||
if base {
|
||||
queue.insert(current_target.into(), vec!["base"]);
|
||||
base = false;
|
||||
}
|
||||
}
|
||||
_ => args.invalid_operand()?,
|
||||
},
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
Op::LongPos(_, package) | Op::ShortPos(_, package) | Op::Value(package) =>
|
||||
if current_target != "" {
|
||||
if let Some(vec) = queue.get_mut(current_target) {
|
||||
|
@ -265,18 +275,18 @@ fn engage_aggregator<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
if flags.contains(TransactionFlags::CREATE) {
|
||||
for cache in cache.registered_handles().iter().filter(|a| a.is_creation()) {
|
||||
targets.extend(cache.metadata().dependencies());
|
||||
}
|
||||
}
|
||||
|
||||
let targets: Option<Vec<&str>> = match flags.contains(TransactionFlags::TARGET_ONLY) {
|
||||
let targets: Option<Vec<&str>> = match flags.intersects(TransactionFlags::TARGET_ONLY | TransactionFlags::CREATE) {
|
||||
true => {
|
||||
if current_target == "" && !flags.contains(TransactionFlags::FILESYSTEM_SYNC) {
|
||||
err!(TargetUnspecified)?
|
||||
}
|
||||
|
||||
if flags.contains(TransactionFlags::CREATE) {
|
||||
for cache in cache.registered_handles().iter().filter(|a| a.is_creation()) {
|
||||
targets.extend(cache.metadata().dependencies());
|
||||
}
|
||||
}
|
||||
|
||||
match flags.contains(TransactionFlags::FILESYSTEM_SYNC) {
|
||||
false => Some(targets.into_iter().collect()),
|
||||
true => None,
|
||||
|
|
|
@ -20,15 +20,17 @@
|
|||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
fs::remove_dir_all,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use pacwrap_core::{
|
||||
config::{cache, ContainerCache},
|
||||
constants::{ARROW_GREEN, BOLD, RESET},
|
||||
constants::{ARROW_GREEN, BOLD, DATA_DIR, RESET},
|
||||
err,
|
||||
impl_error,
|
||||
log::{Level::Info, Logger},
|
||||
process,
|
||||
sync::filesystem::create_blank_state,
|
||||
utils::{arguments::Operand, prompt::prompt_targets, Arguments},
|
||||
Error,
|
||||
ErrorGeneric,
|
||||
|
@ -58,7 +60,7 @@ pub fn remove_containers(args: &mut Arguments) -> Result<()> {
|
|||
let mut targets = vec![];
|
||||
let mut no_confirm = false;
|
||||
let mut force = false;
|
||||
let mut logger = Logger::new("pacwrap-utils");
|
||||
let mut logger = Logger::new("pacwrap-utils").init()?;
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
|
@ -103,8 +105,13 @@ pub fn delete_roots(cache: &ContainerCache<'_>, logger: &mut Logger, targets: &V
|
|||
for container in containers {
|
||||
let root = container.vars().root();
|
||||
let instance = container.vars().instance();
|
||||
|
||||
let state = &format!("{}/state/{instance}.dat", *DATA_DIR);
|
||||
remove_dir_all(root).prepend(|| format!("Failed to delete container root '{root}':"))?;
|
||||
|
||||
if Path::new(state).exists() {
|
||||
create_blank_state(instance)?;
|
||||
}
|
||||
|
||||
eprintln!("{} Deleted container {}{}{} successfully.", *ARROW_GREEN, *BOLD, instance, *RESET);
|
||||
logger.log(Info, &format!("Deleted container {instance}"))?;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue