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:
parent
f3b2b3861d
commit
b84f90d17d
15 changed files with 283 additions and 216 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()));
|
||||
|
|
16
pacwrap-core/src/sync/event.rs
Normal file
16
pacwrap-core/src/sync/event.rs
Normal 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
|
||||
}
|
|
@ -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));
|
||||
|
|
@ -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",
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -154,5 +154,3 @@ impl Display for InvalidArgument {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue