Compare commits

...

2 Commits

Author SHA1 Message Date
Nathan Fisher
af41d1c006 Fix bug introduced revolving around the ustar prefix field. GNU tar
was not giving the full path to each file and ignoring the `prefix`
field. On investigation, the field is only to be used if the filename
is longer than 100 bytes. Since the library code had been simplified to
always use the prefix field this caused all filenames shorter than 100
bytes to fail.

Also updated `package-bootstrap` to the latest patch release, which has
a fix for completions being installed into the base directory rather
than the appropriate subdirectory.
2023-05-05 19:07:44 -04:00
Nathan Fisher
ccf6d5301a Add shells hook 2023-04-29 23:40:17 -04:00
9 changed files with 374 additions and 218 deletions

146
Cargo.lock generated
View File

@ -19,42 +19,51 @@ dependencies = [
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.2.6" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
"anstyle-query",
"anstyle-wincon", "anstyle-wincon",
"concolor-override", "colorchoice",
"concolor-query",
"is-terminal", "is-terminal",
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "0.3.5" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.1.1" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-query"
version = "0.2.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.45.0", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -110,9 +119,9 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.12.0" version = "3.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -153,18 +162,18 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.2.1" version = "4.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
] ]
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.2.1" version = "4.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -175,9 +184,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.2.0" version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd" checksum = "1a19591b2ab0e3c04b588a0e04ddde7b9eaa423646d1b4a8092879216bf47473"
dependencies = [ dependencies = [
"clap", "clap",
] ]
@ -219,19 +228,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "concolor-override" name = "colorchoice"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "concolor-query"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
dependencies = [
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "console" name = "console"
@ -264,9 +264,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.6" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -357,7 +357,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"scratch", "scratch",
"syn 2.0.14", "syn 2.0.15",
] ]
[[package]] [[package]]
@ -374,7 +374,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.14", "syn 2.0.15",
] ]
[[package]] [[package]]
@ -480,9 +480,9 @@ dependencies = [
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.25" version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
@ -680,9 +680,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.141" version = "0.2.142"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]] [[package]]
name = "link-cplusplus" name = "link-cplusplus"
@ -695,9 +695,9 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.3.1" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
[[package]] [[package]]
name = "log" name = "log"
@ -725,9 +725,9 @@ dependencies = [
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.6.2" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [ dependencies = [
"adler", "adler",
] ]
@ -781,9 +781,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "package-bootstrap" name = "package-bootstrap"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b733f09d474cde789d5a1f62eae77e27749f65a88306a9f60d37d7f5193f2beb" checksum = "b44f1db147048186ea8be34e803e16ad50a5eb47c9ab6a8e098276a6c8d112dd"
dependencies = [ dependencies = [
"clap", "clap",
"clap_complete", "clap_complete",
@ -799,9 +799,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
@ -899,9 +899,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.37.11" version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -1009,22 +1009,22 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.160" version = "1.0.162"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.160" version = "1.0.162"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.14", "syn 2.0.15",
] ]
[[package]] [[package]]
@ -1074,9 +1074,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.14" version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1115,7 +1115,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.14", "syn 2.0.15",
] ]
[[package]] [[package]]
@ -1417,7 +1417,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [ dependencies = [
"windows-targets 0.48.0", "windows-targets",
] ]
[[package]] [[package]]
@ -1435,37 +1435,13 @@ dependencies = [
"windows_x86_64_msvc 0.42.2", "windows_x86_64_msvc 0.42.2",
] ]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [ dependencies = [
"windows-targets 0.48.0", "windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
] ]
[[package]] [[package]]
@ -1569,9 +1545,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.4.1" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

16
specs.ron Normal file
View File

@ -0,0 +1,16 @@
Specs(
name: "hpk",
version: SemVer(SemVer(
major: 0,
minor: 1,
patch: 0,
)),
release: 1,
description: "The HitchHiker Package Keeper",
long_description: "",
dependencies: [],
users: None,
groups: None,
shells: None,
post_install: None,
)

View File

