Ciborium replaced with bincode, and pacwrap-agent attained UX parity

- Refactored libalpm events into their own module
 - Agent parameters are acquired via a temporary file attached to the
   container (Still TODO: Provide a shared communication channel)
 - filesystem state files now contain magic values with file header
   verification
 - Miscellaneous tidy up work
This commit is contained in:
Xavier Moffett 2023-12-23 23:00:31 -05:00
parent f3b2b3861d
commit b84f90d17d
15 changed files with 283 additions and 216 deletions

View file

@ -1,54 +1,71 @@
use std::{fs, io::Stdin, process::exit};
use std::{fs::{self, File}, process::exit, os::unix::prelude::FileExt, env};
use serde::Deserialize;
use pacwrap_core::{constants::{BOLD, RESET},
utils::{print_error, print_warning},
sync::{self, AlpmConfigData,
query_event,
progress_event::{self, ProgressEvent},
use pacwrap_core::{sync::{self, AlpmConfigData,
utils::{erroneous_transaction,
erroneous_preparation},
transaction::{TransactionMetadata,
TransactionHandle,
TransactionType,
transaction::{TransactionHandle,
TransactionType,
TransactionMetadata,
TransactionParameters,
Error,
Result}}};
Result, MAGIC_NUMBER},
event::{download::{DownloadCallback, download_event}, progress::{ProgressEvent, callback}, query::questioncb}},
utils::{print_error, print_warning, read_le_32}, constants::{RESET, BOLD}};
pub fn transact() {
let mut stdin = std::io::stdin();
let alpm_remotes: AlpmConfigData = deserialize_stdin(&mut stdin);
let mode: TransactionType = deserialize_stdin(&mut stdin);
let alpm = sync::instantiate_alpm_agent(&alpm_remotes);
let mut meta: TransactionMetadata = deserialize_stdin(&mut stdin);
let mut handle = TransactionHandle::new(alpm, &mut meta);
let mut header_buffer = vec![0; 7];
let mut file = match File::open("/tmp/agent_params") {
Ok(file) => file,
Err(_) => {
if let Ok(var) = env::var("SHELL") {
if ! var.is_empty() {
print_error("Direct execution of this binary is unsupported.");
}
}
if let Err(error) = conduct_transaction(&mut handle, mode) {
exit(2);
},
};
if let Err(error) = file.read_exact_at(&mut header_buffer, 0) {
print_error(format!("'{}/tmp/agent_params{}': {error}", *BOLD, *RESET));
exit(3);
}
let magic = read_le_32(&header_buffer, 0);
let major: u8 = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap();
let minor: u8 = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap();
let patch: u8 = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap();
if magic != MAGIC_NUMBER {
print_error(format!("Magic number {magic} != {MAGIC_NUMBER}"));
exit(4);
}
if major != header_buffer[4] || minor != header_buffer[5] || patch != header_buffer[6] {
print_error(format!("{major}.{minor}.{patch} != {}.{}.{}", header_buffer[4], header_buffer[5], header_buffer[6]));
exit(5);
}
let params: TransactionParameters = deserialize(&mut file);
let alpm_remotes: AlpmConfigData = deserialize(&mut file);
let mut metadata: TransactionMetadata = deserialize(&mut file);
let alpm = sync::instantiate_alpm_agent(&alpm_remotes);
let mut handle = TransactionHandle::new(alpm, &mut metadata);
if let Err(error) = conduct_transaction(&mut handle, params) {
print_error(error);
handle.alpm_mut().trans_release().ok();
exit(1);
}
}
}
fn deserialize_stdin<T: for<'de> Deserialize<'de>>(stdin: &mut Stdin) -> T {
match ciborium::from_reader::<T, &mut Stdin>(stdin) {
Ok(meta) => meta,
Err(err) => {
if let ciborium::de::Error::Semantic(_, error) = err {
match error.contains("integer `10`") {
false => print_error(format!("Deserialization failure occurred with input from {}STDIN{}: {error}", *BOLD, *RESET)),
true => print_error("Interactive user input is not supported by this program."),
}
}
exit(1);
}
}
}
fn conduct_transaction(handle: &mut TransactionHandle, action: TransactionType) -> Result<()> {
fn conduct_transaction(handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> {
let flags = handle.retrieve_flags();
let mode = handle.get_mode().clone();
let mode = agent.mode();
let action = agent.action();
if let Err(error) = handle.alpm_mut().trans_init(flags.1.unwrap()) {
Err(Error::InitializationFailure(error.to_string().into()))?
@ -68,9 +85,9 @@ fn conduct_transaction(handle: &mut TransactionHandle, action: TransactionType)
erroneous_preparation(error)?
}
handle.alpm().set_progress_cb(ProgressEvent::new(&action), progress_event::callback(&mode));
handle.alpm().set_question_cb((), query_event::questioncb);
//handle.alpm().set_dl_cb(DownloadCallback::new(), dl_event::download_event);
handle.alpm().set_progress_cb(ProgressEvent::new(&action), callback(&mode));
handle.alpm().set_question_cb((), questioncb);
handle.alpm().set_dl_cb(DownloadCallback::new(agent.bytes(), agent.files()), download_event);
if let Err(error) = handle.alpm_mut().trans_commit() {
erroneous_transaction(error)?
@ -78,10 +95,20 @@ fn conduct_transaction(handle: &mut TransactionHandle, action: TransactionType)
handle.alpm_mut().trans_release().unwrap();
handle.mark_depends();
if let Err(error) = fs::copy("/etc/ld.so.cache", "/mnt/etc/ld.so.cache") {
print_warning(format!("Failed to propagate ld.so.cache: {}", error));
}
Ok(())
}
fn deserialize<T: for<'de> Deserialize<'de>>(stdin: &mut File) -> T {
match bincode::deserialize_from::<&mut File, T>(stdin) {
Ok(meta) => meta,
Err(error) => {
print_error(format!("Deserilization error: {}", error.as_ref()));
exit(3);
}
}
}

View file

@ -1,5 +1,4 @@
use pacwrap_core::utils::{print_error, Arguments, arguments::Operand};
use serde::{Serialize, Deserialize};
mod agent;
@ -9,14 +8,6 @@ fn main() {
match param {
Operand::Value("transact") => agent::transact(),
_ => print_error(arguments.invalid_operand().to_string())
_ => print_error("Direct execution of this binary is unsupported.")
}
}
#[derive(Serialize, Deserialize, Clone)]
struct Test {
string: String,
version_major: i16,
version_minor: i16,
}

View file

@ -18,6 +18,7 @@ lazy_static! {
pub static ref CACHE_DIR: &'static str = env_default("PACWRAP_CACHE_DIR", PACWRAP_CACHE_DIR);
pub static ref CONFIG_DIR: &'static str = env_default("PACWRAP_CONFIG_DIR", PACWRAP_CONFIG_DIR);
pub static ref DATA_DIR: &'static str = env_default("PACWRAP_DATA_DIR", PACWRAP_DATA_DIR);
pub static ref PACWRAP_AGENT_FILE: &'static str = format!("/run/user/{}/pacwrap_agent_{}", geteuid(), &id()).leak();
pub static ref XDG_RUNTIME_DIR: String = format!("/run/user/{}", geteuid());
pub static ref DBUS_SOCKET: String = format!("/run/user/{}/pacwrap_dbus_{}", geteuid(), &id());
pub static ref LOG_LOCATION: &'static str = format!("{}/pacwrap.log", *DATA_DIR).leak();

View file

@ -1,6 +1,6 @@
use std::{process::{Child, Command, Stdio}, io::Error, env::var};
use std::{process::{Child, Command}, io::Error, env::var};
use crate::{constants::{BWRAP_EXECUTABLE, self},
use crate::{constants::{BWRAP_EXECUTABLE, self, PACWRAP_AGENT_FILE},
config::InstanceHandle,
ErrorKind};
@ -10,11 +10,11 @@ pub fn execute_agent(ins: &InstanceHandle) -> Result<Child,Error> {
Command::new(BWRAP_EXECUTABLE)
.env_clear()
.stdin(Stdio::piped())
.arg("--bind").arg(&ins.vars().root()).arg("/mnt")
.arg("--tmpfs").arg("/tmp")
.arg("--tmpfs").arg("/etc")
.arg("--symlink").arg("/mnt/usr").arg("/usr")
.arg("--symlink").arg("/mnt/usr").arg("/usr")
.arg("--ro-bind").arg(*PACWRAP_AGENT_FILE).arg("/tmp/agent_params")
.arg("--ro-bind").arg(format!("{}/lib", dist_img)).arg("/lib64")
.arg("--ro-bind").arg(format!("{}/bin", dist_img)).arg("/bin")
.arg("--ro-bind").arg("/etc/resolv.conf").arg("/etc/resolv.conf")

View file

@ -7,7 +7,7 @@ use serde::{Serialize, Deserialize};
use crate::{utils::{print_warning, print_error},
constants::{BAR_GREEN, RESET, BOLD, ARROW_RED, CACHE_DIR, DATA_DIR, CONFIG_DIR},
sync::dl_event::DownloadCallback,
sync::event::download::{DownloadCallback, download_event},
config::{InsVars,
InstanceHandle,
cache::InstanceCache}};
@ -18,9 +18,7 @@ lazy_static! {
static ref DEFAULT_ALPM_CONF: AlpmConfigData = AlpmConfigData::new();
}
pub mod progress_event;
pub mod dl_event;
pub mod query_event;
pub mod event;
pub mod utils;
pub mod transaction;
pub mod filesystem;
@ -104,7 +102,7 @@ fn synchronize_database(cache: &InstanceCache, force: bool) {
let mut handle = alpm_handle(&ins.vars(), db_path, &*DEFAULT_ALPM_CONF);
println!("{} {}Synchronising package databases...{}", *BAR_GREEN, *BOLD, *RESET);
handle.set_dl_cb(DownloadCallback::new(0, 0), dl_event::download_event);
handle.set_dl_cb(DownloadCallback::new(0, 0), download_event);
if let Err(err) = handle.syncdbs_mut().update(force) {
print_error(format!("Unable to initialize transaction: {}.",err.to_string()));

View file

@ -0,0 +1,16 @@
pub mod download;
pub mod query;
pub mod progress;
fn whitespace(total: usize, current: usize) -> String {
let mut whitespace = String::new();
let difference = total-current;
if difference > 0 {
for _ in 0..difference {
whitespace.push_str(" ");
}
}
whitespace
}

View file

@ -5,7 +5,7 @@ use dialoguer::console::Term;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use super::utils::whitespace;
use super::whitespace;
lazy_static!{
static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}")
@ -92,7 +92,8 @@ pub fn download_event(file: &str, download: AnyDownloadEvent, this: &mut Downloa
this.files_done += 1;
total.set_message(format!("Total ({}{}/{})",
whitespace(this.total_files_len, this.files_done.to_string().len()),
whitespace(this.total_files_len,
this.files_done.to_string().len()),
this.files_done,
this.total_files));

View file

@ -1,19 +1,16 @@
use std::rc::Rc;
use alpm::Progress;
use dialoguer::console::Term;
use indicatif::{ProgressBar, ProgressStyle};
use crate::constants::{BOLD, RESET};
use super::{utils::whitespace, transaction::{TransactionType, TransactionMode}};
use crate::{constants::{BOLD, RESET}, sync::transaction::{TransactionType, TransactionMode}};
use super::whitespace;
#[derive(Clone)]
pub struct ProgressEvent {
progress: ProgressBar,
offset: usize,
style: ProgressStyle,
current: Rc<str>,
current: String,
}
impl ProgressEvent {
@ -60,7 +57,7 @@ pub fn condensed(progress: Progress, pkgname: &str, percent: i32, howmany: usize
let progress_name: String = name(progress,pkgname);
let whitespace = whitespace(total.to_string().len(), pos.to_string().len());
if this.current.as_ref() != "" {
if this.current != "" {
this.progress = ProgressBar::new(howmany as u64);
this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET));
this.progress.set_style(this.style.clone());
@ -95,7 +92,7 @@ fn name(progress: Progress, pkgname: &str) -> String {
}
}
fn ident(progress: Progress, pkgname: &str) -> Rc<str> {
fn ident(progress: Progress, pkgname: &str) -> String {
match progress {
Progress::KeyringStart => "keyring",
Progress::IntegrityStart => "integrity",

View file

@ -1,84 +1,53 @@
use std::fmt;
use std::fs::{self, File, Metadata};
use std::os::unix::fs::symlink;
use std::os::unix::prelude::MetadataExt;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{Sender, self, Receiver};
use std::{fs::{self, File, Metadata},
io::Read,
os::unix::fs::symlink,
os::unix::prelude::MetadataExt,
path::Path,
sync::{Arc, mpsc::{Sender, self, Receiver}},
collections::{HashMap, HashSet}};
use dialoguer::console::Term;
use rayon::prelude::*;
use rayon::{ThreadPool, ThreadPoolBuilder};
use rayon::{prelude::*, {ThreadPool, ThreadPoolBuilder}};
use indexmap::IndexMap;
use indicatif::{ProgressBar, ProgressStyle, ProgressDrawTarget};
use serde::{Serialize, Deserialize, Deserializer, Serializer, de::Visitor};
use serde::{Serialize, Deserialize};
use walkdir::WalkDir;
use std::collections::{HashMap, HashSet};
use crate::ErrorKind;
use crate::config::{InstanceHandle, InstanceCache, InstanceType::*};
use crate::constants::{RESET, BOLD, ARROW_CYAN, BAR_GREEN, DATA_DIR};
use crate::utils::{print_warning, print_error};
use crate::{ErrorKind,
config::{InstanceHandle, InstanceCache, InstanceType::*},
constants::{RESET, BOLD, ARROW_CYAN, BAR_GREEN, DATA_DIR},
utils::{print_warning, print_error, read_le_32}};
impl Serialize for FileType {
fn serialize<D: Serializer>(&self, serializer: D) -> Result<D::Ok, D::Error>
where D: serde::Serializer {
serializer.serialize_i64(self.as_integer())
}
}
impl <'de>Deserialize<'de> for FileType {
fn deserialize<D: Deserializer<'de>>(serializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
serializer.deserialize_i64(FileTypeVisitor)
}
}
struct FileTypeVisitor;
impl<'de> Visitor<'de> for FileTypeVisitor {
type Value = FileType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "an integer between `0` and `2`")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where E: serde::de::Error, {
let value = v.into();
if let FileType::Invalid(v) = value {
Err(E::invalid_value(serde::de::Unexpected::Signed(v), &self))?
}
Ok(value)
}
}
static VERSION: u32 = 1;
static MAGIC_NUMBER: u32 = 408948530;
#[derive(Serialize, Deserialize, Clone)]
struct FileSystemState {
magic: u32,
version: u32,
files: IndexMap<Arc<str>, (FileType, Arc<str>)>
}
impl FileSystemState {
fn new() -> Self {
Self {
files: IndexMap::new()
magic: MAGIC_NUMBER,
version: VERSION,
files: IndexMap::new(),
}
}
}
#[derive(Clone)]
#[derive(Serialize, Deserialize, Clone)]
enum FileType {
HardLink,
SymLink,
Directory,
Invalid(i64),
Invalid(i8),
}
impl From<i64> for FileType {
fn from(integer: i64) -> Self {
impl From<i8> for FileType {
fn from(integer: i8) -> Self {
match integer {
2 => Self::Directory,
1 => Self::SymLink,
@ -100,23 +69,11 @@ impl From<Metadata> for FileType {
}
}
impl FileType {
fn as_integer(&self) -> i64 {
match self {
Self::Directory => 2,
Self::SymLink => 1,
Self::HardLink => 0,
Self::Invalid(i) => *i,
}
}
}
enum SyncMessage {
LinkComplete(Arc<str>),
SaveState(Arc<str>, FileSystemState),
}
#[allow(dead_code)]
pub struct FileSystemStateSync<'a> {
state_map: HashMap<Arc<str>, FileSystemState>,
state_map_prev: HashMap<Arc<str>, FileSystemState>,
@ -228,39 +185,53 @@ impl <'a>FileSystemStateSync<'a> {
return st.clone()
}
let mut header_buffer = vec![0; 8];
let path = format!("{}/state/{}.dat", *DATA_DIR, instance);
let file = match File::open(&path) {
let mut file = match File::open(&path) {
Ok(file) => file,
Err(err) => {
let state = FileSystemState::new();
Err(err) => {
if err.kind() != std::io::ErrorKind::NotFound {
print_warning(format!("Reading '{}': {}", path, err.kind()));
print_error(format!("'{}': {}", path, err.kind()));
}
self.state_map_prev.insert(instance.clone(), state.clone());
return state
return self.blank_state(instance);
},
};
match ciborium::from_reader::<FileSystemState, File>(file) {
if let Err(error) = file.read_exact(&mut header_buffer) {
print_error(format!("'{}{instance}{}.dat': {error}", *BOLD, *RESET));
return self.blank_state(instance);
}
let magic = read_le_32(&header_buffer, 0);
let version = read_le_32(&header_buffer, 4);
if magic != MAGIC_NUMBER {
print_warning(format!("'{}{instance}{}.dat': Magic number mismatch ({MAGIC_NUMBER} != {magic})", *BOLD, *RESET));
return self.blank_state(instance);
} else if version != VERSION {
return self.blank_state(instance);
}
match bincode::deserialize_from::<&File, FileSystemState>(&file) {
Ok(state) => {
self.state_map_prev.insert(instance.clone(), state.clone());
state
},
Err(err) => {
let state = FileSystemState::new();
if let ciborium::de::Error::Semantic(_, error) = err {
print_error(format!("Deserialization failure occurred with '{}{instance}{}.dat': {error}", *BOLD, *RESET));
}
self.state_map_prev.insert(instance.clone(), state.clone());
state
print_error(format!("Deserialization failure occurred with '{}{instance}{}.dat': {}", *BOLD, *RESET, err.as_ref()));
return self.blank_state(instance);
}
}
}
fn blank_state(&mut self, instance: &Arc<str>) -> FileSystemState {
let state = FileSystemState::new();
self.state_map_prev.insert(instance.clone(), state.clone());
state
}
fn write(&mut self, tx: Sender<()>, ds: FileSystemState, dep: Arc<str>) {
let path: &str = &format!("{}/state/{}.dat", *DATA_DIR, dep);
let output = match File::create(path) {
@ -272,7 +243,7 @@ impl <'a>FileSystemStateSync<'a> {
};
self.pool().unwrap().spawn(move ||{
if let Err(err) = ciborium::into_writer(&ds, output) {
if let Err(err) = bincode::serialize_into(output, &ds) {
print_error(format!("Serialization failure occurred with '{}{dep}{}.dat': {}", *BOLD, *RESET, err.to_string()));
}

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, collections::HashSet, fmt::{Display, Formatter}};
use std::{borrow::Cow, collections::HashSet, fmt::{Display, Formatter}, fs::remove_file};
use bitflags::bitflags;
use alpm::{Alpm, PackageReason, TransFlag};
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::{config,
config::InstanceHandle,
constants::{RESET, BOLD, ARROW_CYAN, BAR_CYAN, ARROW_RED},
constants::{RESET, BOLD, ARROW_CYAN, BAR_CYAN, ARROW_RED, PACWRAP_AGENT_FILE},
sync::{
resolver_local::LocalDependencyResolver,
resolver::DependencyResolver,
@ -29,8 +29,15 @@ mod stage;
pub type Result<T> = std::result::Result<T, Error>;
pub static MAGIC_NUMBER: u32 = 663445956;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum Error {
AgentError,
TransactionFailureAgent,
ParameterAcquisitionFailure,
DeserializationFailure,
InvalidMagicNumber,
AgentVersionMismatch,
NothingToDo,
DependentContainerMissing(String),
RecursionDepthExceeded(isize),
@ -104,6 +111,19 @@ pub struct TransactionMetadata<'a> {
flags: (u8, u32)
}
#[derive(Serialize, Deserialize)]
pub struct TransactionParameters {
magic: u32,
ver_major: u8,
ver_minor: u8,
ver_patch: u8,
bytes: u64,
files: u64,
action: TransactionType,
mode: TransactionMode,
}
impl TransactionMode {
fn bool(&self) -> bool {
match self {
@ -188,6 +208,10 @@ impl Display for Error {
Self::InitializationFailure(msg) => write!(fmter, "Failure to initialize transaction: {msg}"),
Self::PreparationFailure(msg) => write!(fmter, "Failure to prepare transaction: {msg}"),
Self::TransactionFailure(msg) => write!(fmter, "Failure to commit transaction: {msg}"),
Self::DeserializationFailure => write!(fmter, "Deserialization of input parameters failed."),
Self::ParameterAcquisitionFailure => write!(fmter, "Failure to acquire agent runtime parameters."),
Self::AgentVersionMismatch => write!(fmter, "Agent binary mismatch."),
Self::InvalidMagicNumber => write!(fmter, "Deserialization of input parameters failed: Invalid magic number."),
Self::InternalError(msg) => write!(fmter, "Internal failure: {msg}"),
_ => write!(fmter, "Nothing to do."),
}
@ -396,14 +420,16 @@ impl <'a>TransactionHandle<'a> {
fn release_on_fail(self, error: Error) {
match error {
Error::AgentError => (), _ => print_error(error),
Error::TransactionFailureAgent => (), _ => print_error(error),
}
remove_file(*PACWRAP_AGENT_FILE).ok();
println!("{} Transaction failed.", *ARROW_RED);
drop(self);
}
pub fn release(self) {
remove_file(*PACWRAP_AGENT_FILE).ok();
drop(self);
}
@ -439,3 +465,34 @@ impl <'a>TransactionHandle<'a> {
&self.meta
}
}
impl TransactionParameters {
fn new(t_type: TransactionType, t_mode: TransactionMode, dl_size: u64, amount: usize) -> Self {
Self {
magic: MAGIC_NUMBER,
ver_major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
ver_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
ver_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
bytes: dl_size,
files: amount as u64,
action: t_type,
mode: t_mode,
}
}
pub fn bytes(&self) -> u64 {
self.bytes
}
pub fn files(&self) -> usize {
self.files as usize
}
pub fn mode(&self) -> TransactionMode {
self.mode
}
pub fn action(&self) -> TransactionType {
self.action
}
}

View file

@ -1,24 +1,28 @@
use std::process::ChildStdin;
use std::{fs::File, path::Path};
use alpm::Alpm;
use dialoguer::console::Term;
use serde::Serialize;
use crate::{exec::utils::execute_agent, sync::{DEFAULT_ALPM_CONF, utils::erroneous_preparation, self}};
use simplebyteunit::simplebyteunit::{SI, ToByteUnit};
use crate::constants::{RESET, BOLD, DIM};
use crate::{exec::utils::execute_agent,
sync::{DEFAULT_ALPM_CONF, utils::erroneous_preparation, self},
ErrorKind,
utils::prompt::prompt,
constants::{PACWRAP_AGENT_FILE, RESET, BOLD, DIM},
config::InstanceHandle,
};
use crate::utils::prompt::prompt;
use crate::config::InstanceHandle;
use super::{Transaction,
TransactionState,
TransactionHandle,
TransactionAggregator,
TransactionFlags,
TransactionParameters,
Result,
Error};
#[allow(dead_code)]
pub struct Commit {
state: TransactionState,
keyring: bool,
@ -53,56 +57,72 @@ impl Transaction for Commit {
erroneous_preparation(error)?
}
if let Some(result) = confirm(&self.state, ag, handle) {
let result = confirm(&self.state, ag, handle);
let download = result.1.unwrap_or((0,0));
if let Some(result) = result.0 {
return result;
}
handle.set_alpm(None);
match execute_agent(inshandle) {
Ok(mut child) => {
let stdin = child.stdin.take().unwrap();
handle.set_alpm(None);
write_agent_params(ag, handle, download)?;
let mut agent = match execute_agent(inshandle) {
Ok(child) => child,
Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?,
};
write_to_stdin(&*DEFAULT_ALPM_CONF, &stdin)?;
write_to_stdin(ag.action(), &stdin)?;
write_to_stdin(handle.metadata(), &stdin)?;
match agent.wait() {
Ok(exit_status) => match exit_status.code().unwrap_or(-1) {
0 => {
if self.keyring {
ag.keyring_update(inshandle)?;
}
match child.wait() {
Ok(exit_status) => match exit_status.code().unwrap_or(0) {
1 => Err(Error::AgentError),
0 => {
if self.keyring {
ag.keyring_update(inshandle)?;
}
handle.set_alpm(Some(sync::instantiate_alpm(inshandle)));
handle.apply_configuration(inshandle, ag.flags().intersects(TransactionFlags::CREATE));
ag.logger().log(format!("container {instance}'s {state} transaction complete")).ok();
state_transition(&self.state, handle, true)
},
_ => Err(Error::TransactionFailure(format!("Generic failure of agent: Exit code {}", exit_status.code().unwrap_or(0))))?,
},
Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?,
}
handle.set_alpm(Some(sync::instantiate_alpm(inshandle)));
handle.apply_configuration(inshandle, ag.flags().intersects(TransactionFlags::CREATE));
ag.logger().log(format!("container {instance}'s {state} transaction complete")).ok();
state_transition(&self.state, handle, true)
},
1 => Err(Error::TransactionFailureAgent),
2 => Err(Error::ParameterAcquisitionFailure),
3 => Err(Error::DeserializationFailure),
4 => Err(Error::InvalidMagicNumber),
5 => Err(Error::AgentVersionMismatch),
_ => Err(Error::TransactionFailure(format!("Generic failure of agent: Exit code {}", exit_status.code().unwrap_or(-1))))?,
},
Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?,
Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?,
}
}
}
fn write_to_stdin<T: for<'de> Serialize>(input: &T, stdin: &ChildStdin) -> Result<()> {
match ciborium::into_writer::<T, &ChildStdin>(input, stdin) {
fn write_agent_params(ag: &TransactionAggregator, handle: &TransactionHandle, download: (u64, usize)) -> Result<()> {
let f = match File::create(Path::new(*PACWRAP_AGENT_FILE)) {
Ok(f) => f,
Err(error) => Err(ErrorKind::IOError((*PACWRAP_AGENT_FILE).into(), error.kind()))?
};
serialize(&TransactionParameters::new(*ag.action(), *handle.get_mode(), download.0, download.1), &f)?;
serialize(&*DEFAULT_ALPM_CONF, &f)?;
serialize(handle.metadata(), &f)?;
Ok(())
}
fn serialize<T: for<'de> Serialize>(input: &T, file: &File) -> Result<()> {
match bincode::serialize_into::<&File, T>(file, input) {
Ok(()) => Ok(()),
Err(error) => Err(Error::TransactionFailure(format!("Agent data serialization failed: {}", error))),
}
}
fn confirm(state: &TransactionState, ag: &mut TransactionAggregator, handle: &mut TransactionHandle) -> Option<Result<TransactionState>> {
fn confirm(state: &TransactionState, ag: &mut TransactionAggregator, handle: &mut TransactionHandle) -> (Option<Result<TransactionState>>, Option<(u64, usize)>) {
let mut download = None;
if ! handle.get_mode().bool() || ag.flags().intersects(TransactionFlags::DATABASE_ONLY | TransactionFlags::FORCE_DATABASE) {
summary(handle.alpm());
download = Some(summary(handle.alpm()));
if ag.flags().contains(TransactionFlags::PREVIEW) {
return Some(state_transition(state, handle, false));
return (Some(state_transition(state, handle, false)), None);
}
if ! ag.flags().contains(TransactionFlags::NO_CONFIRM) {
@ -110,13 +130,13 @@ fn confirm(state: &TransactionState, ag: &mut TransactionAggregator, handle: &mu
let query = format!("Proceed with {action}?");
if let Err(_) = prompt("::", format!("{}{query}{}", *BOLD, *RESET), true) {
return Some(state_transition(state, handle, false));
return (Some(state_transition(state, handle, false)), None);
}
}
}
handle.alpm_mut().trans_release().ok();
None
(None, download)
}
fn state_transition<'a>(state: &TransactionState, handle: &mut TransactionHandle, updated: bool) -> Result<TransactionState> {
@ -129,8 +149,7 @@ fn state_transition<'a>(state: &TransactionState, handle: &mut TransactionHandle
})
}
#[allow(unused_variables)]
fn summary(handle: &Alpm) {
fn summary(handle: &Alpm) -> (u64, usize) {
let mut installed_size_old: i64 = 0;
let mut installed_size: i64 = 0;
let mut download: i64 = 0;
@ -184,8 +203,8 @@ fn summary(handle: &Alpm) {
if download > 0 {
println!("{}Total Download Size{}: {}", *BOLD, *RESET, download.to_byteunit(SI));
//handle.set_dl_cb(DownloadCallback::new(download as u64, files_to_download), dl_event::download_event);
}
println!();
(download as u64, files_to_download)
}

View file

@ -60,18 +60,6 @@ impl AlpmUtils for Alpm {
}
}
pub fn whitespace(total: usize, current: usize) -> String {
let difference = total-current;
let mut whitespace = String::new();
if difference > 0 {
for _ in 0..difference {
whitespace.push_str(" ");
}
}
whitespace
}
pub fn erroneous_transaction<'a>(error: (CommitResult<'a>, alpm::Error)) -> Result<()> {
match error.0 {
CommitResult::FileConflict(file) => {

View file

@ -52,7 +52,6 @@ pub fn is_color_terminal() -> bool {
is_dumb && isatty(0).is_ok() && isatty(1).is_ok()
}
pub fn is_truecolor_terminal() -> bool {
let colorterm = match var("COLORTERM") {
Ok(value) => {
@ -66,6 +65,10 @@ pub fn is_truecolor_terminal() -> bool {
is_color_terminal() && colorterm
}
pub fn read_le_32(vec: &Vec<u8>, pos: usize) -> u32 {
((vec[pos+0] as u32) << 0) + ((vec[pos+1] as u32) << 8) + ((vec[pos+2] as u32) << 16) + ((vec[pos+3] as u32) << 24)
}
fn wait_on_process(mut child: Child) {
child.wait().ok();
}

View file

@ -154,5 +154,3 @@ impl Display for InvalidArgument {
}
}
}