diff --git a/src/cleanup/mod.rs b/src/cleanup/mod.rs deleted file mode 100644 index f6de2cb..0000000 --- a/src/cleanup/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -use { - super::Hooks, - crate::{InstallError, InstallMessage}, - rayon::prelude::{IntoParallelRefIterator, ParallelIterator}, - std::{error::Error, sync::mpsc::Sender}, -}; - -#[derive(Debug, Default)] -pub struct Cleanup { - hooks: Vec, -} - -impl Cleanup { - pub fn new() -> Self { - Self::default() - } - - pub fn push(&mut self, hook: Hooks) { - match hook { - Hooks::Info(_) | Hooks::User(_, _) | Hooks::Group(_, _) | Hooks::Pinstall(_) => { - self.hooks.push(hook) - } - Hooks::Man | Hooks::GlibSchema => { - if !self.hooks.contains(&hook) { - self.hooks.push(hook); - } - } - } - } - - pub fn run(&self, sender: Sender) -> Result<(), Box> { - self.hooks - .par_iter() - .try_for_each_with(sender, |sender, hook| { - let output = hook.run()?; - match hook { - Hooks::Man => sender.send(InstallMessage::Man)?, - Hooks::Info(s) => sender.send(InstallMessage::Info(s.clone()))?, - Hooks::GlibSchema => sender.send(InstallMessage::GlibSchemas)?, - Hooks::User(u, _) => sender.send(InstallMessage::UserCreated(u.clone()))?, - Hooks::Group(g, _) => sender.send(InstallMessage::GroupCreated(g.clone()))?, - Hooks::Pinstall(_) => { - sender.send(InstallMessage::PostInstallStdout(output.stdout))?; - sender.send(InstallMessage::PostInstallStderr(output.stderr))?; - } - } - Ok::<(), InstallError>(()) - })?; - Ok(()) - } -} diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs deleted file mode 100644 index 4012f37..0000000 --- a/src/hooks/mod.rs +++ /dev/null @@ -1,98 +0,0 @@ -use { - crate::{Group, InstallError, User}, - std::{ - path::PathBuf, - process::{Command, Output}, - }, -}; - -#[derive(Debug, Clone, PartialEq)] -/// A post install script extracted from a package archive and the sysroot in -/// which it should be run (usually "/") -pub struct Pinstall { - script: PathBuf, - root: PathBuf, -} - -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -/// Defines a set of commands to be run in order to finish package installation -pub enum Hooks { - /// Runs `makewhatis` to update the mandoc database if the package contains - /// any Unix man pages - Man, - /// Runs `glib-compile-schemas` if the package contains any GLib schema files - GlibSchema, - /// runs `install-info` to update the GNU Texinfo database - Info(String), - /// runs the post install script for a package - Pinstall(Pinstall), - /// creates a new system user - User(User, Option), - /// creates a new system group - Group(Group, Option), -} - -impl From for Hooks { - fn from(value: Pinstall) -> Self { - Self::Pinstall(value) - } -} - -impl From<(User, Option)> for Hooks { - fn from(value: (User, Option)) -> Self { - Self::User(value.0, value.1) - } -} - -impl From<(Group, Option)> for Hooks { - fn from(value: (Group, Option)) -> Self { - Self::Group(value.0, value.1) - } -} - -impl Hooks { - /// Runs a hook and returns it's output - pub fn run(&self) -> Result { - match self { - Self::Man => makeinfo(), - Self::GlibSchema => compile_schemas(), - Self::Info(path) => install_info(path), - Self::Pinstall(p) => p.run(), - Self::User(_u, _p) => unimplemented!(), - Self::Group(_g, _p) => unimplemented!(), - } - } -} - -fn makeinfo() -> Result { - Command::new("makewhatis").output().map_err(Into::into) -} - -fn compile_schemas() -> Result { - Command::new("glib-compile-schemas") - .arg("/usr/share/glib-2.0/schemas") - .output() - .map_err(Into::into) -} - -fn install_info(path: &str) -> Result { - Command::new("install-info") - .arg(path) - .output() - .map_err(Into::into) -} - -impl Pinstall { - pub fn new(script: PathBuf, root: PathBuf) -> Self { - Self { script, root } - } - - fn run(&self) -> Result { - Command::new("/bin/sh") - .arg(self.script.to_str().unwrap_or("")) - .env("HPK_ROOT", self.root.to_str().unwrap_or("")) - .output() - .map_err(Into::into) - } -} diff --git a/src/hpk.rs b/src/hpk.rs index 5301c36..59db98d 100644 --- a/src/hpk.rs +++ b/src/hpk.rs @@ -1,11 +1,18 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::missing_errors_doc)] +use std::path::Path; + +use hpk::Hooks; + mod cli; use { clap::ArgMatches, cli::cli, - hpk::{Cleanup, CreationError, Creator, Dependency, InstallMessage, Installer, Message, Specs, Version}, + hpk::{ + CreationError, Creator, Dependency, InstallMessage, Installer, Message, Specs, + Version, + }, indicatif::{ProgressBar, ProgressStyle}, ron::ser::{to_writer_pretty, PrettyConfig}, std::{ @@ -118,6 +125,10 @@ fn install_local + fmt::Display>( let pb = ProgressBar::new(0); pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap()); pb.println(format!("Installing package {}", archive,)); + let Some(r) = PathBuf::from(&root).to_str().map(|x| x.to_string()) else { + return Err(io::Error::new(ErrorKind::Other, "bad path").into()); + }; + let mut hooks = Hooks::new(&r); let installer = Installer::new_for_file(root, archive)?; let handle = thread::spawn(move || { for msg in receiver.iter() { @@ -136,7 +147,6 @@ fn install_local + fmt::Display>( } } }); - let mut hooks = Cleanup::new(); installer.install(&mut hooks, sender)?; match handle.join() { Ok(package) => { diff --git a/src/installer/hooks.rs b/src/installer/hooks.rs new file mode 100644 index 0000000..99876b9 --- /dev/null +++ b/src/installer/hooks.rs @@ -0,0 +1,147 @@ +use crate::{Group, InstallError, InstallMessage, User}; +use std::{ + path::PathBuf, + process::{Command, Output}, + sync::mpsc::Sender, +}; + +#[derive(Debug, Clone)] +pub struct Hooks { + root: PathBuf, + man: bool, + glib_schema: bool, + info: Vec, + pinstall: Vec, + users: Vec, + groups: Vec, +} + +impl Default for Hooks { + fn default() -> Self { + Self { + root: PathBuf::from("/"), + man: false, + glib_schema: false, + info: vec![], + pinstall: vec![], + users: vec![], + groups: vec![], + } + } +} + +impl Hooks { + pub fn new(root: &str) -> Self { + let mut h = Self::default(); + h.root = PathBuf::from(PathBuf::from(root)); + h + } + + pub fn push_man(&mut self) { + self.man = true; + } + + pub fn push_glib_schema(&mut self) { + self.glib_schema = true; + } + + pub fn push_info(&mut self, file: &str) { + self.info.push(PathBuf::from(file)); + } + + pub fn push_pinstall(&mut self, pinstall: PathBuf) { + self.pinstall.push(pinstall); + } + + pub fn push_user(&mut self, user: User) { + self.users.push(user); + } + + pub fn push_group(&mut self, group: Group) { + self.groups.push(group); + } + + pub fn makewhatis( + &self, + sender: Sender, + ) -> Result, InstallError> { + if self.man { + let output = Command::new("makewhatis") + .output() + .map_err(Into::::into)?; + sender.send(InstallMessage::Man)?; + Ok(Some(output)) + } else { + Ok(None) + } + } + + pub fn compile_schemas( + &self, + sender: Sender, + ) -> Result, InstallError> { + if self.glib_schema { + let mut dir = self.root.clone(); + dir.push("usr"); + dir.push("share"); + dir.push("glib-2.0"); + dir.push("schemas"); + let output = Command::new("glib-compile-schemas") + .arg(dir.to_str().unwrap()) + .output() + .map_err(Into::::into)?; + sender.send(InstallMessage::GlibSchemas)?; + Ok(Some(output)) + } else { + Ok(None) + } + } + + pub fn install_info(&self, sender: Sender) -> Result<(), InstallError> { + let root = self.root.clone(); + self.info.iter().try_for_each(|page| { + let p = root.clone(); + let page = p.join(page); + let Some(dir) = page.parent() else { + return Err(InstallError::BadPath); + }; + let mut dir = dir.to_path_buf(); + dir.push("dir"); + let Some(page) = page.to_str() else { + return Err(InstallError::BadPath); + }; + let Some(dir) = dir.to_str() else { + return Err(InstallError::BadPath); + }; + Command::new("install-info") + .args([page, dir]) + .output() + .map_err(Into::::into)?; + sender.send(InstallMessage::Info(page.to_string()))?; + Ok(()) + })?; + Ok(()) + } + + pub fn run_pinstall(&self, sender: Sender) -> Result<(), InstallError> { + let root = self.root.clone(); + self.pinstall.iter().try_for_each(|pinstall| { + let p = root.clone(); + let pinstall = p.join(pinstall); + let Some(root) = root.to_str() else { + return Err(InstallError::BadPath); + }; + let Some(pinstall) = pinstall.to_str() else { + return Err(InstallError::BadPath); + }; + let output = Command::new("/bin/sh") + .arg(pinstall) + .env("HPOK_ROOT", root) + .output() + .map_err(Into::::into)?; + sender.send(InstallMessage::PostInstall(output))?; + Ok(()) + })?; + Ok(()) + } +} diff --git a/src/installer/mod.rs b/src/installer/mod.rs index b4690d0..e7bdb49 100644 --- a/src/installer/mod.rs +++ b/src/installer/mod.rs @@ -1,11 +1,13 @@ #![allow(dead_code)] -pub use error::Error; +use std::process::Output; + +pub use {error::Error, hooks::Hooks}; +mod hooks; use { crate::{ - Cleanup, tar::{Archive, Node}, - Entry, Group, Hooks, Package, Pinstall, User, + Entry, Group, Package, User, }, rayon::prelude::{IntoParallelRefIterator, ParallelIterator}, sha2::{Digest, Sha256}, @@ -48,10 +50,8 @@ pub enum InstallMessage { UserCreated(User), /// A `Group` has been successfully created GroupCreated(Group), - /// The output of the post install script sent to stdout - PostInstallStdout(Vec), - /// The output of the post install script sent to stderr - PostInstallStderr(Vec), + /// The output of the post install script + PostInstall(Output), /// Update the mandoc db Man, /// Update the info db @@ -87,7 +87,7 @@ impl Installer { pub fn install( self, - hooks: &mut Cleanup, + hooks: &mut Hooks, sender: Sender, ) -> Result { let reader = Decoder::new(self.reader)?; @@ -102,12 +102,12 @@ impl Installer { if let Some(ref users) = package.users { users .iter() - .for_each(|u| hooks.push((u.clone(), Some(self.root.clone())).into())); + .for_each(|u| hooks.push_user(u.clone())); } if let Some(ref groups) = package.groups { groups .iter() - .for_each(|g| hooks.push((g.clone(), Some(self.root.clone())).into())); + .for_each(|g| hooks.push_group(g.clone())); } let mut db_pkgdir = crate::get_dbdir(Some(self.root.clone())); db_pkgdir.push(&package.name); @@ -115,7 +115,7 @@ impl Installer { fs::create_dir_all(&db_pkgdir)?; } pop_appstream(&mut archive, &db_pkgdir)?; - pop_pinstall(&mut archive, hooks, &package.name, &self.root)?; + pop_pinstall(&mut archive, hooks, &package.name)?; let len = archive.nodes.len(); sender.send(InstallMessage::ArchiveLen(len))?; let mut db_file = db_pkgdir; @@ -137,15 +137,15 @@ impl Installer { if let Some(s) = node.header.prefix() { if s.contains("/share/man/") { let mut h = hooks.lock().unwrap(); - h.push(Hooks::Man); + h.push_man(); } else if s.contains("/share/info") { hooks .lock() .unwrap() - .push(Hooks::Info(fpath.to_str().unwrap().to_string())); + .push_info(fpath.to_str().unwrap()); } else if s.contains("/share/glib-2.0/schemas") { let mut h = hooks.lock().unwrap(); - h.push(Hooks::GlibSchema); + h.push_glib_schema(); } } // Match up a package entry with a tar node @@ -239,9 +239,8 @@ fn pop_appstream(archive: &mut Archive, db_pkgdir: &Path) -> Result<(), Error> { fn pop_pinstall( archive: &mut Archive, - hooks: &mut Cleanup, + hooks: &mut Hooks, pkgname: &str, - root: &Path, ) -> Result<(), Error> { let pinstall = archive.pop("postinstall.sh"); if let Some(node) = pinstall { @@ -253,7 +252,7 @@ fn pop_pinstall( let fd = File::open(&path)?; let writer = BufWriter::new(fd); node.write(writer)?; - hooks.push(Pinstall::new(path, root.to_path_buf()).into()); + hooks.push_pinstall(path); } Ok(()) } @@ -278,6 +277,7 @@ mod error { RonError(SpannedError), SendError(SendError), Tar(tar::Error), + BadPath, ChecksumMismatch, MissingManifest, MutexError, diff --git a/src/lib.rs b/src/lib.rs index 9d8f0c7..7fa405c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,7 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::must_use_candidate, clippy::missing_errors_doc)] -mod cleanup; mod creator; mod db; -mod hooks; mod installer; mod item; mod package; @@ -15,11 +13,9 @@ mod version; use std::path::PathBuf; pub use { - cleanup::Cleanup, creator::{CreationError, Creator, Message}, db::Database, - hooks::{Hooks, Pinstall}, - installer::{Error as InstallError, InstallMessage, Installer}, + installer::{Error as InstallError, Hooks, InstallMessage, Installer}, item::{Error as ItemError, Item}, package::{Arch, Dependency, Group, Package, Specs, User}, plist::{Entry, Plist},