@ -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::{
@ -102,6 +99,7 @@ fn create(matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
Message::Success(_s) => { Message::Success(_s) => {
pb.inc(1); pb.inc(1);
pb.finish_and_clear(); pb.finish_and_clear();
break;
} }
} }
} }
@ -117,6 +115,18 @@ fn create(matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
} }
} }
fn install(matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
let Some(root) = matches.get_one::<String>("root") else {
unreachable!();
};
if let Some(archive) = matches.get_one::<String>("local") {
install_local(archive, root)?;
} else if let Some(package) = matches.get_one::<String>("package") {
unimplemented!();
}
Ok(())
}
fn install_local<P: AsRef<OsStr> + fmt::Display>( fn install_local<P: AsRef<OsStr> + fmt::Display>(
archive: P, archive: P,
root: P, root: P,
@ -158,7 +168,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);
@ -171,10 +181,6 @@ fn search(_matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
unimplemented!(); unimplemented!();
} }
fn install(_matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
unimplemented!();
}
fn remove(_matches: &ArgMatches) -> Result<(), Box<dyn Error>> { fn remove(_matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
unimplemented!(); unimplemented!();
} }

View File

@ -1,8 +1,11 @@
use crate::{Group, InstallError, InstallMessage, User}; use crate::{Group, InstallError, InstallMessage, User};
use std::{ use std::{
fs::OpenOptions,
io::{Read, Seek, SeekFrom, Write},
path::PathBuf, path::PathBuf,
process::{Command, Output}, process::{Command, Output},
sync::mpsc::Sender, sync::mpsc::Sender,
thread,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -14,6 +17,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 +30,7 @@ impl Default for Hooks {
pinstall: vec![], pinstall: vec![],
users: vec![], users: vec![],
groups: vec![], groups: vec![],
shells: vec![],
} }
} }
} }
@ -61,87 +66,224 @@ 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);
sender: Sender<InstallMessage>,
) -> Result<Option<Output>, InstallError> {
if self.man {
let output = Command::new("makewhatis")
.output()
.map_err(Into::<InstallError>::into)?;
sender.send(InstallMessage::Man)?;
Ok(Some(output))
} else {
Ok(None)
}
} }
pub fn compile_schemas( pub fn run(self, sender: Sender<InstallMessage>) -> Result<(), InstallError> {
&self, let Some(root) = self.root.to_str().map(|x| x.to_string()) else {
sender: Sender<InstallMessage>, return Err(InstallError::BadPath);
) -> Result<Option<Output>, InstallError> { };
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 { if self.glib_schema {
let mut dir = self.root.clone(); let sender = sender.clone();
dir.push("usr"); let root = root.clone();
dir.push("share"); threads.push(thread::spawn(move || {
dir.push("glib-2.0"); compile_schemas(&root, sender)?;
dir.push("schemas"); Ok::<(), InstallError>(())
let output = Command::new("glib-compile-schemas") }));
.arg(dir.to_str().unwrap()) }
.output() if !self.info.is_empty() {
.map_err(Into::<InstallError>::into)?; let sender = sender.clone();
sender.send(InstallMessage::GlibSchemas)?; let root = PathBuf::from(&root);
Ok(Some(output)) let pages = self.info;
} else { threads.push(thread::spawn(move || {
Ok(None) 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:?}");
}
} }
}
pub fn install_info(&self, sender: Sender<InstallMessage>) -> 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::<InstallError>::into)?;
sender.send(InstallMessage::Info(page.to_string()))?;
Ok(())
})?;
Ok(())
}
pub fn run_pinstall(&self, sender: Sender<InstallMessage>) -> 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::<InstallError>::into)?;
sender.send(InstallMessage::PostInstall(output))?;
Ok(())
})?;
Ok(()) 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(())
}

View File

@ -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();

View File

@ -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()
} }
@ -126,4 +129,8 @@ impl Package {
&& (self.version > other.version && (self.version > other.version
|| (self.version == other.version && self.release > other.release)) || (self.version == other.version && self.release > other.release))
} }
pub fn resolve_deps(&self, queue: &mut Vec<Package>) -> Result<(), Box<dyn Error>> {
unimplemented!();
}
} }

View File

@ -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>,
} }

View File

