From 91b5c79c5a28f6a91015edcbfeb42925a2a649aa Mon Sep 17 00:00:00 2001 From: Xavier Date: Sat, 30 Mar 2024 21:36:06 -0400 Subject: [PATCH] Further documentation and overhaul of the help menu CLI - Shields.io badges added to README.md with clarification on functionality present in pacwrap. - Stipulate the requirement for libalpm 14 or greater in README.md - Module documentation written in markdown located in /docs/ for filesystems and permissions modules - Help CLI refactored with format improvements and fixes. - Lock assert calls for container composition, creation, and remote sync - Added value opperand for process module in main frontend module --- README.md | 15 +- docs/dbus/README.md | 13 + docs/filesystems/README.md | 10 + docs/filesystems/home.md | 19 + docs/filesystems/root.md | 19 + docs/filesystems/to_home.md | 27 ++ docs/filesystems/to_root.md | 28 ++ docs/modules/README.md | 13 + docs/modules/dev.md | 22 ++ docs/modules/display.md | 14 + docs/modules/env.md | 25 ++ docs/modules/gpu.md | 14 + docs/modules/net.md | 14 + docs/modules/pipewire.md | 14 + docs/modules/pulseaudio.md | 14 + pacwrap-core/src/sync.rs | 9 +- pacwrap/README.md | 15 +- pacwrap/src/compose.rs | 13 +- pacwrap/src/help.rs | 306 +++++++++++++++ pacwrap/src/help/config.rs | 160 ++++++++ pacwrap/src/help/manual.rs | 641 +++++++++++++++++++++++++++++++ pacwrap/src/help/version.rs | 73 ++++ pacwrap/src/main.rs | 10 +- pacwrap/src/manual.rs | 741 ------------------------------------ pacwrap/src/sync.rs | 4 +- pacwrap/src/utils/delete.rs | 5 +- 26 files changed, 1476 insertions(+), 762 deletions(-) create mode 100644 docs/dbus/README.md create mode 100644 docs/filesystems/README.md create mode 100644 docs/filesystems/home.md create mode 100644 docs/filesystems/root.md create mode 100644 docs/filesystems/to_home.md create mode 100644 docs/filesystems/to_root.md create mode 100644 docs/modules/dev.md create mode 100644 docs/modules/display.md create mode 100644 docs/modules/env.md create mode 100644 docs/modules/gpu.md create mode 100644 docs/modules/net.md create mode 100644 docs/modules/pipewire.md create mode 100644 docs/modules/pulseaudio.md create mode 100644 pacwrap/src/help.rs create mode 100644 pacwrap/src/help/config.rs create mode 100644 pacwrap/src/help/manual.rs create mode 100644 pacwrap/src/help/version.rs delete mode 100644 pacwrap/src/manual.rs diff --git a/README.md b/README.md index 044d3ca..fcfb517 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # pacwrap +[![pacwrap](https://shields.io/aur/version/pacwrap?style=for-the-badge&color=599ffb&archlinux&label=pacwrap)](https://aur.archlinux.org/packages/pacwrap/) +[![pacwrap-git](https://shields.io/aur/version/pacwrap-git?style=for-the-badge&color=599ffb&logo=archlinux&label=pacwrap-git)](https://aur.archlinux.org/packages/pacwrap-git/) +[![License](https://shields.io/crates/l/pacwrap/0.8.0?style=for-the-badge&color=6dfb59)](https://spdx.org/licenses/GPL-3.0-only.html) +![MSRV](https://shields.io/crates/msrv/pacwrap/0.8.0?style=for-the-badge&color=fba759) + A package management front-end which utilises libalpm to facilitate the creation of unprivileged, userspace containers with parallelised, filesystem-agnostic deduplication. These containers are constructed via bubblewrap to execute package transactions and launch applications. @@ -30,6 +35,12 @@ And finally, to install ```neovim``` inside of a fresh, aggregated container cal $ pacwrap -Syucat editor --dep=base neovim ``` +To update these containers just created in aggregate: + +``` +$ pacwrap -Syu +``` + More advanced examples along with further documentation of configuration can be found further elaborated upon **[here](./docs/)**. @@ -45,7 +56,7 @@ If a feature you see here is not completed, feel free to submit a PR; or submit | Transaction Agent | Transact within a sandboxed runtime environment | ✅ | | Transaction CLI | Functional | ✅ | | Global Configuration | Functional | ✅ | -| Dependency Resolution | Functional, but too liberal to compensate for a lack of conflict resolution | ⚠ | +| Package Dependency Resolution | Utilizes a recursive first-depth search algorithm; resilient to cycling | ✅ | | Foreign Database Resolution | Populates foreign package database in aggregate containers | ✅ | | Foreign Database Resolution (Lazy) | Not yet implemented | ❌ | | Conflict Resolution | Not yet implemented | ❌ | @@ -78,7 +89,7 @@ An online version of the user manual is viewable **[here](./docs/manual.md)**. A minimum version of Rust 1.72 is required to build with the following libraries fulfilled by your distribution: ``` -libalpm, libseccomp, libzstd +libalpm>=14, libseccomp, libzstd ``` ## Packaging requirements diff --git a/docs/dbus/README.md b/docs/dbus/README.md new file mode 100644 index 0000000..3947c9a --- /dev/null +++ b/docs/dbus/README.md @@ -0,0 +1,13 @@ +# Modules + +This directory contains brief documentation along with examples for each permission module. + +### Table of Contents + +- [Devices](./dev.md) +- [Display Servers](./display.md) +- [Environment Variables](./env.md) +- [Graphics Devices](./gpu.md) +- [Networking](./gpu.md) +- [Pipewire](./pipewire.md) +- [PulseAudio](./pulseaudio.md) diff --git a/docs/filesystems/README.md b/docs/filesystems/README.md new file mode 100644 index 0000000..1061856 --- /dev/null +++ b/docs/filesystems/README.md @@ -0,0 +1,10 @@ +# Filesystem Modules + +This directory contains brief documentation along with examples for each filesystem module. + +### Table of Contents + +- [Home](./home.md) +- [Root](./root.md) +- [Mount to root](./to_home.md) +- [Mount to home](./to_root.md) diff --git a/docs/filesystems/home.md b/docs/filesystems/home.md new file mode 100644 index 0000000..a7bafdc --- /dev/null +++ b/docs/filesystems/home.md @@ -0,0 +1,19 @@ +# Mount home + +Mount the container's home directory into the container. + +## Example + +``` +filesystems: +- mount: home +``` + +## Description + +Provides the binding for the container's home directory. By default, this module +will mount `~/.local/share/pacwrap/home/[container_name]` to `$HOME` inside the +container at `/home/[container_name]`. Please refer to the [**to_home**](./to_home.md) +module for more advanced options. + +This module plays an important role in initializing the container's runtime environment. diff --git a/docs/filesystems/root.md b/docs/filesystems/root.md new file mode 100644 index 0000000..dbdd805 --- /dev/null +++ b/docs/filesystems/root.md @@ -0,0 +1,19 @@ +# Mount root + +Mount the container's home directory into the container. + +## Example + +``` +filesystems: +- mount: root +``` + +## Description + +Provides the binding for the container's root filesystem. By default, this module +will bind the minimum required at `~/.local/share/pacwrap/root/[container_name]` +to allow for a functional userspace inside of the container. Please refer to the +[**to_root**](./to_root.md) module for more advanced options. + +This module plays an important role in initializing the container's runtime environment. diff --git a/docs/filesystems/to_home.md b/docs/filesystems/to_home.md new file mode 100644 index 0000000..1d1efd3 --- /dev/null +++ b/docs/filesystems/to_home.md @@ -0,0 +1,27 @@ +# Mount to home + +Mount filesystem volumes to the container's root directory. + +## Example + +Mount `~/.config/fontconfig/ to the container's `$HOME` directory as read-only; then mount the +`~/Downloads` to the container's `$HOME` directory with read-write permissions. + +``` +filesystems: +- mount: to_home + volumes: + - path: .config/fontconfig + - permission: rw + path: Downloads +``` + +## Description + +Mount filesystem volumes from the runtime user's `$HOME` into the container, in the container's `$HOME` +directory, unless the destination is otherwise specified with `dest` variable. Pacwrap will terminate +with an error condition if the file or directory is not found; or the user is otherwise not sufficiently +privileged. + +Specify read/write permissions with a string using the `permission` variable. Valid combinations are +abbreviated as following: `ro` for read-only, and `rw` for read-write permissions. diff --git a/docs/filesystems/to_root.md b/docs/filesystems/to_root.md new file mode 100644 index 0000000..9708220 --- /dev/null +++ b/docs/filesystems/to_root.md @@ -0,0 +1,28 @@ +# Mount to root + +Mount filesystem volumes to the container's root directory. + +## Example + +Mount `/usr/share/icons/`, and `/etc/fonts/`, as read-only to the container; then mount +`/media/Storage/Games/Steam` to `/mnt/SteamLibrary`/ in the container with read-write permissions. + +``` +filesystems: +- mount: to_root + volumes: + - path: /usr/share/fonts + - path: /etc/fonts + - permission: rw + path: /media/Storage/Games/Steam + dest: /mnt/SteamLibrary +``` + +## Description + +Mount filesystem volumes from the host into the container, at the destination mirroring the host, unless +the destination is otherwise specified with `dest` variable. Pacwrap will terminate with an error condition +if the file or directory is not found; or the user is otherwise not sufficiently privileged. + +Specify read/write permissions with a string using the `permission` variable. Valid value combinations are +abbreviated as following: `ro` for read-only, and `rw` for read-write permissions. diff --git a/docs/modules/README.md b/docs/modules/README.md index e69de29..3947c9a 100644 --- a/docs/modules/README.md +++ b/docs/modules/README.md @@ -0,0 +1,13 @@ +# Modules + +This directory contains brief documentation along with examples for each permission module. + +### Table of Contents + +- [Devices](./dev.md) +- [Display Servers](./display.md) +- [Environment Variables](./env.md) +- [Graphics Devices](./gpu.md) +- [Networking](./gpu.md) +- [Pipewire](./pipewire.md) +- [PulseAudio](./pulseaudio.md) diff --git a/docs/modules/dev.md b/docs/modules/dev.md new file mode 100644 index 0000000..e0ffdae --- /dev/null +++ b/docs/modules/dev.md @@ -0,0 +1,22 @@ +# Device Module + +Permiss access to devices from the `/dev/` sysfs. + +## Example + +Permiss access to the /dev/input device in the container environment. + +``` +- module: dev + devices: + - input +``` + +## Description + +Mount a list of devices as specified in the `devices` array. Requires the user +have unprivileged access to the device in question. Otherwise this module will +terminate pacwrap with an error condition. + +In most cases, the automatic permissions should be sufficient with the exception +of some USB input devices for particular use cases. diff --git a/docs/modules/display.md b/docs/modules/display.md new file mode 100644 index 0000000..4d69b5c --- /dev/null +++ b/docs/modules/display.md @@ -0,0 +1,14 @@ +# Display Module + +Provide access to display server sockets. + +## Example +``` +permissions: +- module: display +``` + +## Description + +Use this module to automatically detect and bind available display server sockets to the container environment. +If no sockets are available, this module will terminate pacwrap with an error condition. diff --git a/docs/modules/env.md b/docs/modules/env.md new file mode 100644 index 0000000..75f671c --- /dev/null +++ b/docs/modules/env.md @@ -0,0 +1,25 @@ +# Environment Variables Module + +Set environment variables in the container environment. + +## Example + +Passthrough the environment variable `LANG`, and set the environment variable `QT_X11_NO_MITSHM`. + +``` +permissions: +- module: env + variables: + - var: LANG + - var: QT_X11_NO_MITSHM + set: '1' +``` + +## Description + +Use this module to passthrough or define environment variables in the container environment. +When an environment variable is not available in the host environment, this module will +provide warning and set a blank value in the target container. + +By default pacwrap, will not passthrough environment variables from the host, unless specified +otherwise by the user with the elidation of the `set` parameter when definining a `var`. diff --git a/docs/modules/gpu.md b/docs/modules/gpu.md new file mode 100644 index 0000000..18f9de6 --- /dev/null +++ b/docs/modules/gpu.md @@ -0,0 +1,14 @@ +# GPU Module + +Provide available GPUs to the container. + +## Example + +``` +permissions: +- module: gpu +``` + +## Description + +Use this module to automatically detect and bind available GPUs to the container environment. diff --git a/docs/modules/net.md b/docs/modules/net.md new file mode 100644 index 0000000..a512c1e --- /dev/null +++ b/docs/modules/net.md @@ -0,0 +1,14 @@ +# Networking Module + +Provide host networking to the container. + +## Example + +``` +permissions: +- module: net +``` + +## Description + +Use this module to bind all available host networking to the container environment. diff --git a/docs/modules/pipewire.md b/docs/modules/pipewire.md new file mode 100644 index 0000000..21212c5 --- /dev/null +++ b/docs/modules/pipewire.md @@ -0,0 +1,14 @@ +# Pipewire Module + +Avail pipewire audio devices to the container. + +## Example + +``` +permissions: +- module: pipewire +``` + +## Description + +Use this module to automatically detect and bind available pipewire sockets to the container environment. diff --git a/docs/modules/pulseaudio.md b/docs/modules/pulseaudio.md new file mode 100644 index 0000000..613b0d4 --- /dev/null +++ b/docs/modules/pulseaudio.md @@ -0,0 +1,14 @@ +# PulseAudio Module + +Avail PulseAudio devices to the container. + +## Example + +``` +permissions: +- module: pulseaudio +``` + +## Description + +Use this module to automatically detect and bind available PulseAudio sockets to the container environment. diff --git a/pacwrap-core/src/sync.rs b/pacwrap-core/src/sync.rs index eb1dc8f..87fd2c5 100644 --- a/pacwrap-core/src/sync.rs +++ b/pacwrap-core/src/sync.rs @@ -153,11 +153,10 @@ impl AlpmConfigData { pub fn instantiate_alpm_agent(config: &Global, remotes: &AlpmConfigData) -> Alpm { let mut handle = Alpm::new("/mnt/fs", "/mnt/fs/var/lib/pacman/").unwrap(); + let hook_dirs = vec!["/mnt/fs/usr/share/libalpm/hooks/", "/mnt/fs/etc/pacman.d/hooks/"]; handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); - handle - .set_hookdirs(vec!["/mnt/fs/usr/share/libalpm/hooks/", "/mnt/fs/etc/pacman.d/hooks/"].iter()) - .unwrap(); + handle.set_hookdirs(hook_dirs.iter()).unwrap(); handle.set_cachedirs(vec!["/mnt/share/cache"].iter()).unwrap(); handle.set_gpgdir("/mnt/share/gnupg").unwrap(); handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); @@ -256,13 +255,15 @@ fn synchronize_database(cache: &ContainerCache, force: bool, lock: &Lock) -> Res } Alpm::release(handle).unwrap(); + lock.assert()?; for i in cache.registered().iter() { - let ins: &ContainerHandle = cache.get_instance(i).unwrap(); + let ins: &ContainerHandle = cache.get_instance(i)?; for repo in PACMAN_CONF.repos.iter() { let src = &format!("{}/pacman/sync/{}.db", *DATA_DIR, repo.name); let dest = &format!("{}/var/lib/pacman/sync/{}.db", ins.vars().root(), repo.name); + if let Err(error) = create_hard_link(src, dest).prepend(|| format!("Failed to hardlink db '{}'", dest)) { error.warn(); } diff --git a/pacwrap/README.md b/pacwrap/README.md index 983e966..2a6ba6f 100644 --- a/pacwrap/README.md +++ b/pacwrap/README.md @@ -1,5 +1,10 @@ # pacwrap +[![pacwrap](https://shields.io/aur/version/pacwrap?style=for-the-badge&color=599ffb&archlinux&label=pacwrap)](https://aur.archlinux.org/packages/pacwrap/) +[![pacwrap-git](https://shields.io/aur/version/pacwrap-git?style=for-the-badge&color=599ffb&logo=archlinux&label=pacwrap-git)](https://aur.archlinux.org/packages/pacwrap-git/) +[![License](https://shields.io/crates/l/pacwrap/0.8.0?style=for-the-badge&color=6dfb59)](https://spdx.org/licenses/GPL-3.0-only.html) +![MSRV](https://shields.io/crates/msrv/pacwrap/0.8.0?style=for-the-badge&color=fba759) + A package management front-end which utilises libalpm to facilitate the creation of unprivileged, userspace containers with parallelised, filesystem-agnostic deduplication. These containers are constructed via bubblewrap to execute package transactions and launch applications. @@ -30,6 +35,12 @@ And finally, to install ```neovim``` inside of a fresh, aggregated container cal $ pacwrap -Syucat editor --dep=base neovim ``` +To update these containers just created in aggregate: + +``` +$ pacwrap -Syu +``` + More advanced examples along with further documentation of configuration can be found further elaborated upon **[here](../docs/)**. @@ -45,7 +56,7 @@ If a feature you see here is not completed, feel free to submit a PR; or submit | Transaction Agent | Transact within a sandboxed runtime environment | ✅ | | Transaction CLI | Functional | ✅ | | Global Configuration | Functional | ✅ | -| Dependency Resolution | Functional, but too liberal to compensate for a lack of conflict resolution | ⚠ | +| Package Dependency Resolution | Utilizes a recursive first-depth search algorithm; resilient to cycling | ✅ | | Foreign Database Resolution | Populates foreign package database in aggregate containers | ✅ | | Foreign Database Resolution (Lazy) | Not yet implemented | ❌ | | Conflict Resolution | Not yet implemented | ❌ | @@ -78,7 +89,7 @@ An online version of the user manual is viewable **[here](../docs/manual.md)**. A minimum version of Rust 1.72 is required to build with the following libraries fulfilled by your distribution: ``` -libalpm, libseccomp, libzstd +libalpm>=14, libseccomp, libzstd ``` ## Packaging requirements diff --git a/pacwrap/src/compose.rs b/pacwrap/src/compose.rs index 45eabc4..86dc744 100644 --- a/pacwrap/src/compose.rs +++ b/pacwrap/src/compose.rs @@ -58,6 +58,7 @@ pub fn compose(args: &mut Arguments) -> Result<()> { fn delete_containers<'a>( cache: &'a ContainerCache<'a>, + lock: &'a Lock, logger: &mut Logger, delete: &Vec<&str>, flags: &TransactionFlags, @@ -67,9 +68,9 @@ fn delete_containers<'a>( if flags.contains(TransactionFlags::NO_CONFIRM) { println!("{} {}{}...{}", *BAR_GREEN, *BOLD, &message, *RESET); - delete_roots(cache, logger, delete, force)?; + delete_roots(cache, lock, logger, delete, force)?; } else if let Ok(_) = prompt_targets(&delete, &message, false) { - delete_roots(cache, logger, delete, force)?; + delete_roots(cache, lock, logger, delete, force)?; } Ok(()) @@ -103,10 +104,12 @@ fn compose_handles<'a>( fn instantiate<'a>( composed: HashMap<&'a str, ContainerHandle<'a>>, mut cache: ContainerCache<'a>, + lock: &'a Lock, logger: &mut Logger, ) -> Result> { + lock.assert()?; println!("{} {}Instantiating container{}...{}", *BAR_GREEN, *BOLD, if composed.len() > 1 { "s" } else { "" }, *RESET); - + for (instance, handle) in composed { instantiate_container(&handle)?; @@ -220,10 +223,10 @@ fn engage_aggregator<'a>(args: &mut Arguments, lock: &'a Lock) -> Result<()> { } if delete.len() > 0 { - delete_containers(&cache, &mut logger, &delete, &flags, force)?; + delete_containers(&cache, lock, &mut logger, &delete, &flags, force)?; } - cache = instantiate(compose_handles(&cache, compose)?, cache, &mut logger)?; + cache = instantiate(compose_handles(&cache, compose)?, cache, lock, &mut logger)?; acquire_targets(&cache, &mut targets, &mut queue)?; Ok(TransactionAggregator::new(&cache, &mut logger, TransactionType::Upgrade(true, true, false)) .assert_lock(lock)? diff --git a/pacwrap/src/help.rs b/pacwrap/src/help.rs new file mode 100644 index 0000000..f2448c3 --- /dev/null +++ b/pacwrap/src/help.rs @@ -0,0 +1,306 @@ +/* + * pacwrap + * + * Copyright (C) 2023-2024 Xavier R.M. + * SPDX-License-Identifier: GPL-3.0-only + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use indexmap::IndexSet; +use lazy_static::lazy_static; +use std::fmt::{Display, Formatter, Result as FmtResult}; + +use pacwrap_core::{ + err, + impl_error, + utils::{arguments::Operand, is_color_terminal, Arguments}, + Error, + ErrorTrait, +}; + +mod manual; +mod config; +pub mod version; + +pub use version::print_version; + +lazy_static! { + static ref HELP_ALL: Vec = [ + HelpTopic::Execute, + HelpTopic::Sync, + HelpTopic::Remove, + HelpTopic::Compose, + HelpTopic::Query, + HelpTopic::Process, + HelpTopic::Utils, + HelpTopic::List, + HelpTopic::Help, + HelpTopic::Env, + HelpTopic::Version, + HelpTopic::Copyright + ] + .into(); +} + +#[derive(Debug)] +enum ErrorKind { + InvalidTopic(String), +} + +impl_error!(ErrorKind); + +impl Display for ErrorKind { + fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + match self { + Self::InvalidTopic(err) => write!(fmter, "Topic '{}' is not available.", err), + }?; + + write!(fmter, "\nTry 'pacwrap -h' for more information on valid operational parameters.") + } +} + +pub fn help(mut args: &mut Arguments) -> Result<(), Error> { + let help = ascertain_help(&mut args)?; + let mut buffer = String::new(); + + for topic in help.0 { + topic.write(&mut buffer, help.1).unwrap(); + } + + if let HelpLayout::Console = help.1 { + print!("\x1b[?7l{}\x1b[?7h", buffer) + } else { + print!("{}", buffer) + } + + Ok(()) +} + +fn ascertain_help<'a>(args: &'a mut Arguments) -> Result<(IndexSet<&'a HelpTopic>, &'a HelpLayout), Error> { + let mut layout = match is_color_terminal() { + true => &HelpLayout::Console, + false => &HelpLayout::Dumb, + }; + let mut topic: Vec<&HelpTopic> = vec![&HelpTopic::Default]; + let mut more = false; + + while let Some(arg) = args.next() { + match arg { + Operand::Long("format") | Operand::Long("help") | Operand::Short('f') | Operand::Short('h') => continue, + Operand::Short('m') | Operand::Long("more") => more = true, + Operand::ShortPos('f', "man") | Operand::LongPos("format", "man") => layout = &HelpLayout::Man, + Operand::ShortPos('f', "ansi") | Operand::LongPos("format", "ansi") => layout = &HelpLayout::Console, + Operand::ShortPos('f', "dumb") | Operand::LongPos("format", "dumb") => layout = &HelpLayout::Dumb, + Operand::ShortPos('f', "markdown") | Operand::LongPos("format", "markdown") => layout = &HelpLayout::Markdown, + Operand::ShortPos('h', "all") + | Operand::LongPos("help", "all") + | Operand::Short('a') + | Operand::Long("all") + | Operand::Value("all") => topic.extend(HELP_ALL.iter()), + Operand::ShortPos('h', value) | Operand::LongPos("help", value) | Operand::Value(value) => + topic.push(HelpTopic::from(value)?), + _ => args.invalid_operand()?, + } + } + + let len = topic.len(); + let start = if more || len == 1 || len > 7 { 0 } else { 1 }; + + args.set_index(1); + Ok((topic.drain(start ..).collect(), layout)) +} + +#[derive(Eq, PartialEq, Hash)] +enum HelpTopic { + Sync, + Remove, + Compose, + Execute, + Default, + Query, + Utils, + Process, + List, + Help, + Env, + Copyright, + Version, + PacwrapYml, +} + +impl HelpTopic { + fn from(str: &str) -> Result<&Self, Error> { + Ok(match str { + "E" | "exec" | "run" => &HelpTopic::Execute, + "S" | "sync" | "init" => &HelpTopic::Sync, + "P" | "process" | "ps" => &HelpTopic::Process, + "L" | "list" | "ls" => &HelpTopic::List, + "U" | "utils" => &HelpTopic::Utils, + "R" | "remove" => &HelpTopic::Remove, + "C" | "compose" => &HelpTopic::Compose, + "Q" | "query" => &HelpTopic::Query, + "V" | "version" => &HelpTopic::Version, + "h" | "help" => &HelpTopic::Help, + "env" | "environment" => &HelpTopic::Env, + "copyright" => &HelpTopic::Copyright, + "synopsis" => &HelpTopic::Default, + "pacwrap.yml" => &HelpTopic::PacwrapYml, + _ => err!(ErrorKind::InvalidTopic(str.into()))?, + }) + } + + fn write(&self, buf: &mut String, layout: &HelpLayout) -> FmtResult { + match self { + Self::Default => manual::default(buf, layout), + Self::Sync => manual::sync(buf, layout), + Self::Remove => manual::remove(buf, layout), + Self::Execute => manual::execute(buf, layout), + Self::Process => manual::process(buf, layout), + Self::Version => manual::version(buf, layout), + Self::Env => manual::environment(buf, layout), + Self::Compose => manual::compose(buf, layout), + Self::Utils => manual::utils(buf, layout), + Self::List => manual::list(buf, layout), + Self::Help => manual::meta(buf, layout), + Self::Query => manual::query(buf, layout), + Self::Copyright => manual::copyright(buf, layout), + Self::PacwrapYml => config::default(buf, layout), + } + } +} + +enum HelpLayout { + Man, + Dumb, + Markdown, + Console, +} + +impl HelpLayout { + fn head(&self) -> &str { + match self { + Self::Console => "", + Self::Markdown => "## ", + Self::Man => ".SH\n", + Self::Dumb => "", + } + } + + fn sub_bold(&self) -> &str { + match self { + Self::Console => " ", + Self::Markdown => "#### **", + Self::Man => ".TP\n\\fB", + Self::Dumb => " ", + } + } + + fn sub(&self) -> &str { + match self { + Self::Markdown => "#### ", + Self::Man => ".TP\n", + Self::Dumb | Self::Console => " ", + } + } + + fn sub_section(&self) -> &str { + match self { + Self::Console => " ", + Self::Markdown => "### **", + Self::Man => ".SS\n", + Self::Dumb => " ", + } + } + + fn sub_paragraph(&self) -> &str { + match self { + Self::Console | Self::Dumb => " ", + Self::Man => ".PP\n", + Self::Markdown => "", + } + } + + fn tab(&self) -> &str { + match self { + Self::Console | Self::Dumb => " ", + Self::Markdown | Self::Man => "", + } + } + + #[allow(dead_code)] + fn underline(&self) -> &str { + match self { + Self::Console => "", + Self::Man => "\n.I", + Self::Markdown => "", + Self::Dumb => "", + } + } + + #[allow(dead_code)] + fn reset_underline(&self) -> &str { + match self { + Self::Console => "", + Self::Man => "\n.I", + Self::Markdown => "", + Self::Dumb => "", + } + } + + fn reset(&self) -> &str { + match self { + Self::Console => "", + Self::Man => "\\fR", + Self::Markdown | Self::Dumb => "", + } + } + + fn reset_bold(&self) -> &str { + match self { + Self::Console => "", + Self::Man => "\\fR", + Self::Markdown => "**", + Self::Dumb => "", + } + } + + fn bold(&self) -> &str { + match self { + Self::Console => "", + Self::Man => "\\fB", + Self::Markdown => "**", + Self::Dumb => "", + } + } + + fn code(&self) -> &str { + match self { + Self::Console | Self::Dumb | Self::Man => "", + Self::Markdown => "```", + } + } +} + +fn version_string() -> String { + let version = env!("CARGO_PKG_VERSION"); + let release = env!("PACWRAP_BUILD"); + let head = env!("PACWRAP_BUILDHEAD"); + let date = env!("PACWRAP_BUILDSTAMP"); + + if head.is_empty() { + format!("{version} ({date})") + } else { + format!("{version}-{head}-{release} ({date})") + } +} diff --git a/pacwrap/src/help/config.rs b/pacwrap/src/help/config.rs new file mode 100644 index 0000000..bfd0ba2 --- /dev/null +++ b/pacwrap/src/help/config.rs @@ -0,0 +1,160 @@ +/* + * pacwrap + * + * Copyright (C) 2023-2024 Xavier R.M. + * SPDX-License-Identifier: GPL-3.0-only + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use std::fmt::{Result as FmtResult, Write}; + +use crate::help::{version_string, HelpLayout}; + +pub fn default(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let bold = layout.bold(); + let sub_bold = layout.sub_bold(); + let sub_para = layout.sub_paragraph(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let code = layout.code(); + let name = env!("CARGO_PKG_NAME"); + let date = env!("PACWRAP_BUILDSTAMP"); + + match layout { + HelpLayout::Man => writeln!( + buf, + ".nh\n.TH {name}.yml 2 \"{date}\" \"{name} version_string_placeholder\" \"Pacwrap Configuration Directives\"\n" + )?, + HelpLayout::Markdown => writeln!( + buf, + "# Pacwrap Configuration Directives + +This document was generated by the {name} binary with version {} of the program.\n", + version_string(), + )?, + _ => (), + } + + writeln!( + buf, + "{head}NAME{reset} +{sub_bold}pacwrap.yml{reset_bold} - pacwrap configuration file. +{sub_bold}repositories.conf{reset_bold} - pacman repository file. + +{head}SYNOPSIS{reset} +{sub_bold}~/.config/pacwrap/pacwrap.yml{reset_bold} +{sub_bold}~/.config/pacwrap/repositories.conf{reset_bold} + +{head}DESCRIPTION{reset} +{tab}Pacwrap upon invocation will attempt to deserialise {bold}pacwrap.yml{reset_bold}(2) at the location specified herein +{tab}compliant with the XDG Directory Specification. Upon activation of {bold}libalpm{reset_bold}(3), the repositories.conf +{tab}file will be deserialised by the pacman_conf crate in order to faciliate the population of package +{tab}repositories and mirrors. + +{sub_para}Repository configuration is parsed using {bold}pacman.conf{reset_bold}(5)'s ini format. All other options related the +{tab}{bold}libalpm{reset_bold}(3) as defined by {bold}pacman.conf{reset_bold}(5) therein are otherwise ignored. At present by default, +{tab}the repository configuration, as defined, references the mirrorlist defined by your system. +{tab}This ensures as smooth of an out-of-box experience as humanly possible. + +{tab}Optionally you may specify environment variables, as specified in {bold}pacwrap{reset_bold}(1), to override +{tab}these locations. These options are designated for experienced users only. + +{head}EXAMPLE{reset} +{code} +{tab}config: +{tab} logging: Basic +{tab} summary: Table +{tab} progress: +{tab} transact: CondensedForeign +{tab} download: CondensedForeign +{tab}alpm: +{tab} ignore_pkg: +{tab} - nvidia-utils +{tab} - lib32-nvidia-utils +{code} + +{head}SECTIONS{reset} +{sub_bold}config:{reset_bold} +{sub_para}{tab}Configuration pertaining to {bold}pacwrap{reset_bold}(1) are to be declared within this section. + +{sub_bold}alpm:{reset_bold} +{sub_para}{tab}Configuration pertaining to {bold}libalpm{reset_bold}(3) are to be declared within this section. + +{head}CONFIG{reset} +{sub_bold}logging{reset_bold}: Basic +{tab}{tab}Logging verbosity specified here. Available options are {bold}Basic{reset_bold}, {bold}Verbose{bold}, and {bold}None{reset_bold}. + +{sub_bold}summary{reset_bold}: Basic +{tab}{tab}Transaction summary type. Available options are {bold}Basic{reset_bold}, {bold}BasicForeign{reset_bold}, {bold}Table{reset_bold}, and {bold}TableForeign{reset_bold}. + +{tab}{tab}Each option suffixed with the {bold}Foreign{reset_bold} juxtaposition, will take effect only during the +{tab}{tab}juxtaposed transaction type with otherwise the opposite effect. + +{sub_bold}progress:{reset_bold} +{tab}{tab}Progress types are declared within this subsection. + +{head}PROGRESS{reset} +{sub_bold}transact{reset_bold}: CondensedForeign +{tab}{tab}Progress type for transaction progress is specified with this option. Available values are +{tab}{tab}{bold}Basic{reset_bold}, {bold}Condensed{bold}, {bold}CondensedForeign{reset_bold}, {bold}CondensedLocal{reset_bold}, and {bold}Verbose{reset_bold}. + +{tab}{tab}Each option suffixed with the {bold}Foreign{reset_bold} or {bold}Local{reset_bold} juxtaposition, will take effect only during +{tab}{tab}the juxtaposed transaction type with otherwise the opposite effect. + +{sub_bold}download{reset_bold}: Verbose +{tab}{tab}Download type for download progress is specified with this option. Available values are +{tab}{tab}{bold}Basic{reset_bold}, {bold}Condensed{bold}, {bold}CondensedForeign{reset_bold}, {bold}CondensedLocal{reset_bold}, and {bold}Verbose{reset_bold}. + +{tab}{tab}Each option suffixed with the {bold}Foreign{reset_bold} or {bold}Local{reset_bold} juxtaposition, will take effect only during +{tab}{tab}the juxtaposed transaction type with otherwise the opposite effect. + +{head}ALPM{reset} +{sub_bold}ignore_pkg:{reset_bold} +{tab}{tab}Ignored package(s) are declared herein with a string array. + +{sub_bold}hold_pkg:{reset_bold} +{tab}{tab}Held package(s) are declared herein with a string array. + +{sub_bold}sig_level{reset_bold}: Required DatabaseOptional +{tab}{tab}Default global signature level - see {bold}pacman.conf{reset_bold}(5) for valid options. Options are declared +{tab}{tab}as a singular string value. + +{sub_bold}sig_level_local{reset_bold}: Optional +{tab}{tab}Default local signature level - see {bold}pacman.conf{reset_bold}(5) for valid options. Options are declared +{tab}{tab}as a single string value. + +{sub_bold}check_space{reset_bold}: true +{tab}{tab}Instructs {bold}libalpm{reset_bold}(3), where applicable, to check if there's available space on disk in order +{tab}{tab}to facilitate a transaction. Value is declared with a {bold}bool{reset_bold}. + +{sub_bold}download_timeout{reset_bold}: true +{tab}{tab}Instructs {bold}libalpm{reset_bold}(3) to timeout downloads from unsatisfactory mirrors. Value is declared with +{tab}{tab}a {bold}bool{reset_bold}. + +{sub_bold}parallel_downloads{reset_bold}: 1 +{tab}{tab}Instructs {bold}libalpm{reset_bold}(3) to parallelise the download queue with a maximum queue amount. Specify an +{tab}{tab}{bold}integer{reset_bold} to declare a maximum value. + +{head}SEE ALSO{reset} +{tab}{tab}{bold}pacman.conf{reset_bold}(5), {bold}libalpm{reset_bold}(3) + +{head}COPYRIGHT{reset} +{tab}Copyright (C) 2023-2024 Xavier R.M. + +{sub_para}This program may be freely redistributed under the +{tab}terms of the GNU General Public License v3 only.\n" + ) +} diff --git a/pacwrap/src/help/manual.rs b/pacwrap/src/help/manual.rs new file mode 100644 index 0000000..f03f7c5 --- /dev/null +++ b/pacwrap/src/help/manual.rs @@ -0,0 +1,641 @@ +/* + * pacwrap + * + * Copyright (C) 2023-2024 Xavier R.M. + * SPDX-License-Identifier: GPL-3.0-only + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use std::fmt::{Result as FmtResult, Write}; + +use crate::help::{version_string, HelpLayout}; + +fn header(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let name = env!("CARGO_PKG_NAME"); + let date = env!("PACWRAP_BUILDSTAMP"); + + match layout { + HelpLayout::Man => writeln!(buf, ".nh\n.TH {name} 1 \"{date}\" \"{name} version_string_placeholder\" \"User Manual\"\n"), + HelpLayout::Markdown => writeln!( + buf, + "# Pacwrap User Manual + +This document was generated by the {name} binary with version {} of the program.\n", + version_string() + ), + _ => Ok(()), + } +} + +pub fn default(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let sub_para = layout.sub_paragraph(); + let sub_bold = layout.sub_bold(); + let bold = layout.bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + + header(buf, layout)?; + writeln!( + buf, + "{head}NAME{reset} +{tab}pacwrap + +{head}SYNOPSIS{reset} +{tab}pacwrap [{bold}OPERATION{reset_bold} | {bold}COMMAND MODULE{reset_bold}] [{bold}ARGUMENTS{reset_bold}] [{bold}TARGETS{reset_bold}] + +{head}DESCRIPTION{reset} +{sub_para}A package management front-end which utilises libalpm to facilitate the creation of unprivileged, +{tab}namespace containers with parallelised, filesystem-agnostic deduplication. These containers +{tab}are constructed with bubblewrap to execute package transactions and launch applications. + +{sub_para}This application is designed to allow for the creation and execution of secure, replicable +{tab}containerised environments for general-purpose use. CLI and GUI applications are all supported. +{tab}Once a container environment is configured, it can be re-established or replicated on any system. + +{head}OPERATIONS{reset} +{sub_bold}-S, --sync{reset_bold} +{tab}{tab}Synchronize package databases and update packages in target containers. + +{sub_bold}-R, --remove{reset_bold} +{tab}{tab}Remove packages from target containers. + +{sub_bold}-Q, --query{reset_bold} +{tab}{tab}Query package information from target container. + +{sub_bold}-C, --compose{reset_bold} +{tab}{tab}Compose a container from configuration. + +{sub_bold}-P, --process{reset_bold} +{tab}{tab}Manage and show status of running container processes. + +{sub_bold}-E, --execute{reset_bold} +{tab}{tab}Executes application in target container using bubblewrap. + +{sub_bold}-L, --list{reset_bold} +{tab}{tab}List of available containers managed by pacwrap. + +{sub_bold}-U, --utils{reset_bold} +{tab}{tab}Invoke miscellaneous utilities to manage containers. + +{sub_bold}-h, --help={reset_bold} +{tab}{tab}Invoke a printout of this manual to {bold}STDOUT{reset_bold}. + +{sub_bold}-V, --version{reset_bold} +{tab}{tab}Display version and copyright information in {bold}STDOUT{reset_bold}.\n" + ) +} + +pub fn execute(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let sub_bold = layout.sub_bold(); + let tab = layout.tab(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + + writeln!( + buf, + "{head}EXECUTE{reset} +{sub_bold}-r, --root{reset_bold} +{tab}{tab}Execute operation with fakeroot and fakechroot. Facilitates a command with faked privileges. + +{sub_bold}-s, --shell{reset_bold} +{tab}{tab}Invoke a bash shell\n" + ) +} + +pub fn meta(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let bold = layout.bold(); + let sub_bold = layout.sub_bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let tab = layout.tab(); + + writeln!( + buf, + "{head}HELP{reset} +{sub_bold}-m, --more{reset_bold} +{tab}{tab}When specifying a topic to display, show the default topic in addition to specified options. + +{sub_bold}-f, --format=FORMAT{reset_bold} +{tab}{tab}Change output format of help in {bold}STDOUT{reset_bold}. Format options include: 'ansi', 'dumb', 'markdown', and 'man'. +{tab}{tab}This option is for the express purposes of generating documentation at build time, and has little utility +{tab}{tab}outside the context of package maintenance. 'man' option produces troff-formatted documents for man pages. + +{sub_bold}-a, --all, --help=all{reset_bold} +{tab}{tab}Display all help topics.\n" + ) +} + +pub fn sync(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let bold = layout.bold(); + let tab = layout.tab(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let sub = layout.sub(); + let sub_bold = layout.sub_bold(); + let sub_sect = layout.sub_section(); + let sub_para = layout.sub_paragraph(); + + writeln!( + buf, + "{head}SYNCHRONIZATION{reset} +{sub_para}Provides the facilities required to be able to synchronize and create containers in aggregate. + +{sub_bold}-y, --refresh{reset_bold} +{tab}{tab}Synchronize remote package databases. Specify up to 2 times to force a refresh. + +{sub_bold}-u, --upgrade{reset_bold} +{tab}{tab}Execute aggregate upgrade routine on all or specified containers. Use {bold}-t, --target={reset_bold} followed by +{tab}{tab}a list of packages to specify package targets. Packages applicable to a target {bold}must{reset_bold} only be specified +{tab}{tab}after the target operand. + +{sub_bold}-f, --filesystem{reset_bold} +{tab}{tab}Force execution of filesystem synchronization target on all or specified containers. In combination +{tab}{tab}with {bold}-o/--target-only{reset_bold}, in addition to no other specified targets, filesystem slices will be synchronized +{tab}{tab}without package synhcronization on on all applicable containers. This operation is useful for propagation +{tab}{tab}of manual filesystem changes to all aggregate containers. + +{sub_bold}-c, --create{reset_bold} +{tab}{tab}Create a container with the first specified target. A container type argument is also required. + +{sub_bold}-b, --base{reset_bold} +{tab}{tab}Base container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation. + +{tab}{tab}This container type is used as the base layer for all downstream containers. Only one base container +{tab}{tab}dependency per slice or aggregate is supported. Filesystem and package deduplication via slices and +{tab}{tab}aggregate containers are recommended, but optional. This container type is not dependant. + +{sub_bold}-s, --slice{reset_bold} +{tab}{tab}Slice container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation. +{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, to ascertain foreign +{tab}{tab}packages and influence ordering of downstream synchronization target(s). Container slicing provides +{tab}{tab}the ability to install packages in a lightweight, sliced filesytem, which aid in the deduplication +{tab}{tab}of common downstream package and filesystem dependencies. + +{tab}{tab}Useful for graphics drivers, graphical toolkits, fonts, etc.; these are not meant for applications. + +{sub_bold}-a, --aggegrate{reset_bold} +{tab}{tab}Aggregate container type. Specify alongside {bold}-c, --create{reset_bold} to this assign container type during creation. +{tab}{tab} +{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, in order to acertain foreign +{tab}{tab}packages and amalgamate the target. These containers are ideal for installing software with the aid of +{tab}{tab}filesystem and package deduplication. +{tab}{tab} +{tab}{tab}Useful for all general purpose applications, browsers, e-mail clients, or even terminal user interface +{tab}{tab}applications such as IRC clients. It is recommended to base your containers on aggregate type containers. + +{sub_bold}-t, --target=TARGET{reset_bold} +{tab}{tab}Specify a target container for the specified operation. + +{sub_bold}-d, --dep=DEPEND{reset_bold} +{tab}{tab}Specify a dependent container for the specified operation. + +{sub_bold}-p, --preview{reset_bold} +{tab}{tab}Perform a dryrun operation on existing containers to preview changes applicable or otherwise specified. +{tab}{tab}Only applicable to + +{sub_bold}-o, --target-only{reset_bold} +{tab}{tab}Apply specified operation on the specified target(s) only. + +{sub_bold}--force-foreign{reset_bold} +{tab}{tab}Force synchronization of foreign packages on resident container. Useful for when installing +{tab}{tab}a new package in an aggregate container without all the prerequisite foreign dependencies +{tab}{tab}synchronized to this container's package database. + +{sub_bold}--dbonly{reset_bold} +{tab}{tab}Transact on resident containers with a database-only transaction. + +{sub_bold}--noconfirm{reset_bold} +{tab}{tab}Override confirmation prompts and confirm all operations. + +{sub_sect}EXAMPLES{reset_bold} +{sub}`$ pacwrap -Syucbt base` +{tab}{tab}Create a base container named base with no additional packages + +{sub}`$ pacwrap -Syucbt firefox firefox --dep=base,common,nvidia` +{tab}{tab}Create aggregate container named firefox with firefox installed. + +{sub}`$ pacwrap -Syut electron element-desktop -t mozilla firefox thunderbird` +{tab}{tab}Synchronize package databases and upgrade all containers, as well as install element-desktop +{tab}{tab}in the target electron, and install firefox and thunderbird in the target mozilla. + +{sub}`$ pacwrap -Syucst common gtk3 qt6-base --dep=base -cst nvidia nvidia-utils --dep=base,common` +{tab}{tab}Create two sliced containers, one named common with the packages gtk3, qt6-base, and another +{tab}{tab}named nvidia with the package nvidia-utils. + +{sub}`$ pacwrap -Sof` +{tab}{tab}Synchronize filesystem state of all associated containers present in the data directory.\n") +} + +pub fn remove(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let sub = layout.sub(); + let bold = layout.bold(); + let sub_sect = layout.sub_section(); + let sub_bold = layout.sub_bold(); + let sub_para = layout.sub_paragraph(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + + writeln!( + buf, + "{head}REMOVE{reset} +{sub_para}Remove packages from specified containers. + +{sub_bold}-s, --recursive{reset_bold} +{tab}{tab}Recursively remove all target packages with the associated target container. This does +{tab}{tab}not apply to packages upstream of a downstream container. + +{sub_bold}-c, --cascade{reset_bold} +{tab}{tab}Remove all target packages with the associated target container, including all their +{tab}{tab}associated dependencies, provided they are not required by other packages, and are not +{tab}{tab}marked as being upstream of the target container. + +{sub_bold}-t, --target=TARGET{reset_bold} +{tab}{tab}Specify a target container for the specified operation. At least one container target is +{tab}{tab}is required for package removal operations. + +{sub_bold}--force-foreign{reset_bold} +{tab}{tab}Force the removal of foreign packages on target container. Useful for cleaning up +{tab}{tab}the package database of foreign, upstream dependencies synchronized to the target +{tab}{tab}container's package database. + +{sub_bold}-m, --delete{reset_bold} +{tab}{tab}Delete root filesystem(s) of specified targets. Shortcout to {bold}-Ur{reset_bold}. + +{sub_bold}-p, --preview{reset_bold} +{tab}{tab}Preview operation and perform no transaction. + +{sub_bold}--dbonly{reset_bold} +{tab}{tab}Transact on resident containers with a database-only transaction. + +{sub_bold}--noconfirm{reset_bold} +{tab}{tab}Override confirmation prompts and confirm all operations. + +{sub_sect}EXAMPLES{reset_bold} +{sub}`$ pacwrap -Rt firefox firefox` +{tab}{tab}Remove the target package firefox from target container firefox. + +{sub}`$ pacwrap rm firefox` +{tab}{tab}Delete the root filesystem for the firefox container. +\n" + ) +} + +pub fn compose(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let bold = layout.bold(); + let sub_bold = layout.sub_bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let sub = layout.sub(); + let sub_sect = layout.sub_section(); + + writeln!( + buf, + "{head}COMPOSE{reset} +{tab}Compose containers from container configuration files. This functionality provides a way +{tab}to deterministically compose containers from an established configuration. + +{sub_bold}{reset_bold} +{tab}{tab}Compose a container from the specified configuration file on disk. Unless a target is +{tab}{tab}otherwise specified, the container will be initialized with a name derived from the +{tab}{tab}filename provided. + +{sub_bold}-r, --reinitialize{reset_bold} +{tab}{tab}Compose an available, existing container for composition. The pre-existing container root +{tab}{tab}will be deleted and the container will be composited from the configuration data enumerated. + +{tab}{tab}Reinitialize container + +{sub_bold}-t, --target=TARGET{reset_bold} +{tab}{tab}Specify a target container for the specified operation. + +{sub_bold}-f, --force{reset_bold} +{tab}{tab}Disable sanity checks and force removal of container filesystem(s). + +{sub_bold}--reinitialize-all{reset_bold} +{tab}{tab}Queues all available, existing containers for composition. All pre-existing container roots +{tab}{tab}will be deleted and composited from the available configuration data enumerated. + +{sub_bold}--from-config{reset_bold} +{tab}{tab}Instruct pacwrap to populate configuration data from uninitialized containers. Under normal +{tab}{tab}circumstances, configuration data will only be populated from containers with configuration +{tab}{tab}data and an associative container root present. This option engages an alternate enuermation +{tab}{tab}pathway to allow composition of dormant, uninitialized container configurations. + +{sub_bold}--noconfirm{reset_bold} +{tab}{tab}Override confirmation prompts and confirm all operations. + +{sub_sect}EXAMPLES{reset_bold} +{sub}`$ pacwrap compose -rt element element.yml` +{tab}{tab}Reinitialize an existing container named element with its configuration derived +{tab}{tab}from the file 'element.yml'. + +{sub}`$ pacwrap compose --reinitialize-all --from-config` +{tab}{tab}Reinitialize all available containers as configured in '{bold}$PACWRAP_CONFIG_DIR{reset_bold}/container/'.\n" + ) +} + +pub fn query(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let sub_bold = layout.sub_bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let sub = layout.sub(); + let bold = layout.bold(); + let sub_sect = layout.sub_section(); + let sub_para = layout.sub_paragraph(); + + writeln!( + buf, + "{head}QUERY{reset} +{sub_para}Query package list on target container. This module presently is not complete. + +{sub_bold}-q, --quiet{reset_bold} +{tab}{tab}Quiet the output by truncating the package string. + +{sub_bold}-t, --target=TARGET{reset_bold} +{tab}{tab}Specify a target container for the specified operation. + +{sub_bold}-e, --explicit{reset_bold} +{tab}{tab}Filter output to explicitly-marked packages. + +{sub_sect}EXAMPLE{reset_bold} +{sub}`$ pacwrap -Qqe base` +{tab}{tab}Print a list of explicit packages from the {bold}base{reset_bold} container to {bold}STDOUT{reset_bold}.\n" + ) +} + +pub fn process(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let sub_bold = layout.sub_bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let sub = layout.sub(); + let bold = layout.bold(); + let sub_sect = layout.sub_section(); + let sub_para = layout.sub_paragraph(); + + writeln!( + buf, + "{head}PROCESS{reset} +{sub_para}Table a process list of running containers. Containers may be filtered on target and process depth. + +{sub_bold}-s, --summary{reset_bold} +{tab}{tab}Enumerate a process summary of containers being executed by pacwrap. + +{sub_bold}-k, --kill{reset_bold} +{tab}{tab}Kill target containers and their associated processes. + +{sub_bold}-a, --all{reset_bold} +{tab}{tab}Enumerate all processes associated with running containers. + +{sub_bold}-d, --depth{reset_bold} +{tab}{tab}Enumerate all processes at the specified depth associated with running containers. + +{sub_bold}-t, --target=TARGET{reset_bold} +{tab}{tab}Specify a target container for the specified operation. + +{sub_bold}--noconfirm{reset_bold} +{tab}{tab}Override confirmation prompts and confirm all operations. + +{sub_sect}EXAMPLE{reset_bold} +{sub}`$ pacwrap -Psaxc` +{tab}{tab}Print table enumerating all container processes to {bold}STDOUT{reset_bold} with process +{tab}{tab}arguments and execution path split into separate columns.\n" + ) +} + +pub fn utils(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let sub = layout.sub(); + let sub_para = layout.sub_paragraph(); + let sub_sect = layout.sub_section(); + let sub_bold = layout.sub_bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let bold = layout.bold(); + + writeln!( + buf, + "{head}UTILITIES{reset} +{sub_para}Miscellaneous utilities which provide helpful auxiliary functionality to aid in configuration and +{tab}maintenance of operate containers. + +{sub_bold}-v, --view{reset_bold} +{tab}{tab}Invoke {bold}$EDITOR{reset_bold} to view file associated with pacwrap. + +{sub_bold}-e, --edit{reset_bold} +{tab}{tab}Invoke {bold}$EDITOR{reset_bold} to edit file associated with pacwrap. + +{sub_bold}-o, --open{reset_bold} +{tab}{tab}Invoke default file viewer on specified target's home or root directory. + +{sub_bold}-l, --list{reset_bold} +{tab}{tab}Print a list of containers and basic metrics. + +{sub_bold}-s, --symlink{reset_bold} +{tab}{tab}Create a symbolic container. + +{sub_bold}-r, --remove{reset_bold} +{tab}{tab}Delete a container(s) root filesystem. + +{sub_sect}EDITOR OPTIONS{reset_bold} +{sub_para}These options are associated with the {bold}--edit{reset_bold} and {bold}--view{reset_bold} utility command modules. + +{sub_bold}-c, --config=CONTAINER{reset_bold} +{tab}{tab}Edit specified container configuration located in the pacwrap data directory. Defaults to +{tab}{tab}the primary configuration file: '{bold}$PACWRAP_CONFIG_DIR{reset_bold}/pacwrap.yml' if no option is otherwise +{tab}{tab}specified. + +{sub_bold}-d, --desktop=APPLICATION{reset_bold} +{tab}{tab}Edit specified desktop file associated with a pacwrap container. + +{sub_bold}-r, --repo{reset_bold} +{tab}{tab}Edit repositories configuration file: '{bold}$PACWRAP_CONFIG_DIR{reset_bold}/repositories.conf'. + +{sub_bold}-l, --log{reset_bold} +{tab}{tab}View 'pacwrap.log'. This file contains transaction log information. + +{sub_sect}OPEN OPTIONS{reset_bold} +{sub_para}These options are associated with the {bold}--open{reset_bold} utility command module. + +{sub_bold}-h, --home=CONTAINER{reset_bold} +{tab}{tab}Specified container's home filesystem. + +{sub_bold}-r, --root=CONTAINER{reset_bold} +{tab}{tab}Specified container's root filesystem. + +{sub_bold}-t, --target=CONTAINER{reset_bold} +{tab}{tab}Target container to perform the operation. + +{sub_sect}LIST{reset} +{sub_para}These options are associated with the {bold}--list{reset_bold} utility command module. + +{sub_bold}-t, --total{reset_bold} +{tab}{tab}Display a total column. + +{sub_bold}-d, --on-disk{reset_bold} +{tab}{tab}Display a size on disk column. + +{sub_bold}-s, --summary{reset_bold} +{tab}{tab}Print out a summary table to {bold}STDOUT{reset_bold}. + +{sub_bold}-b, --bytes{reset_bold} +{tab}{tab}Toggle byte unit display for the proceeding item. + +{sub_sect}REMOVE OPTIONS{reset_bold} +{sub_para}These options are associated with the {bold}--remove{reset_bold} utility command module. + +{sub_bold}-t, --target{reset_bold} +{tab}{tab}Target container to perform the operation. + +{sub_bold}--noconfirm{reset_bold} +{tab}{tab}Peform the operation without confirmation. + +{sub_bold}--force{reset_bold} +{tab}{tab}Disable sanity checks and force removal of conatiner filesystem. + +{sub_sect}SYMBOLIC{reset_bold} +{sub_para}These options are associated with the {bold}--symlink{reset_bold} utility command module. + +{sub_bold} {reset_bold} +{tab}{tab}Create a symbolic container of target at destination. + +{sub_bold}-n, --new{reset_bold} +{tab}{tab}Create a fresh configuration rather than derive it from the target. + +{sub_sect}EXAMPLES{reset_bold} +{sub}`$ pacwrap -Ulbtbts` +{tab}{tab}Print table listing containers out to {bold}STDOUT{reset_bold} with two total columns, one showing +{tab}{tab}the total amount of bytes. Then print a summary calculation of total consumption below. + +{sub}`$ pacwrap -Uvl` +{tab}{tab}View '{bold}$PACWRAP_DATA_DIR{reset_bold}/pacwrap.log' with {bold}$EDITOR{reset_bold}.\n" + ) +} + +pub fn list(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let sub = layout.sub(); + let sub_sect = layout.sub_section(); + let sub_bold = layout.sub_bold(); + let sub_para = layout.sub_paragraph(); + let bold = layout.bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + + writeln!( + buf, + "{head}LIST{reset} +{sub_para}List all initialized containers presently managed by pacwrap. + +{sub_para}This command module is a shortcut to {bold}-Ul{reset_bold}. + +{sub_bold}-t, --total{reset_bold} +{tab}{tab}Display a total column. + +{sub_bold}-o, --on-disk{reset_bold} +{tab}{tab}Display a size on disk column. + +{sub_bold}-b, --bytes{reset_bold} +{tab}{tab}Toggle byte unit display. + +{sub_sect}EXAMPLE{reset_bold} +{sub}`$ pacwrap ls -btbts` +{tab}{tab}Print table listing containers out to {bold}STDOUT{reset_bold} with two total columns, one showing +{tab}{tab}the total amount of bytes. Then print a summary calculation of total consumption below.\n" + ) +} + +pub fn environment(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let sub_bold = layout.sub_bold(); + let tab = layout.tab(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + let sub_para = layout.sub_paragraph(); + + writeln!( + buf, + "{head}ENVIRONMENT VARIABLES{reset} +{sub_para}Provided herein are environment variables of which can be used to configure pacwrap's runtime parameters. +{tab}{tab}Use with care: These variables if used improperly could result in undesired behaviour. + +{sub_bold}PACWRAP_CONFIG_DIR{reset_bold} +{tab}{tab}Set the configuration directory. This environment variable overrides the default, +{tab}{tab}XDG Directory Specification compliant path. + +{sub_bold}PACWRAP_DATA_DIR{reset_bold} +{tab}{tab}Set the data directory. This environment variable overrides the default, +{tab}{tab}XDG Directory Specification compliant path. + +{sub_bold}PACWRAP_CACHE_DIR{reset_bold} +{tab}{tab}Set the cache directory. This environment variable overrides the default, +{tab}{tab}XDG Directory Specification compliant path. + +{sub_bold}PACWRAP_VERBOSE=[0|1]{reset_bold} +{tab}{tab}Toggle verbose output during a transaction. This option may be removed or otherwise +{tab}{tab}differ in functionality in future. + +{sub_bold}PACWRAP_HOME{reset_bold} +{tab}{tab}Upon execution, mount the set path provided when engaging the 'home' module. + +{sub_bold}PACWRAP_ROOT{reset_bold} +{tab}{tab}Upon execution, Mount the set path provided when engaging the 'root' module.\n" + ) +} + +pub fn version(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let sub_bold = layout.sub_bold(); + let tab = layout.tab(); + let bold = layout.bold(); + let reset = layout.reset(); + let reset_bold = layout.reset_bold(); + + writeln!( + buf, + "{head}VERSION{reset} +{sub_bold}-V, --version, --version=min{reset_bold} +{tab}{tab}Sends version information to {bold}STDOUT{reset_bold} with colourful ASCII art. +{tab}{tab}The 'min' option provides a minimalistic output as is provided to non-colour terms.\n" + ) +} + +pub fn copyright(buf: &mut String, layout: &HelpLayout) -> FmtResult { + let head = layout.head(); + let tab = layout.tab(); + let reset = layout.reset(); + + writeln!( + buf, + "{head}COPYRIGHT{reset} +{tab}Copyright (C) 2023-2024 Xavier R.M. + +{tab}This program may be freely redistributed under the +{tab}terms of the GNU General Public License v3 only.\n" + ) +} diff --git a/pacwrap/src/help/version.rs b/pacwrap/src/help/version.rs new file mode 100644 index 0000000..9464b7f --- /dev/null +++ b/pacwrap/src/help/version.rs @@ -0,0 +1,73 @@ +/* + * pacwrap + * + * Copyright (C) 2023-2024 Xavier R.M. + * SPDX-License-Identifier: GPL-3.0-only + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use pacwrap_core::{ + utils::{arguments::Operand, is_truecolor_terminal, Arguments}, + Result, +}; + +use crate::help::version_string; + +fn minimal(args: &mut Arguments) -> bool { + args.into_iter() + .filter(|a| a == &Operand::LongPos("version", "min") || a == &Operand::ShortPos('V', "min")) + .last() + .is_some() +} + +pub fn print_version(mut args: &mut Arguments) -> Result<()> { + let version = format!("{} v{}", env!("CARGO_PKG_NAME"), version_string()); + + if !minimal(&mut args) && is_truecolor_terminal() { + println!("\x1b[?7l\n  PPAACWWRRAAP  +  RAPPPACCCCCCCCCCCCCCCAPA[0R  + RAPPPAC#####CCCCCCCCCCCCCCCCCCCCCCAPAR  +PCCC############CCCCCCCCCCCCCCCCCCCCCCCAPPR  +PCCC##############CCCCCCCCCCCCCCCCCCCCCCCCCCAA {version} +ACCCCCC###########CC####CCC####CCC####CCCCCCCP Copyright (C) 2023-2024 Xavier R.M. +ACCCCCCCC#####CCCCC######C######C###CCCCCCCCCP +ACCCCCCCCCCC######CC####CCC####CCCCCCCCCCCCCCP +ACCCCCCCCCCCCCC##CCCCCCCCCCCCCCCCCCCCCCCCCCCCP Website: https://pacwrap.sapphirus.org/ +ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP Github: https://github.com/pacwrap/pacwrap +ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP +ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP + RPCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP This program may be freely redistributed under the + RPCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP terms of the GNU General Public License v3 only. +  AACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCPAR  +  AACCCCCCCCCCCCCCCCCCCCCCCCCCCCCPPR  +  AACCCCCCCCCCCCCCCCCCCCCCCAPA  +  PCCCCCCCCCCCCCCCCCCAPA  +  PCCCCCCCCCCCCCPPR  + RPCCCCCCCPPR  + RPCAPA \n\x1b[?7h"); + } else { + println!( + "{version} +Copyright (C) 2023-2024 Xavier R.M. + +Website: https://pacwrap.sapphirus.org/ +Github: https://github.com/pacwrap/pacwrap + +This program may be freely redistributed under the +terms of the GNU General Public License v3 only.\n" + ); + } + + Ok(()) +} diff --git a/pacwrap/src/main.rs b/pacwrap/src/main.rs index 2587c34..44244e0 100644 --- a/pacwrap/src/main.rs +++ b/pacwrap/src/main.rs @@ -23,7 +23,7 @@ use crate::utils::list; mod compose; mod exec; -mod manual; +mod help; mod proc; mod query; mod remove; @@ -36,13 +36,13 @@ fn main() { Op::Short('E') | Op::Long("exec") | Op::Value("shell") | Op::Value("run") => exec::execute(arguments), Op::Short('S') | Op::Long("sync") | Op::Value("sync") | Op::Value("init") => sync::synchronize(arguments), Op::Short('L') | Op::Long("list") | Op::Value("ls") | Op::Value("list") => list::list_containers(arguments), - Op::Short('R') | Op::Long("remove") | Op::Value("remove") => remove::remove(arguments), + Op::Short('R') | Op::Long("remove") | Op::Value("remove") | Op::Value("rm") => remove::remove(arguments), + Op::Short('P') | Op::Long("process") | Op::Value("process") | Op::Value("ps") => proc::process(arguments), Op::Short('Q') | Op::Long("query") | Op::Value("query") => query::query(arguments), Op::Short('C') | Op::Long("compose") | Op::Value("compose") => compose::compose(arguments), - Op::Short('P') | Op::Long("process") | Op::Value("ps") => proc::process(arguments), Op::Short('U') | Op::Long("utils") | Op::Value("utils") => utils::engage_utility(arguments), - Op::Short('V') | Op::Long("version") | Op::Value("version") => manual::print_version(arguments), - Op::Short('h') | Op::Long("help") | Op::Value("help") => manual::help(arguments), + Op::Short('V') | Op::Long("version") | Op::Value("version") => help::print_version(arguments), + Op::Short('h') | Op::Long("help") | Op::Value("help") => help::help(arguments), _ => arguments.invalid_operand(), }; diff --git a/pacwrap/src/manual.rs b/pacwrap/src/manual.rs deleted file mode 100644 index 4245c26..0000000 --- a/pacwrap/src/manual.rs +++ /dev/null @@ -1,741 +0,0 @@ -/* - * pacwrap - * - * Copyright (C) 2023-2024 Xavier R.M. - * SPDX-License-Identifier: GPL-3.0-only - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -use indexmap::IndexSet; -use lazy_static::lazy_static; -use std::fmt::{Display, Formatter, Write}; - -use pacwrap_core::{ - err, - impl_error, - utils::{arguments::Operand, is_color_terminal, is_truecolor_terminal, Arguments}, - Error, - ErrorTrait, -}; - -lazy_static! { - static ref HELP_ALL: Vec = [ - HelpTopic::Execute, - HelpTopic::Sync, - HelpTopic::Process, - HelpTopic::Utils, - HelpTopic::Help, - HelpTopic::Env, - HelpTopic::Version, - HelpTopic::Copyright - ] - .into(); -} - -#[derive(Debug)] -enum ErrorKind { - InvalidTopic(String), -} - -impl_error!(ErrorKind); - -impl Display for ErrorKind { - fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { - Self::InvalidTopic(err) => write!(fmter, "Topic '{}' is not available.", err), - }?; - - write!(fmter, "\nTry 'pacwrap -h' for more information on valid operational parameters.") - } -} - -pub fn help(mut args: &mut Arguments) -> Result<(), Error> { - let help = ascertain_help(&mut args)?; - let mut buffer = String::new(); - - for topic in help.0 { - topic.write(&mut buffer, help.1).unwrap(); - } - - if let HelpLayout::Console = help.1 { - print!("\x1b[?7l{}\x1b[?7h", buffer) - } else { - print!("{}", buffer) - } - - Ok(()) -} - -fn ascertain_help<'a>(args: &mut Arguments) -> Result<(IndexSet<&'a HelpTopic>, &'a HelpLayout), Error> { - let mut layout = match is_color_terminal() { - true => &HelpLayout::Console, - false => &HelpLayout::Dumb, - }; - let mut topic: Vec<&HelpTopic> = vec![&HelpTopic::Default]; - let mut more = false; - - while let Some(arg) = args.next() { - match arg { - Operand::Long("format") | Operand::Long("help") | Operand::Short('f') | Operand::Short('h') => continue, - Operand::Short('m') | Operand::Long("more") => more = true, - Operand::ShortPos('f', "man") | Operand::LongPos("format", "man") => layout = &HelpLayout::Man, - Operand::ShortPos('f', "ansi") | Operand::LongPos("format", "ansi") => layout = &HelpLayout::Console, - Operand::ShortPos('f', "dumb") | Operand::LongPos("format", "dumb") => layout = &HelpLayout::Dumb, - Operand::ShortPos('f', "markdown") | Operand::LongPos("format", "markdown") => layout = &HelpLayout::Markdown, - Operand::ShortPos('h', "sync") - | Operand::ShortPos('h', "S") - | Operand::LongPos("help", "sync") - | Operand::LongPos("help", "S") - | Operand::Value("S") - | Operand::Value("sync") => topic.push(&HelpTopic::Sync), - Operand::ShortPos('h', "E") - | Operand::ShortPos('h', "run") - | Operand::ShortPos('h', "exec") - | Operand::LongPos("help", "E") - | Operand::LongPos("help", "run") - | Operand::LongPos("help", "exec") - | Operand::Value("E") - | Operand::Value("exec") - | Operand::Value("run") => topic.push(&HelpTopic::Execute), - Operand::ShortPos('h', "process") - | Operand::ShortPos('h', "ps") - | Operand::ShortPos('h', "P") - | Operand::LongPos("help", "process") - | Operand::LongPos("help", "ps") - | Operand::LongPos("help", "P") - | Operand::Value("P") - | Operand::Value("ps") - | Operand::Value("process") => topic.push(&HelpTopic::Process), - Operand::ShortPos('h', "utils") - | Operand::ShortPos('h', "U") - | Operand::LongPos("help", "utils") - | Operand::LongPos("help", "U") - | Operand::Value("utils") - | Operand::Value("U") => topic.push(&HelpTopic::Utils), - Operand::ShortPos('h', "env") - | Operand::ShortPos('h', "environment") - | Operand::LongPos("help", "env") - | Operand::LongPos("help", "environment") - | Operand::Value("env") - | Operand::Value("environment") => topic.push(&HelpTopic::Env), - Operand::ShortPos('h', "help") - | Operand::ShortPos('h', "h") - | Operand::LongPos("help", "help") - | Operand::LongPos("help", "h") => topic.push(&HelpTopic::Help), - Operand::ShortPos('h', "V") - | Operand::ShortPos('h', "version") - | Operand::LongPos("help", "V") - | Operand::LongPos("help", "version") - | Operand::Value("version") - | Operand::Value("V") => topic.push(&HelpTopic::Version), - Operand::ShortPos('h', "copyright") | Operand::LongPos("help", "copyright") => topic.push(&HelpTopic::Copyright), - Operand::ShortPos('h', "synopsis") | Operand::LongPos("help", "synopsis") => topic.push(&HelpTopic::Default), - Operand::ShortPos('h', "pacwrap.yml") | Operand::LongPos("help", "pacwrap.yml") | Operand::Value("pacwrap.yml") => - topic.push(&HelpTopic::PacwrapYml), - Operand::ShortPos('h', "all") - | Operand::LongPos("help", "all") - | Operand::Short('a') - | Operand::Long("all") - | Operand::Value("all") => topic.extend(HELP_ALL.iter()), - Operand::ShortPos('h', topic) | Operand::LongPos("help", topic) => err!(ErrorKind::InvalidTopic(topic.into()))?, - _ => args.invalid_operand()?, - } - } - - let len = topic.len(); - let start = if more || len == 1 || len > 7 { 0 } else { 1 }; - - args.set_index(1); - Ok((topic.drain(start ..).collect(), layout)) -} - -fn minimal(args: &mut Arguments) -> bool { - match args.next().unwrap_or_default() { - Operand::LongPos("version", "min") | Operand::ShortPos('V', "min") => true, - _ => false, - } -} - -#[derive(Eq, PartialEq, Hash)] -enum HelpTopic { - Sync, - Execute, - Default, - Utils, - Process, - Help, - Env, - Copyright, - Version, - PacwrapYml, -} - -enum HelpLayout { - Man, - Dumb, - Markdown, - Console, -} - -impl HelpLayout { - fn head(&self) -> &str { - match self { - Self::Console => "", - Self::Markdown => "## ", - Self::Man => ".SH\n", - Self::Dumb => "", - } - } - - fn sub_head(&self) -> &str { - match self { - Self::Console => " ", - Self::Markdown => "#### **", - Self::Man => ".TP\n\\fB", - Self::Dumb => " ", - } - } - - fn sub(&self) -> &str { - match self { - Self::Console | Self::Dumb => " ", - Self::Man => ".PP\n", - Self::Markdown => "", - } - } - - fn tab(&self) -> &str { - match self { - Self::Console | Self::Dumb => " ", - Self::Markdown | Self::Man => "", - } - } - - fn reset(&self) -> &str { - match self { - Self::Console => "", - Self::Markdown | Self::Man | Self::Dumb => "", - } - } - - fn reset_bold(&self) -> &str { - match self { - Self::Console => "", - Self::Man => "\\fP", - Self::Markdown => "**", - Self::Dumb => "", - } - } - - fn bold(&self) -> &str { - match self { - Self::Console => "", - Self::Man => "\\fP", - Self::Markdown => "**", - Self::Dumb => "", - } - } - - fn code(&self) -> &str { - match self { - Self::Console | Self::Dumb | Self::Man => "", - Self::Markdown => "```", - } - } -} - -impl HelpTopic { - fn write(&self, buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - match self { - Self::Default => default(buf, layout), - Self::Sync => sync(buf, layout), - Self::Execute => execute(buf, layout), - Self::Process => process(buf, layout), - Self::Utils => utils(buf, layout), - Self::Help => meta(buf, layout), - Self::Copyright => copyright(buf, layout), - Self::Version => version(buf, layout), - Self::Env => environment(buf, layout), - Self::PacwrapYml => pacwrap_yml(buf, layout), - } - } -} - -fn pacwrap_yml(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let tab = layout.tab(); - let bold = layout.bold(); - let sub_head = layout.sub_head(); - let sub = layout.sub(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - let code = layout.code(); - let name = env!("CARGO_PKG_NAME"); - let date = env!("PACWRAP_BUILDSTAMP"); - - match layout { - HelpLayout::Man => writeln!( - buf, - ".nh\n.TH {name}.yml 2 \"{date}\" \"{name} version_string_placeholder\" \"Pacwrap Configuration Directives\"\n" - )?, - HelpLayout::Markdown => writeln!( - buf, - "# Pacwrap Configuration Directives - -This document was generated by the {name} binary with version {} of the program.\n", - version_string(), - )?, - _ => (), - } - - writeln!( - buf, - "{head}NAME{reset} -{sub_head}pacwrap.yml{reset_bold} - pacwrap configuration file. -{sub_head}repositories.conf{reset_bold} - pacman repository file. - -{head}SYNOPSIS{reset} -{sub_head}~/.config/pacwrap/pacwrap.yml{reset_bold} -{sub_head}~/.config/pacwrap/repositories.conf{reset_bold} - -{head}DESCRIPTION{reset} -{tab}Pacwrap upon invocation will attempt to deserialise {bold}pacwrap.yml{reset_bold}(2) at the location specified herein -{tab}compliant with the XDG Directory Specification. Upon activation of {bold}libalpm{reset_bold}(3), the repositories.conf -{tab}file will be deserialised by the pacman_conf crate in order to faciliate the population of package -{tab}repositories and mirrors. - -{sub}Repository configuration is parsed using {bold}pacman.conf{reset_bold}(5)'s ini format. All other options related the -{tab}{bold}libalpm{reset_bold}(3) as defined by {bold}pacman.conf{reset_bold}(5) therein are otherwise ignored. At present by default, -{tab}the repository configuration, as defined, references the mirrorlist defined by your system. -{tab}This ensures as smooth of an out-of-box experience as humanly possible. - -{tab}Optionally you may specify environment variables, as specified in {bold}pacwrap{reset_bold}(1), to override -{tab}these locations. These options are designated for experienced users only. - -{head}EXAMPLE{reset} -{code} -{tab}config: -{tab} logging: Basic -{tab} summary: Table -{tab} progress: -{tab} transact: CondensedForeign -{tab} download: CondensedForeign -{tab}alpm: -{tab} ignore_pkg: -{tab} - nvidia-utils -{tab} - lib32-nvidia-utils -{code} - -{head}SECTIONS{reset} -{sub_head}config:{reset_bold} -{sub}{tab}Configuration pertaining to {bold}pacwrap{reset_bold}(1) are to be declared within this section. - -{sub_head}alpm:{reset_bold} -{sub}{tab}Configuration pertaining to {bold}libalpm{reset_bold}(3) are to be declared within this section. - -{head}CONFIG{reset} -{sub_head}logging{reset_bold}: Basic -{tab}{tab}Logging verbosity specified here. Available options are {bold}Basic{reset_bold}, {bold}Verbose{bold}, and {bold}None{reset_bold}. - -{sub_head}summary{reset_bold}: Basic -{tab}{tab}Transaction summary type. Available options are {bold}Basic{reset_bold}, {bold}BasicForeign{reset_bold}, {bold}Table{reset_bold}, and {bold}TableForeign{reset_bold}. - -{tab}{tab}Each option suffixed with the {bold}Foreign{reset_bold} juxtaposition, will take effect only during the -{tab}{tab}juxtaposed transaction type with otherwise the opposite effect. - -{sub_head}progress:{reset_bold} -{tab}{tab}Progress types are declared within this subsection. - -{head}PROGRESS{reset} -{sub_head}transact{reset_bold}: CondensedForeign -{tab}{tab}Progress type for transaction progress is specified with this option. Available values are -{tab}{tab}{bold}Basic{reset_bold}, {bold}Condensed{bold}, {bold}CondensedForeign{reset_bold}, {bold}CondensedLocal{reset_bold}, and {bold}Verbose{reset_bold}. - -{tab}{tab}Each option suffixed with the {bold}Foreign{reset_bold} or {bold}Local{reset_bold} juxtaposition, will take effect only during -{tab}{tab}the juxtaposed transaction type with otherwise the opposite effect. - -{sub_head}download{reset_bold}: Verbose -{tab}{tab}Download type for download progress is specified with this option. Available values are -{tab}{tab}{bold}Basic{reset_bold}, {bold}Condensed{bold}, {bold}CondensedForeign{reset_bold}, {bold}CondensedLocal{reset_bold}, and {bold}Verbose{reset_bold}. - -{tab}{tab}Each option suffixed with the {bold}Foreign{reset_bold} or {bold}Local{reset_bold} juxtaposition, will take effect only during -{tab}{tab}the juxtaposed transaction type with otherwise the opposite effect. - -{head}ALPM{reset} -{sub_head}ignore_pkg:{reset_bold} -{tab}{tab}Ignored package(s) are declared herein with a string array. - -{sub_head}hold_pkg:{reset_bold} -{tab}{tab}Held package(s) are declared herein with a string array. - -{sub_head}sig_level{reset_bold}: Required DatabaseOptional -{tab}{tab}Default global signature level - see {bold}pacman.conf{reset_bold}(5) for valid options. Options are declared -{tab}{tab}as a singular string value. - -{sub_head}sig_level_local{reset_bold}: Optional -{tab}{tab}Default local signature level - see {bold}pacman.conf{reset_bold}(5) for valid options. Options are declared -{tab}{tab}as a single string value. - -{sub_head}check_space{reset_bold}: true -{tab}{tab}Instructs {bold}libalpm{reset_bold}(3), where applicable, to check if there's available space on disk in order -{tab}{tab}to facilitate a transaction. Value is declared with a {bold}bool{reset_bold}. - -{sub_head}download_timeout{reset_bold}: true -{tab}{tab}Instructs {bold}libalpm{reset_bold}(3) to timeout downloads from unsatisfactory mirrors. Value is declared with -{tab}{tab}a {bold}bool{reset_bold}. - -{sub_head}parallel_downloads{reset_bold}: 1 -{tab}{tab}Instructs {bold}libalpm{reset_bold}(3) to parallelise the download queue with a maximum queue amount. Specify an -{tab}{tab}{bold}integer{reset_bold} to declare a maximum value. - -{head}SEE ALSO{reset} -{tab}{tab}{bold}pacman.conf{reset_bold}(5), {bold}libalpm{reset_bold}(3) - -{head}COPYRIGHT{reset} -{tab}Copyright (C) 2023-2024 Xavier R.M. - -{sub}This program may be freely redistributed under the -{tab}terms of the GNU General Public License v3 only.\n" - ) -} - -fn default(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let tab = layout.tab(); - let sub_head = layout.sub_head(); - let sub = layout.sub(); - let bold = layout.bold(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - let name = env!("CARGO_PKG_NAME"); - let date = env!("PACWRAP_BUILDSTAMP"); - - match layout { - HelpLayout::Man => writeln!(buf, ".nh\n.TH {name} 1 \"{date}\" \"{name} version_string_placeholder\" \"User Manual\"\n")?, - HelpLayout::Markdown => writeln!( - buf, - "# Pacwrap User Manual - -This document was generated by the {name} binary with version {} of the program.\n", - version_string() - )?, - _ => (), - } - - writeln!( - buf, - "{head}NAME{reset} -{tab}pacwrap - -{head}SYNOPSIS{reset} -{tab}pacwrap [{bold}OPERATION{reset_bold}] [{bold}ARGUMENTS{reset_bold}] [{bold}TARGETS{reset_bold}] - -{head}DESCRIPTION{reset} -{tab}A package management front-end which utilises libalpm to facilitate the creation of unprivileged, -{tab}namespace containers with parallelised, filesystem-agnostic deduplication. These containers -{tab}are constructed with bubblewrap to execute package transactions and launch applications. - -{sub}This application is designed to allow for the creation and execution of secure, replicable -{tab}containerised environments for general-purpose use. CLI and GUI applications are all supported. -{tab}Once a container environment is configured, it can be re-established or replicated on any system. - -{head}OPERATIONS{reset} -{sub_head}-S, --sync{reset_bold} -{tab}{tab}Synchronize package databases and update packages in target containers. - -{sub_head}-U, --utils{reset_bold} -{tab}{tab}Invoke miscellaneous utilities to manage containers. - -{sub_head}-P, --process{reset_bold} -{tab}{tab}Manage and show status of running container processes. - -{sub_head}-E, --execute{reset_bold} -{tab}{tab}Executes application in target container using bubblewrap. - -{sub_head}-h, --help=OPTION{reset_bold} -{tab}{tab}Invoke a printout of this manual to {bold}STDOUT{reset_bold}. - -{sub_head}-V, --version{reset_bold} -{tab}{tab}Display version and copyright information in {bold}STDOUT{reset_bold}.\n" - ) -} - -fn execute(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let sub_head = layout.sub_head(); - let tab = layout.tab(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - - writeln!( - buf, - "{head}EXECUTE{reset} -{sub_head}-r, --root{reset_bold} -{tab}{tab}Execute operation with fakeroot and fakechroot. Facilitates a command with faked privileges. - -{sub_head}-s, --shell{reset_bold} -{tab}{tab}Invoke a bash shell\n" - ) -} - -fn environment(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let sub_head = layout.sub_head(); - let tab = layout.tab(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - - writeln!( - buf, - "{head}ENVIRONMENT VARIABLES{reset} -{sub_head}PACWRAP_CONFIG_DIR{reset_bold} -{tab}{tab}Overrides the default XDG Directory Specification compliant configuration directory. - -{sub_head}PACWRAP_DATA_DIR{reset_bold} -{tab}{tab}Overrides the default XDG Directory Specification compliant data directory. - -{sub_head}PACWRAP_CACHE_DIR{reset_bold} -{tab}{tab}Overrides the default XDG Directory Specification compliant cache directory.\n" - ) -} - -fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let bold = layout.bold(); - let sub_head = layout.sub_head(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - let tab = layout.tab(); - - writeln!( - buf, - "{head}HELP{reset} -{sub_head}-m, --more{reset_bold} -{tab}{tab}When specifying a topic to display, show the default topic in addition to specified options. - -{sub_head}-f, --format=FORMAT{reset_bold} -{tab}{tab}Change output format of help in {bold}STDOUT{reset_bold}. Format options include: 'ansi', 'dumb', 'markdown', and 'man'. -{tab}{tab}This option is for the express purposes of generating documentation at build time, and has little utility -{tab}{tab}outside the context of package maintenance. 'man' option produces troff-formatted documents for man pages. - -{sub_head}-a, --all, --help=all{reset_bold} -{tab}{tab}Display all help topics.\n" - ) -} - -fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let bold = layout.bold(); - let tab = layout.tab(); - let sub_head = layout.sub_head(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - - writeln!( - buf, - "{head}SYNCHRONIZATION{reset} -{sub_head}-y, --refresh{reset_bold} -{tab}{tab}Synchronize remote package databases. Specify up to 2 times to force a refresh. - -{sub_head}-u, --upgrade{reset_bold} -{tab}{tab}Execute aggregate upgrade routine on all or specified containers. Use {bold}-t, --target=TARGET{reset_bold} to limit -{tab}{tab}package synchronization operations to the specified target containers. Packages applicable to -{tab}{tab}a target {bold}must{reset_bold} be specified only after the target operand. -{tab}{tab}e.g. '-t electron element-desktop -t mozilla firefox thunderbird' - -{sub_head}-f, --filesystem{reset_bold} -{tab}{tab}Force execution of filesystem synchronization target on all or specified containers. In combination -{tab}{tab}with {bold}-o/--target-only{reset_bold}, in addition to no other specified targets, filesystem slices will be -{tab}{tab}synchronized without package synchronization on all applicable containers. - -{sub_head}-c, --create{reset_bold} -{tab}{tab}Create a container with the first specified target. A container type argument is also required. - -{sub_head}-b, --base{reset_bold} -{tab}{tab}Base container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation. -{tab}{tab} -{tab}{tab}This container type is used as the base layer for all downstream containers. Only one base container -{tab}{tab}dependency per slice or aggregate is supported. Filesystem and package deduplication via slices and -{tab}{tab}aggregate containers are recommended, but optional. - -{sub_head}-s, --slice{reset_bold} -{tab}{tab}Slice container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation. -{tab}{tab} -{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, to ascertain foreign -{tab}{tab}packages and influence ordering of downstream synchronization target(s). Container slicing provides -{tab}{tab}the ability to install packages in a lightweight, sliced filesytem, which aid in the deduplication -{tab}{tab}of common downstream package and filesystem dependencies. -{tab}{tab} -{tab}{tab}Useful for graphics drivers, graphical toolkits, fonts, etc.; these are not meant for applications. - -{sub_head}-a, --aggegrate{reset_bold} -{tab}{tab}Aggregate container type. Specify alongside {bold}-c, --create{reset_bold} to this assign container type during creation. -{tab}{tab} -{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, in order to acertain foreign -{tab}{tab}packages and amalgamate the target. These containers are ideal for installing software with the aid of -{tab}{tab}filesystem and package deduplication. -{tab}{tab} -{tab}{tab}Useful for all general purpose applications, browsers, e-mail clients, and even terminal user interface -{tab}{tab}applications such as IRC clients. It is recommended to base your containers on aggregate type containers. - -{sub_head}-t, --target=TARGET{reset_bold} -{tab}{tab}Specify a target container for the specified operation. - -{sub_head}-d, --dep=DEPEND{reset_bold} -{tab}{tab}Specify a dependent container for the specified operation. - -{sub_head}-o, --target-only{reset_bold} -{tab}{tab}Apply specified operation on the specified target only. - -{sub_head}--force-foreign{reset_bold} -{tab}{tab}Force synchronization of foreign packages on resident container. Useful for when installing -{tab}{tab}a new package in an aggregate container without all the prerequisite foreign dependencies -{tab}{tab}synchronized to this container's package database. - -{sub_head}--dbonly{reset_bold} -{tab}{tab}Transact on resident containers with a database-only transaction. - -{sub_head}--noconfirm{reset_bold} -{tab}{tab}Override confirmation prompts and confirm all operations.\n" - ) -} - -fn process(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let tab = layout.tab(); - let sub_head = layout.sub_head(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - - writeln!( - buf, - "{head}PROCESS{reset} -{sub_head}-s, --summary{reset_bold} -{tab}{tab}Enumerate a process summary of containers being executed by pacwrap. - -{sub_head}-k, --kill{reset_bold} -{tab}{tab}Kill target containers and their associated processes. - -{sub_head}-a, --all{reset_bold} -{tab}{tab}Enumerate all processes associated with running containers. - -{sub_head}-d, --depth{reset_bold} -{tab}{tab}Enumerate all processes at the specified depth associated with running containers. - -{sub_head}-t, --target=TARGET{reset_bold} -{tab}{tab}Specify a target container for the specified operation. - -{sub_head}--noconfirm{reset_bold} -{tab}{tab}Override confirmation prompts and confirm all operations.\n" - ) -} - -fn utils(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let tab = layout.tab(); - let reset = layout.reset(); - - writeln!( - buf, - "{head}UTILITIES{reset} -{tab}{tab}-TODO-\n" - ) -} - -fn version(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let sub_head = layout.sub_head(); - let tab = layout.tab(); - let bold = layout.bold(); - let reset = layout.reset(); - let reset_bold = layout.reset_bold(); - - writeln!( - buf, - "{head}VERSION{reset} -{sub_head}-V, --version, --version=min{reset_bold} -{tab}{tab}Sends version information to {bold}STDOUT{reset_bold} with colourful ASCII art. -{tab}{tab}The 'min' option provides a minimalistic output as is provided to non-colour terms.\n" - ) -} - -fn copyright(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { - let head = layout.head(); - let tab = layout.tab(); - let sub = layout.sub(); - let reset = layout.reset(); - - writeln!( - buf, - "{head}COPYRIGHT{reset} -{tab}Copyright (C) 2023-2024 Xavier R.M. - -{sub}This program may be freely redistributed under the -{tab}terms of the GNU General Public License v3 only.\n" - ) -} - -pub fn print_version(mut args: &mut Arguments) -> Result<(), Error> { - let version = format!("{} v{}", env!("CARGO_PKG_NAME"), version_string()); - - if !minimal(&mut args) && is_truecolor_terminal() { - println!("\x1b[?7l\n  PPAACWWRRAAP  -  RAPPPACCCCCCCCCCCCCCCAPA[0R  - RAPPPAC#####CCCCCCCCCCCCCCCCCCCCCCAPAR  -PCCC############CCCCCCCCCCCCCCCCCCCCCCCAPPR  -PCCC##############CCCCCCCCCCCCCCCCCCCCCCCCCCAA {version} -ACCCCCC###########CC####CCC####CCC####CCCCCCCP Copyright (C) 2023-2024 Xavier R.M. -ACCCCCCCC#####CCCCC######C######C###CCCCCCCCCP -ACCCCCCCCCCC######CC####CCC####CCCCCCCCCCCCCCP -ACCCCCCCCCCCCCC##CCCCCCCCCCCCCCCCCCCCCCCCCCCCP Website: https://pacwrap.sapphirus.org/ -ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP Github: https://github.com/pacwrap/pacwrap -ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP -ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP - RPCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP This program may be freely redistributed under the - RPCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP terms of the GNU General Public License v3 only. -  AACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCPAR  -  AACCCCCCCCCCCCCCCCCCCCCCCCCCCCCPPR  -  AACCCCCCCCCCCCCCCCCCCCCCCAPA  -  PCCCCCCCCCCCCCCCCCCAPA  -  PCCCCCCCCCCCCCPPR  - RPCCCCCCCPPR  - RPCAPA \n\x1b[?7h"); - } else { - println!( - "{version} -Copyright (C) 2023-2024 Xavier R.M. - -Website: https://pacwrap.sapphirus.org/ -Github: https://github.com/pacwrap/pacwrap - -This program may be freely redistributed under the -terms of the GNU General Public License v3 only.\n" - ); - } - - Ok(()) -} - -fn version_string() -> String { - let version = env!("CARGO_PKG_VERSION"); - let release = env!("PACWRAP_BUILD"); - let head = env!("PACWRAP_BUILDHEAD"); - let date = env!("PACWRAP_BUILDSTAMP"); - - if head.is_empty() { - format!("{version} ({date})") - } else { - format!("{version}-{head}-{release} ({date})") - } -} diff --git a/pacwrap/src/sync.rs b/pacwrap/src/sync.rs index 3683dc9..8b38ac1 100644 --- a/pacwrap/src/sync.rs +++ b/pacwrap/src/sync.rs @@ -76,6 +76,7 @@ fn action(args: &mut Arguments) -> (TransactionType, bool) { fn instantiate<'a>( cache: &mut ContainerCache<'a>, + lock: &'a Lock, logger: &mut Logger, action_type: &TransactionType, targets: IndexMap<&'a str, (ContainerType, Vec<&'a str>)>, @@ -102,6 +103,7 @@ fn instantiate<'a>( } } + lock.assert()?; println!("{} {}Instantiating container{}...{}", *BAR_GREEN, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET); for (container, (container_type, deps)) in targets { @@ -239,7 +241,7 @@ fn engage_aggregator<'a>( if create_targets.len() > 0 || init { flags = flags | TransactionFlags::CREATE | TransactionFlags::FORCE_DATABASE; instantiate_trust()?; - instantiate(&mut cache, log, &action_type, create_targets)?; + instantiate(&mut cache, lock, log, &action_type, create_targets)?; } Ok(TransactionAggregator::new(cache, log, action_type) diff --git a/pacwrap/src/utils/delete.rs b/pacwrap/src/utils/delete.rs index 17653ef..93c9224 100644 --- a/pacwrap/src/utils/delete.rs +++ b/pacwrap/src/utils/delete.rs @@ -92,14 +92,14 @@ pub fn remove_containers(args: &mut Arguments) -> Result<()> { let lock = Lock::new().lock()?; if let (true, _) | (_, Ok(_)) = (no_confirm, prompt_targets(&instances, "Delete containers?", false)) { - delete_roots(&cache, &mut logger, &instances, force)?; + delete_roots(&cache, &lock, &mut logger, &instances, force)?; lock.unlock() } else { Ok(()) } } -pub fn delete_roots(cache: &ContainerCache<'_>, logger: &mut Logger, targets: &Vec<&str>, force: bool) -> Result<()> { +pub fn delete_roots(cache: &ContainerCache<'_>, lock: &Lock, logger: &mut Logger, targets: &Vec<&str>, force: bool) -> Result<()> { let process = process::list(&cache)?; let processes = process.filter_by_target(&targets); let containers = cache.filter_target_handle(&targets, vec![]); @@ -115,6 +115,7 @@ pub fn delete_roots(cache: &ContainerCache<'_>, logger: &mut Logger, targets: &V let instance = container.vars().instance(); let state = format!("{}/state/{instance}.dat", *DATA_DIR); + lock.assert()?; remove_dir_all(root).prepend(|| format!("Failed to delete container root '{root}':"))?; if Path::new(&state).exists() {