//!
//! This is a collection of small and useful tools.
use std::any::Any;
-use std::collections::HashMap;
-use std::hash::BuildHasher;
-use std::fs::File;
-use std::io::{self, BufRead, Read, Seek, SeekFrom};
use std::os::unix::io::RawFd;
-use std::path::Path;
use anyhow::{bail, format_err, Error};
use serde_json::Value;
use openssl::hash::{hash, DigestBytes, MessageDigest};
-use percent_encoding::{utf8_percent_encode, AsciiSet};
pub use proxmox::tools::fd::Fd;
use proxmox::tools::fs::{create_path, CreateOptions};
ProxyConfig,
};
-pub mod acl;
+pub use pbs_tools::json;
+pub use pbs_tools::nom;
+pub use pbs_tools::{run_command, command_output, command_output_as_string};
+pub use pbs_tools::process_locker::{
+ ProcessLocker, ProcessLockExclusiveGuard, ProcessLockSharedGuard
+};
+
pub mod apt;
pub mod async_io;
-pub mod cert;
pub mod compression;
pub mod config;
pub mod cpio;
mod memcom;
pub use memcom::Memcom;
-pub mod json;
pub mod logrotate;
pub mod loopdev;
-pub mod lru_cache;
-pub mod async_lru_cache;
-pub mod nom;
pub mod serde_filter;
pub mod statistics;
pub mod subscription;
pub mod systemd;
pub mod ticket;
-pub mod xattr;
-pub mod zip;
pub mod sgutils2;
pub mod paperkey;
mod async_channel_writer;
pub use async_channel_writer::AsyncChannelWriter;
-mod std_channel_writer;
-pub use std_channel_writer::StdChannelWriter;
-
-mod tokio_writer_adapter;
-pub use tokio_writer_adapter::TokioWriterAdapter;
-
-mod process_locker;
-pub use process_locker::{ProcessLocker, ProcessLockExclusiveGuard, ProcessLockSharedGuard};
-
mod file_logger;
pub use file_logger::{FileLogger, FileLogOptions};
-mod broadcast_future;
-pub use broadcast_future::{BroadcastData, BroadcastFuture};
+pub use pbs_tools::broadcast_future::{BroadcastData, BroadcastFuture};
+pub use pbs_tools::ops::ControlFlow;
/// The `BufferedRead` trait provides a single function
/// `buffered_read`. It returns a reference to an internal buffer. The
fn buffered_read(&mut self, offset: u64) -> Result<&[u8], Error>;
}
-pub fn json_object_to_query(data: Value) -> Result<String, Error> {
- let mut query = url::form_urlencoded::Serializer::new(String::new());
-
- let object = data.as_object().ok_or_else(|| {
- format_err!("json_object_to_query: got wrong data type (expected object).")
- })?;
-
- for (key, value) in object {
- match value {
- Value::Bool(b) => {
- query.append_pair(key, &b.to_string());
- }
- Value::Number(n) => {
- query.append_pair(key, &n.to_string());
- }
- Value::String(s) => {
- query.append_pair(key, &s);
- }
- Value::Array(arr) => {
- for element in arr {
- match element {
- Value::Bool(b) => {
- query.append_pair(key, &b.to_string());
- }
- Value::Number(n) => {
- query.append_pair(key, &n.to_string());
- }
- Value::String(s) => {
- query.append_pair(key, &s);
- }
- _ => bail!(
- "json_object_to_query: unable to handle complex array data types."
- ),
- }
- }
- }
- _ => bail!("json_object_to_query: unable to handle complex data types."),
- }
- }
-
- Ok(query.finish())
-}
-
pub fn required_string_param<'a>(param: &'a Value, name: &str) -> Result<&'a str, Error> {
match param[name].as_str() {
Some(s) => Ok(s),
}
}
-pub fn complete_file_name<S>(arg: &str, _param: &HashMap<String, String, S>) -> Vec<String>
-where
- S: BuildHasher,
-{
- let mut result = vec![];
-
- use nix::fcntl::AtFlags;
- use nix::fcntl::OFlag;
- use nix::sys::stat::Mode;
-
- let mut dirname = std::path::PathBuf::from(if arg.is_empty() { "./" } else { arg });
-
- let is_dir = match nix::sys::stat::fstatat(libc::AT_FDCWD, &dirname, AtFlags::empty()) {
- Ok(stat) => (stat.st_mode & libc::S_IFMT) == libc::S_IFDIR,
- Err(_) => false,
- };
-
- if !is_dir {
- if let Some(parent) = dirname.parent() {
- dirname = parent.to_owned();
- }
- }
-
- let mut dir =
- match nix::dir::Dir::openat(libc::AT_FDCWD, &dirname, OFlag::O_DIRECTORY, Mode::empty()) {
- Ok(d) => d,
- Err(_) => return result,
- };
-
- for item in dir.iter() {
- if let Ok(entry) = item {
- if let Ok(name) = entry.file_name().to_str() {
- if name == "." || name == ".." {
- continue;
- }
- let mut newpath = dirname.clone();
- newpath.push(name);
-
- if let Ok(stat) =
- nix::sys::stat::fstatat(libc::AT_FDCWD, &newpath, AtFlags::empty())
- {
- if (stat.st_mode & libc::S_IFMT) == libc::S_IFDIR {
- newpath.push("");
- if let Some(newpath) = newpath.to_str() {
- result.push(newpath.to_owned());
- }
- continue;
- }
- }
- if let Some(newpath) = newpath.to_str() {
- result.push(newpath.to_owned());
- }
- }
- }
- }
-
- result
-}
-
/// Shortcut for md5 sums.
pub fn md5sum(data: &[u8]) -> Result<DigestBytes, Error> {
hash(MessageDigest::md5(), data).map_err(Error::from)
None
}
-/// percent encode a url component
-pub fn percent_encode_component(comp: &str) -> String {
- utf8_percent_encode(comp, percent_encoding::NON_ALPHANUMERIC).to_string()
-}
-
/// Detect modified configuration files
///
/// This function fails with a reasonable error message if checksums do not match.
Ok((path, components))
}
-/// Helper to check result from std::process::Command output
-///
-/// The exit_code_check() function should return true if the exit code
-/// is considered successful.
-pub fn command_output(
- output: std::process::Output,
- exit_code_check: Option<fn(i32) -> bool>,
-) -> Result<Vec<u8>, Error> {
-
- if !output.status.success() {
- match output.status.code() {
- Some(code) => {
- let is_ok = match exit_code_check {
- Some(check_fn) => check_fn(code),
- None => code == 0,
- };
- if !is_ok {
- let msg = String::from_utf8(output.stderr)
- .map(|m| if m.is_empty() { String::from("no error message") } else { m })
- .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
-
- bail!("status code: {} - {}", code, msg);
- }
- }
- None => bail!("terminated by signal"),
- }
- }
-
- Ok(output.stdout)
-}
-
-/// Helper to check result from std::process::Command output, returns String.
-///
-/// The exit_code_check() function should return true if the exit code
-/// is considered successful.
-pub fn command_output_as_string(
- output: std::process::Output,
- exit_code_check: Option<fn(i32) -> bool>,
-) -> Result<String, Error> {
- let output = command_output(output, exit_code_check)?;
- let output = String::from_utf8(output)?;
- Ok(output)
-}
-
-pub fn run_command(
- mut command: std::process::Command,
- exit_code_check: Option<fn(i32) -> bool>,
-) -> Result<String, Error> {
-
- let output = command.output()
- .map_err(|err| format_err!("failed to execute {:?} - {}", command, err))?;
-
- let output = command_output_as_string(output, exit_code_check)
- .map_err(|err| format_err!("command {:?} failed - {}", command, err))?;
-
- Ok(output)
-}
-
pub fn fd_change_cloexec(fd: RawFd, on: bool) -> Result<(), Error> {
use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
let mut flags = FdFlag::from_bits(fcntl(fd, F_GETFD)?)
SimpleHttp::with_options(options)
}
-/// This used to be: `SIMPLE_ENCODE_SET` plus space, `"`, `#`, `<`, `>`, backtick, `?`, `{`, `}`
-pub const DEFAULT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS // 0..1f and 7e
- // The SIMPLE_ENCODE_SET adds space and anything >= 0x7e (7e itself is already included above)
- .add(0x20)
- .add(0x7f)
- // the DEFAULT_ENCODE_SET added:
- .add(b' ')
- .add(b'"')
- .add(b'#')
- .add(b'<')
- .add(b'>')
- .add(b'`')
- .add(b'?')
- .add(b'{')
- .add(b'}');
-
-/// Get an iterator over lines of a file, skipping empty lines and comments (lines starting with a
-/// `#`).
-pub fn file_get_non_comment_lines<P: AsRef<Path>>(
- path: P,
-) -> Result<impl Iterator<Item = io::Result<String>>, Error> {
- let path = path.as_ref();
-
- Ok(io::BufReader::new(
- File::open(path).map_err(|err| format_err!("error opening {:?}: {}", path, err))?,
- )
- .lines()
- .filter_map(|line| match line {
- Ok(line) => {
- let line = line.trim();
- if line.is_empty() || line.starts_with('#') {
- None
- } else {
- Some(Ok(line.to_string()))
- }
- }
- Err(err) => Some(Err(err)),
- }))
-}
-
pub fn setup_safe_path_env() {
std::env::set_var("PATH", "/sbin:/bin:/usr/sbin:/usr/bin");
// Make %ENV safer - as suggested by https://perldoc.perl.org/perlsec.html
}
}
-pub fn strip_ascii_whitespace(line: &[u8]) -> &[u8] {
- let line = match line.iter().position(|&b| !b.is_ascii_whitespace()) {
- Some(n) => &line[n..],
- None => return &[],
- };
- match line.iter().rev().position(|&b| !b.is_ascii_whitespace()) {
- Some(n) => &line[..(line.len() - n)],
- None => &[],
- }
-}
-
-/// Seeks to start of file and computes the SHA256 hash
-pub fn compute_file_csum(file: &mut File) -> Result<([u8; 32], u64), Error> {
-
- file.seek(SeekFrom::Start(0))?;
-
- let mut hasher = openssl::sha::Sha256::new();
- let mut buffer = proxmox::tools::vec::undefined(256*1024);
- let mut size: u64 = 0;
-
- loop {
- let count = match file.read(&mut buffer) {
- Ok(0) => break,
- Ok(count) => count,
- Err(ref err) if err.kind() == std::io::ErrorKind::Interrupted => {
- continue;
- }
- Err(err) => return Err(err.into()),
- };
- size += count as u64;
- hasher.update(&buffer[..count]);
- }
-
- let csum = hasher.finish();
-
- Ok((csum, size))
-}
-
/// Create the base run-directory.
///
/// This exists to fixate the permissions for the run *base* directory while allowing intermediate
let _: bool = create_path(pbs_buildcfg::PROXMOX_BACKUP_RUN_DIR_M!(), None, Some(opts))?;
Ok(())
}
-
-/// Modeled after the nightly `std::ops::ControlFlow`.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum ControlFlow<B, C = ()> {
- Continue(C),
- Break(B),
-}
-
-impl<B> ControlFlow<B> {
- pub const CONTINUE: ControlFlow<B, ()> = ControlFlow::Continue(());
-}