@ -125,7 +125,7 @@ impl Header {
/// ///
/// let header = Header::new("test/1.txt").unwrap(); /// let header = Header::new("test/1.txt").unwrap();
/// let filename = header.filename().unwrap(); /// let filename = header.filename().unwrap();
/// assert_eq!(filename.as_str(), "1.txt"); /// assert_eq!(filename.as_str(), "test/1.txt");
/// ``` /// ```
pub fn filename(&self) -> Result<String, fmt::Error> { pub fn filename(&self) -> Result<String, fmt::Error> {
let mut s = String::new(); let mut s = String::new();
@ -220,14 +220,18 @@ impl Header {
} }
/// Gets the path to the file minus it's final component /// Gets the path to the file minus it's final component
/// > Note: the ustar format only uses this field if the
/// > complete filename would be longer than 100 bytes.
/// > If the combined filename is shorter, this function
/// > returns `None`.
/// ///
/// # Example /// # Example
/// ``` /// ```
/// use hpk::tar::Header; /// use hpk::tar::Header;
/// ///
/// let header = Header::new("test/1.txt").unwrap(); /// let header = Header::new("test/1.txt").unwrap();
/// let prefix = header.prefix().unwrap(); /// let prefix = header.prefix();
/// assert_eq!(prefix.as_str(), "test"); /// assert!(prefix.is_none());
/// ``` /// ```
pub fn prefix(&self) -> Option<String> { pub fn prefix(&self) -> Option<String> {
let mut s = String::new(); let mut s = String::new();
@ -272,20 +276,24 @@ impl Header {
let meta = fs::symlink_metadata(filename)?; let meta = fs::symlink_metadata(filename)?;
let (filename, prefix) = { let (filename, prefix) = {
// Original tar has a maximum file name length of 100 bytes. The ustar // Original tar has a maximum file name length of 100 bytes. The ustar
// revision allows storing the path prefix separately, with 100 bytes // revision allows storing the path prefix separately if the combined
// reserved for the file name and 150 bytes for the rest of the path. // length is more than 100 bytes.
let path = PathBuf::from(&filename); if filename.len() > 100 {
let name = match path.file_name().and_then(OsStr::to_str) { let path = PathBuf::from(&filename);
Some(n) => n.to_string(), let name = match path.file_name().and_then(OsStr::to_str) {
None => { Some(n) => n.to_string(),
return Err(Error::Io(io::Error::new( None => {
io::ErrorKind::Other, return Err(Error::Io(io::Error::new(
"Cannot get file name", io::ErrorKind::Other,
))) "Cannot get file name",
} )))
}; }
let dir = path.parent().map(|x| format!("{}", x.display())); };
(name, dir) let dir = path.parent().map(|x| format!("{}", x.display()));
(name, dir)
} else {
(filename.to_string(), None)
}
}; };
/* Fill in metadata */ /* Fill in metadata */
@ -338,20 +346,24 @@ impl Header {
let mut header = Header::default(); let mut header = Header::default();
let (filename, prefix) = { let (filename, prefix) = {
// Original tar has a maximum file name length of 100 bytes. The ustar // Original tar has a maximum file name length of 100 bytes. The ustar
// revision allows storing the path prefix separately, with 100 bytes // revision allows storing the path prefix separately if the combined
// reserved for the file name and 150 bytes for the rest of the path. // length is more than 100 bytes.
let path = PathBuf::from(&filename); if filename.len() > 100 {
let name = match path.file_name().and_then(OsStr::to_str) { let path = PathBuf::from(&filename);
Some(n) => n.to_string(), let name = match path.file_name().and_then(OsStr::to_str) {
None => { Some(n) => n.to_string(),
return Err(Error::Io(io::Error::new( None => {
io::ErrorKind::Other, return Err(Error::Io(io::Error::new(
"Cannot get file name", io::ErrorKind::Other,
))) "Cannot get file name",
} )))
}; }
let dir = path.parent().map(|x| format!("{}", x.display())); };
(name, dir) let dir = path.parent().map(|x| format!("{}", x.display()));
(name, dir)
} else {
(filename.to_string(), None)
}
}; };
header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
let mode = format!("{:07o}", meta.st_mode()); let mode = format!("{:07o}", meta.st_mode());

Binary file not shown.