290 lines
8.3 KiB
Rust
290 lines
8.3 KiB
Rust
use crate::{Group, InstallError, InstallMessage, User};
|
|
use std::{
|
|
fs::OpenOptions,
|
|
io::{Read, Seek, SeekFrom, Write},
|
|
path::PathBuf,
|
|
process::{Command, Output},
|
|
sync::mpsc::Sender,
|
|
thread,
|
|
};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Hooks {
|
|
root: PathBuf,
|
|
man: bool,
|
|
glib_schema: bool,
|
|
info: Vec<PathBuf>,
|
|
pinstall: Vec<PathBuf>,
|
|
users: Vec<User>,
|
|
groups: Vec<Group>,
|
|
shells: Vec<PathBuf>,
|
|
}
|
|
|
|
impl Default for Hooks {
|
|
fn default() -> Self {
|
|
Self {
|
|
root: PathBuf::from("/"),
|
|
man: false,
|
|
glib_schema: false,
|
|
info: vec![],
|
|
pinstall: vec![],
|
|
users: vec![],
|
|
groups: vec![],
|
|
shells: 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 push_shell(&mut self, shell: PathBuf) {
|
|
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>,
|
|
) -> Result<(), InstallError> {
|
|
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")
|
|
.output()
|
|
.map_err(Into::<InstallError>::into)?;
|
|
sender.send(InstallMessage::Man)?;
|
|
Ok(output)
|
|
}
|
|
|
|
pub fn compile_schemas(root: &str, sender: Sender<InstallMessage>) -> Result<Output, InstallError> {
|
|
let mut dir = PathBuf::from(root);
|
|
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::<InstallError>::into)?;
|
|
sender.send(InstallMessage::GlibSchemas)?;
|
|
Ok(output)
|
|
}
|
|
|
|
pub fn install_info(
|
|
pages: Vec<PathBuf>,
|
|
root: PathBuf,
|
|
sender: Sender<InstallMessage>,
|
|
) -> Result<(), InstallError> {
|
|
pages.iter().try_for_each(|page| {
|
|
let page = root.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::<InstallError>::into)?;
|
|
sender.send(InstallMessage::Info(page.to_string()))?;
|
|
Ok(())
|
|
})?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn run_pinstall(
|
|
scripts: Vec<PathBuf>,
|
|
root: PathBuf,
|
|
sender: Sender<InstallMessage>,
|
|
) -> Result<(), InstallError> {
|
|
scripts.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::<InstallError>::into)?;
|
|
sender.send(InstallMessage::PostInstall(output))?;
|
|
Ok(())
|
|
})?;
|
|
Ok(())
|
|
}
|