Add shells hook
This commit is contained in:
parent
5544418184
commit
ccf6d5301a
@ -9,10 +9,7 @@ mod cli;
|
|||||||
use {
|
use {
|
||||||
clap::ArgMatches,
|
clap::ArgMatches,
|
||||||
cli::cli,
|
cli::cli,
|
||||||
hpk::{
|
hpk::{CreationError, Creator, Dependency, InstallMessage, Installer, Message, Specs, Version},
|
||||||
CreationError, Creator, Dependency, InstallMessage, Installer, Message, Specs,
|
|
||||||
Version,
|
|
||||||
},
|
|
||||||
indicatif::{ProgressBar, ProgressStyle},
|
indicatif::{ProgressBar, ProgressStyle},
|
||||||
ron::ser::{to_writer_pretty, PrettyConfig},
|
ron::ser::{to_writer_pretty, PrettyConfig},
|
||||||
std::{
|
std::{
|
||||||
@ -158,7 +155,7 @@ fn install_local<P: AsRef<OsStr> + fmt::Display>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init(matches: &ArgMatches) -> Result<PathBuf, Box<dyn Error>> {
|
fn init(matches: &ArgMatches) -> Result<PathBuf, Box<dyn Error>> {
|
||||||
let specsfile = PathBuf::from("package.specs");
|
let specsfile = PathBuf::from("specs.ron");
|
||||||
let cfg = PrettyConfig::new().struct_names(true);
|
let cfg = PrettyConfig::new().struct_names(true);
|
||||||
let buf = File::create(&specsfile)?;
|
let buf = File::create(&specsfile)?;
|
||||||
let writer = BufWriter::new(buf);
|
let writer = BufWriter::new(buf);
|
||||||
|
@ -3,6 +3,7 @@ use std::{
|
|||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Command, Output},
|
process::{Command, Output},
|
||||||
sync::mpsc::Sender,
|
sync::mpsc::Sender,
|
||||||
|
thread, fs::OpenOptions, io::{Seek, Read, Write, SeekFrom},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -14,6 +15,7 @@ pub struct Hooks {
|
|||||||
pinstall: Vec<PathBuf>,
|
pinstall: Vec<PathBuf>,
|
||||||
users: Vec<User>,
|
users: Vec<User>,
|
||||||
groups: Vec<Group>,
|
groups: Vec<Group>,
|
||||||
|
shells: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Hooks {
|
impl Default for Hooks {
|
||||||
@ -26,6 +28,7 @@ impl Default for Hooks {
|
|||||||
pinstall: vec![],
|
pinstall: vec![],
|
||||||
users: vec![],
|
users: vec![],
|
||||||
groups: vec![],
|
groups: vec![],
|
||||||
|
shells: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,27 +64,166 @@ impl Hooks {
|
|||||||
self.groups.push(group);
|
self.groups.push(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn makewhatis(
|
pub fn push_shell(&mut self, shell: PathBuf) {
|
||||||
&self,
|
self.shells.push(shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self, sender: Sender<InstallMessage>) -> Result<(), InstallError> {
|
||||||
|
let Some(root) = self.root.to_str().map(|x| x.to_string()) else {
|
||||||
|
return Err(InstallError::BadPath);
|
||||||
|
};
|
||||||
|
let mut threads = vec![];
|
||||||
|
if !self.users.is_empty() {
|
||||||
|
let sender = sender.clone();
|
||||||
|
let root = root.clone();
|
||||||
|
let users = self.users;
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
create_users(users, &root, sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if !self.groups.is_empty() {
|
||||||
|
let sender = sender.clone();
|
||||||
|
let root = root.clone();
|
||||||
|
let groups = self.groups;
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
create_groups(groups, &root, sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if !self.shells.is_empty() {
|
||||||
|
let sender = sender.clone();
|
||||||
|
let root = root.clone();
|
||||||
|
let shells = self.shells;
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
append_shells(shells, &root, sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if self.man && root.as_str() == "/" {
|
||||||
|
let sender = sender.clone();
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
install_man(sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if self.glib_schema {
|
||||||
|
let sender = sender.clone();
|
||||||
|
let root = root.clone();
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
compile_schemas(&root, sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if !self.info.is_empty() {
|
||||||
|
let sender = sender.clone();
|
||||||
|
let root = PathBuf::from(&root);
|
||||||
|
let pages = self.info;
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
install_info(pages, root, sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if !self.pinstall.is_empty() {
|
||||||
|
let scripts = self.pinstall;
|
||||||
|
threads.push(thread::spawn(move || {
|
||||||
|
run_pinstall(scripts, root.into(), sender)?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for th in threads {
|
||||||
|
if let Err(e) = th.join() {
|
||||||
|
eprintln!("{e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_shells(
|
||||||
|
shells: Vec<PathBuf>,
|
||||||
|
root: &str,
|
||||||
sender: Sender<InstallMessage>,
|
sender: Sender<InstallMessage>,
|
||||||
) -> Result<Option<Output>, InstallError> {
|
) -> Result<(), InstallError> {
|
||||||
if self.man {
|
shells.iter().try_for_each(|shell| {
|
||||||
|
let s = PathBuf::from("/");
|
||||||
|
let shell = s.join(shell);
|
||||||
|
let Some(shell) = shell.to_str().map(|x| x.to_string()) else {
|
||||||
|
return Err(InstallError::BadPath);
|
||||||
|
};
|
||||||
|
let mut f = PathBuf::from(root);
|
||||||
|
f.push("etc");
|
||||||
|
f.push("shells");
|
||||||
|
let mut fd = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(&f)?;
|
||||||
|
let meta = fd.metadata()?;
|
||||||
|
let len = meta.len();
|
||||||
|
let pos = fd.seek(SeekFrom::Start(len - 1))?;
|
||||||
|
if !pos == len - 1 {
|
||||||
|
eprintln!("Bad seek operation in /etc/shells");
|
||||||
|
}
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
fd.read_exact(&mut buf)?;
|
||||||
|
if !buf[0] == b'\n' {
|
||||||
|
write!(fd, "\n")?;
|
||||||
|
}
|
||||||
|
write!(fd, "{shell}")?;
|
||||||
|
sender.send(InstallMessage::ShellAdded(shell))?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_users(
|
||||||
|
users: Vec<User>,
|
||||||
|
root: &str,
|
||||||
|
sender: Sender<InstallMessage>,
|
||||||
|
) -> Result<(), InstallError> {
|
||||||
|
users.iter().try_for_each(|user| {
|
||||||
|
let mut cmd = Command::new("useradd");
|
||||||
|
cmd.args(["-rs", "/sbin/nologin", "-R", root]);
|
||||||
|
if let Some(uid) = user.uid {
|
||||||
|
cmd.args(["-u", &format!("{uid}")]);
|
||||||
|
}
|
||||||
|
cmd.arg(&user.name);
|
||||||
|
let _output = cmd.output().map_err(Into::<InstallError>::into)?;
|
||||||
|
sender.send(InstallMessage::UserCreated(user.clone()))?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_groups(
|
||||||
|
groups: Vec<Group>,
|
||||||
|
root: &str,
|
||||||
|
sender: Sender<InstallMessage>,
|
||||||
|
) -> Result<(), InstallError> {
|
||||||
|
groups.iter().try_for_each(|group| {
|
||||||
|
let mut cmd = Command::new("groupadd");
|
||||||
|
cmd.args(["-r", "-R", root]);
|
||||||
|
if let Some(gid) = group.gid {
|
||||||
|
cmd.args(["-u", &format!("{gid}")]);
|
||||||
|
}
|
||||||
|
cmd.arg(&group.name);
|
||||||
|
let _output = cmd.output().map_err(Into::<InstallError>::into)?;
|
||||||
|
sender.send(InstallMessage::GroupCreated(group.clone()))?;
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_man(sender: Sender<InstallMessage>) -> Result<Output, InstallError> {
|
||||||
let output = Command::new("makewhatis")
|
let output = Command::new("makewhatis")
|
||||||
.output()
|
.output()
|
||||||
.map_err(Into::<InstallError>::into)?;
|
.map_err(Into::<InstallError>::into)?;
|
||||||
sender.send(InstallMessage::Man)?;
|
sender.send(InstallMessage::Man)?;
|
||||||
Ok(Some(output))
|
Ok(output)
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_schemas(
|
pub fn compile_schemas(root: &str, sender: Sender<InstallMessage>) -> Result<Output, InstallError> {
|
||||||
&self,
|
let mut dir = PathBuf::from(root);
|
||||||
sender: Sender<InstallMessage>,
|
|
||||||
) -> Result<Option<Output>, InstallError> {
|
|
||||||
if self.glib_schema {
|
|
||||||
let mut dir = self.root.clone();
|
|
||||||
dir.push("usr");
|
dir.push("usr");
|
||||||
dir.push("share");
|
dir.push("share");
|
||||||
dir.push("glib-2.0");
|
dir.push("glib-2.0");
|
||||||
@ -91,17 +233,16 @@ impl Hooks {
|
|||||||
.output()
|
.output()
|
||||||
.map_err(Into::<InstallError>::into)?;
|
.map_err(Into::<InstallError>::into)?;
|
||||||
sender.send(InstallMessage::GlibSchemas)?;
|
sender.send(InstallMessage::GlibSchemas)?;
|
||||||
Ok(Some(output))
|
Ok(output)
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_info(&self, sender: Sender<InstallMessage>) -> Result<(), InstallError> {
|
pub fn install_info(
|
||||||
let root = self.root.clone();
|
pages: Vec<PathBuf>,
|
||||||
self.info.iter().try_for_each(|page| {
|
root: PathBuf,
|
||||||
let p = root.clone();
|
sender: Sender<InstallMessage>,
|
||||||
let page = p.join(page);
|
) -> Result<(), InstallError> {
|
||||||
|
pages.iter().try_for_each(|page| {
|
||||||
|
let page = root.join(page);
|
||||||
let Some(dir) = page.parent() else {
|
let Some(dir) = page.parent() else {
|
||||||
return Err(InstallError::BadPath);
|
return Err(InstallError::BadPath);
|
||||||
};
|
};
|
||||||
@ -123,9 +264,12 @@ impl Hooks {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_pinstall(&self, sender: Sender<InstallMessage>) -> Result<(), InstallError> {
|
pub fn run_pinstall(
|
||||||
let root = self.root.clone();
|
scripts: Vec<PathBuf>,
|
||||||
self.pinstall.iter().try_for_each(|pinstall| {
|
root: PathBuf,
|
||||||
|
sender: Sender<InstallMessage>,
|
||||||
|
) -> Result<(), InstallError> {
|
||||||
|
scripts.iter().try_for_each(|pinstall| {
|
||||||
let p = root.clone();
|
let p = root.clone();
|
||||||
let pinstall = p.join(pinstall);
|
let pinstall = p.join(pinstall);
|
||||||
let Some(root) = root.to_str() else {
|
let Some(root) = root.to_str() else {
|
||||||
@ -144,4 +288,3 @@ impl Hooks {
|
|||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -50,6 +50,8 @@ pub enum InstallMessage {
|
|||||||
UserCreated(User),
|
UserCreated(User),
|
||||||
/// A `Group` has been successfully created
|
/// A `Group` has been successfully created
|
||||||
GroupCreated(Group),
|
GroupCreated(Group),
|
||||||
|
/// A shell has been added to '/etc/shells'
|
||||||
|
ShellAdded(String),
|
||||||
/// The output of the post install script
|
/// The output of the post install script
|
||||||
PostInstall(Output),
|
PostInstall(Output),
|
||||||
/// Update the mandoc db
|
/// Update the mandoc db
|
||||||
@ -100,14 +102,13 @@ impl<T: io::Read> Installer<T> {
|
|||||||
pr_node.write(&mut buf)?;
|
pr_node.write(&mut buf)?;
|
||||||
let package: Package = ron::de::from_bytes(&buf)?;
|
let package: Package = ron::de::from_bytes(&buf)?;
|
||||||
if let Some(ref users) = package.users {
|
if let Some(ref users) = package.users {
|
||||||
users
|
users.iter().for_each(|u| hooks.push_user(u.clone()));
|
||||||
.iter()
|
|
||||||
.for_each(|u| hooks.push_user(u.clone()));
|
|
||||||
}
|
}
|
||||||
if let Some(ref groups) = package.groups {
|
if let Some(ref groups) = package.groups {
|
||||||
groups
|
groups.iter().for_each(|g| hooks.push_group(g.clone()));
|
||||||
.iter()
|
}
|
||||||
.for_each(|g| hooks.push_group(g.clone()));
|
if let Some(ref shells) = package.shells {
|
||||||
|
shells.iter().for_each(|s| hooks.push_shell(s.clone()));
|
||||||
}
|
}
|
||||||
let mut db_pkgdir = crate::get_dbdir(Some(self.root.clone()));
|
let mut db_pkgdir = crate::get_dbdir(Some(self.root.clone()));
|
||||||
db_pkgdir.push(&package.name);
|
db_pkgdir.push(&package.name);
|
||||||
@ -139,10 +140,7 @@ impl<T: io::Read> Installer<T> {
|
|||||||
let mut h = hooks.lock().unwrap();
|
let mut h = hooks.lock().unwrap();
|
||||||
h.push_man();
|
h.push_man();
|
||||||
} else if s.contains("/share/info") {
|
} else if s.contains("/share/info") {
|
||||||
hooks
|
hooks.lock().unwrap().push_info(fpath.to_str().unwrap());
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_info(fpath.to_str().unwrap());
|
|
||||||
} else if s.contains("/share/glib-2.0/schemas") {
|
} else if s.contains("/share/glib-2.0/schemas") {
|
||||||
let mut h = hooks.lock().unwrap();
|
let mut h = hooks.lock().unwrap();
|
||||||
h.push_glib_schema();
|
h.push_glib_schema();
|
||||||
@ -237,11 +235,7 @@ fn pop_appstream(archive: &mut Archive, db_pkgdir: &Path) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_pinstall(
|
fn pop_pinstall(archive: &mut Archive, hooks: &mut Hooks, pkgname: &str) -> Result<(), Error> {
|
||||||
archive: &mut Archive,
|
|
||||||
hooks: &mut Hooks,
|
|
||||||
pkgname: &str,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let pinstall = archive.pop("postinstall.sh");
|
let pinstall = archive.pop("postinstall.sh");
|
||||||
if let Some(node) = pinstall {
|
if let Some(node) = pinstall {
|
||||||
let mut path: PathBuf = ["/", "tmp", "hpk", pkgname].iter().collect();
|
let mut path: PathBuf = ["/", "tmp", "hpk", pkgname].iter().collect();
|
||||||
|
@ -12,7 +12,7 @@ use {
|
|||||||
fs,
|
fs,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
path::Path,
|
path::{Path, PathBuf},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,6 +56,8 @@ pub struct Package {
|
|||||||
pub users: Option<Vec<User>>,
|
pub users: Option<Vec<User>>,
|
||||||
/// an optional list of groups to be created upon installation
|
/// an optional list of groups to be created upon installation
|
||||||
pub groups: Option<Vec<Group>>,
|
pub groups: Option<Vec<Group>>,
|
||||||
|
/// applicable only if this package contains a shell
|
||||||
|
pub shells: Option<Vec<PathBuf>>,
|
||||||
/// an optional post installation shell script to be run
|
/// an optional post installation shell script to be run
|
||||||
pub post_install: Option<String>,
|
pub post_install: Option<String>,
|
||||||
}
|
}
|
||||||
@ -71,6 +73,7 @@ impl From<Specs> for Package {
|
|||||||
dependencies: value.dependencies,
|
dependencies: value.dependencies,
|
||||||
users: value.users,
|
users: value.users,
|
||||||
groups: value.groups,
|
groups: value.groups,
|
||||||
|
shells: value.shells,
|
||||||
post_install: value.post_install,
|
post_install: value.post_install,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use {
|
|||||||
super::{Group, User},
|
super::{Group, User},
|
||||||
crate::{Dependency, Version},
|
crate::{Dependency, Version},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
|
std::path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||||
@ -22,6 +23,8 @@ pub struct Specs {
|
|||||||
pub users: Option<Vec<User>>,
|
pub users: Option<Vec<User>>,
|
||||||
/// an optional list of groups to be created upon installation
|
/// an optional list of groups to be created upon installation
|
||||||
pub groups: Option<Vec<Group>>,
|
pub groups: Option<Vec<Group>>,
|
||||||
|
/// applicable only if this package contains a shell
|
||||||
|
pub shells: Option<Vec<PathBuf>>,
|
||||||
/// an optional post installation shell script to be run
|
/// an optional post installation shell script to be run
|
||||||
pub post_install: Option<String>,
|
pub post_install: Option<String>,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user