rust fmt for pbs src

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht
2022-04-14 14:03:46 +02:00
parent ee0ea73500
commit 9531d2c570
30 changed files with 644 additions and 572 deletions

View File

@ -1,14 +1,14 @@
use std::collections::HashSet;
use std::collections::HashMap;
use std::collections::HashSet;
use anyhow::{Error, bail, format_err};
use anyhow::{bail, format_err, Error};
use apt_pkg_native::Cache;
use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
use proxmox_schema::const_regex;
use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
use pbs_api_types::APTUpdateInfo;
use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
const APT_PKG_STATE_FN: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/pkg-state.json");
@ -25,8 +25,13 @@ pub struct PkgState {
pub fn write_pkg_cache(state: &PkgState) -> Result<(), Error> {
let serialized_state = serde_json::to_string(state)?;
replace_file(APT_PKG_STATE_FN, serialized_state.as_bytes(), CreateOptions::new(), false)
.map_err(|err| format_err!("Error writing package cache - {}", err))?;
replace_file(
APT_PKG_STATE_FN,
serialized_state.as_bytes(),
CreateOptions::new(),
false,
)
.map_err(|err| format_err!("Error writing package cache - {}", err))?;
Ok(())
}
@ -42,7 +47,7 @@ pub fn read_pkg_state() -> Result<Option<PkgState>, Error> {
.map_err(|err| format_err!("could not parse cached package status - {}", err))
}
pub fn pkg_cache_expired () -> Result<bool, Error> {
pub fn pkg_cache_expired() -> Result<bool, Error> {
if let Ok(pbs_cache) = std::fs::metadata(APT_PKG_STATE_FN) {
let apt_pkgcache = std::fs::metadata("/var/cache/apt/pkgcache.bin")?;
let dpkg_status = std::fs::metadata("/var/lib/dpkg/status")?;
@ -57,27 +62,29 @@ pub fn pkg_cache_expired () -> Result<bool, Error> {
}
pub fn update_cache() -> Result<PkgState, Error> {
// update our cache
let all_upgradeable = list_installed_apt_packages(|data| {
data.candidate_version == data.active_version &&
data.installed_version != Some(data.candidate_version)
}, None);
// update our cache
let all_upgradeable = list_installed_apt_packages(
|data| {
data.candidate_version == data.active_version
&& data.installed_version != Some(data.candidate_version)
},
None,
);
let cache = match read_pkg_state() {
Ok(Some(mut cache)) => {
cache.package_status = all_upgradeable;
cache
},
_ => PkgState {
notified: None,
package_status: all_upgradeable,
},
};
write_pkg_cache(&cache)?;
Ok(cache)
let cache = match read_pkg_state() {
Ok(Some(mut cache)) => {
cache.package_status = all_upgradeable;
cache
}
_ => PkgState {
notified: None,
package_status: all_upgradeable,
},
};
write_pkg_cache(&cache)?;
Ok(cache)
}
const_regex! {
VERSION_EPOCH_REGEX = r"^\d+:";
FILENAME_EXTRACT_REGEX = r"^.*/.*?_(.*)_Packages$";
@ -108,9 +115,12 @@ fn get_changelog_url(
if output.len() < 2 {
bail!("invalid output (URI part too short) from 'apt-get changelog --print-uris': {}", output)
}
output[1..output.len()-1].to_owned()
},
None => bail!("invalid output from 'apt-get changelog --print-uris': {}", output)
output[1..output.len() - 1].to_owned()
}
None => bail!(
"invalid output from 'apt-get changelog --print-uris': {}",
output
),
};
return Ok(output);
} else if origin == "Proxmox" {
@ -123,18 +133,22 @@ fn get_changelog_url(
let base_capture = captures.get(1);
match base_capture {
Some(base_underscore) => base_underscore.as_str().replace("_", "/"),
None => bail!("incompatible filename, cannot find regex group")
None => bail!("incompatible filename, cannot find regex group"),
}
},
None => bail!("incompatible filename, doesn't match regex")
}
None => bail!("incompatible filename, doesn't match regex"),
};
if component == "pbs-enterprise" {
return Ok(format!("https://enterprise.proxmox.com/{}/{}_{}.changelog",
base, package, version));
return Ok(format!(
"https://enterprise.proxmox.com/{}/{}_{}.changelog",
base, package, version
));
} else {
return Ok(format!("http://download.proxmox.com/{}/{}_{}.changelog",
base, package, version));
return Ok(format!(
"http://download.proxmox.com/{}/{}_{}.changelog",
base, package, version
));
}
}
@ -162,7 +176,6 @@ pub fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(
filter: F,
only_versions_for: Option<&str>,
) -> Vec<APTUpdateInfo> {
let mut ret = Vec::new();
let mut depends = HashSet::new();
@ -172,26 +185,20 @@ pub fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(
let mut cache_iter = match only_versions_for {
Some(name) => cache.find_by_name(name),
None => cache.iter()
None => cache.iter(),
};
loop {
match cache_iter.next() {
Some(view) => {
let di = if only_versions_for.is_some() {
query_detailed_info(
PackagePreSelect::All,
&filter,
view,
None
)
query_detailed_info(PackagePreSelect::All, &filter, view, None)
} else {
query_detailed_info(
PackagePreSelect::OnlyInstalled,
&filter,
view,
Some(&mut depends)
Some(&mut depends),
)
};
if let Some(info) = di {
@ -201,7 +208,7 @@ pub fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(
if only_versions_for.is_some() {
break;
}
},
}
None => {
drop(cache_iter);
// also loop through missing dependencies, as they would be installed
@ -209,15 +216,10 @@ pub fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(
let mut iter = cache.find_by_name(pkg);
let view = match iter.next() {
Some(view) => view,
None => continue // package not found, ignore
None => continue, // package not found, ignore
};
let di = query_detailed_info(
PackagePreSelect::OnlyNew,
&filter,
view,
None
);
let di = query_detailed_info(PackagePreSelect::OnlyNew, &filter, view, None);
if let Some(info) = di {
ret.push(info);
}
@ -238,7 +240,7 @@ fn query_detailed_info<'a, F, V>(
) -> Option<APTUpdateInfo>
where
F: Fn(FilterData) -> bool,
V: std::ops::Deref<Target = apt_pkg_native::sane::PkgView<'a>>
V: std::ops::Deref<Target = apt_pkg_native::sane::PkgView<'a>>,
{
let current_version = view.current_version();
let candidate_version = view.candidate_version();
@ -247,8 +249,8 @@ where
PackagePreSelect::OnlyInstalled => match (current_version, candidate_version) {
(Some(cur), Some(can)) => (Some(cur), can), // package installed and there is an update
(Some(cur), None) => (Some(cur.clone()), cur), // package installed and up-to-date
(None, Some(_)) => return None, // package could be installed
(None, None) => return None, // broken
(None, Some(_)) => return None, // package could be installed
(None, None) => return None, // broken
},
PackagePreSelect::OnlyNew => match (current_version, candidate_version) {
(Some(_), Some(_)) => return None,
@ -267,7 +269,6 @@ where
// get additional information via nested APT 'iterators'
let mut view_iter = view.versions();
while let Some(ver) = view_iter.next() {
let package = view.name();
let version = ver.version();
let mut origin_res = "unknown".to_owned();
@ -299,7 +300,6 @@ where
let mut origin_iter = ver.origin_iter();
let origin = origin_iter.next();
if let Some(origin) = origin {
if let Some(sd) = origin.short_desc() {
short_desc = sd;
}
@ -324,8 +324,8 @@ where
// build changelog URL from gathered information
// ignore errors, use empty changelog instead
let url = get_changelog_url(&package, &filename,
&version, &origin_res, &component);
let url =
get_changelog_url(&package, &filename, &version, &origin_res, &component);
if let Ok(url) = url {
change_log_url = url;
}
@ -338,7 +338,7 @@ where
let dep = match dep_iter.next() {
Some(dep) if dep.dep_type() != "Depends" => continue,
Some(dep) => dep,
None => break
None => break,
};
let dep_pkg = dep.target_pkg();
@ -358,7 +358,7 @@ where
version: candidate_version.clone(),
old_version: match current_version {
Some(vers) => vers,
None => "".to_owned()
None => "".to_owned(),
},
priority: priority_res,
section: section_res,

View File

@ -119,7 +119,9 @@ pub fn from_property_string<T>(input: &str, schema: &'static Schema) -> Result<T
where
T: for<'de> Deserialize<'de>,
{
Ok(serde_json::from_value(schema.parse_property_string(input)?)?)
Ok(serde_json::from_value(
schema.parse_property_string(input)?,
)?)
}
/// Serialize a data structure using a 'key: value' config file format.
@ -154,7 +156,7 @@ fn object_to_writer(output: &mut dyn Write, object: &Object) -> Result<(), Error
for (key, value) in object.iter() {
match value {
_ if key == "description" => continue, // skip description as we handle it above
Value::Null => continue, // delete this entry
Value::Null => continue, // delete this entry
Value::Bool(v) => writeln!(output, "{}: {}", key, v)?,
Value::String(v) => {
if v.as_bytes().contains(&b'\n') {
@ -183,11 +185,10 @@ fn test() {
acmedomain1: test2.invalid.local\n\
";
let data: NodeConfig = from_str(NODE_CONFIG, &NodeConfig::API_SCHEMA)
.expect("failed to parse simple node config");
let data: NodeConfig =
from_str(NODE_CONFIG, &NodeConfig::API_SCHEMA).expect("failed to parse simple node config");
let config = to_bytes(&data, &NodeConfig::API_SCHEMA)
.expect("failed to serialize node config");
let config = to_bytes(&data, &NodeConfig::API_SCHEMA).expect("failed to serialize node config");
assert_eq!(config, NODE_CONFIG.as_bytes());
}

View File

@ -14,10 +14,10 @@ use once_cell::sync::OnceCell;
use ::serde::{Deserialize, Serialize};
use proxmox_schema::api;
use proxmox_lang::error::io_err_other;
use proxmox_sys::linux::procfs::{mountinfo::Device, MountInfo};
use proxmox_lang::{io_bail, io_format_err};
use proxmox_schema::api;
use proxmox_sys::linux::procfs::{mountinfo::Device, MountInfo};
use pbs_api_types::{StorageStatus, BLOCKDEVICE_NAME_REGEX};

View File

@ -6,11 +6,7 @@ use std::any::Any;
use anyhow::{bail, format_err, Error};
use openssl::hash::{hash, DigestBytes, MessageDigest};
use proxmox_http::{
client::SimpleHttp,
client::SimpleHttpOptions,
ProxyConfig,
};
use proxmox_http::{client::SimpleHttp, client::SimpleHttpOptions, ProxyConfig};
pub mod apt;
pub mod config;
@ -36,8 +32,7 @@ pub fn get_hardware_address() -> Result<String, Error> {
let contents = proxmox_sys::fs::file_get_contents(FILENAME)
.map_err(|e| format_err!("Error getting host key - {}", e))?;
let digest = md5sum(&contents)
.map_err(|e| format_err!("Error digesting host key - {}", e))?;
let digest = md5sum(&contents).map_err(|e| format_err!("Error digesting host key - {}", e))?;
Ok(hex::encode(&digest).to_uppercase())
}
@ -49,11 +44,13 @@ pub fn assert_if_modified(digest1: &str, digest2: &str) -> Result<(), Error> {
Ok(())
}
/// Detect modified configuration files
///
/// This function fails with a reasonable error message if checksums do not match.
pub fn detect_modified_configuration_file(digest1: &[u8;32], digest2: &[u8;32]) -> Result<(), Error> {
pub fn detect_modified_configuration_file(
digest1: &[u8; 32],
digest2: &[u8; 32],
) -> Result<(), Error> {
if digest1 != digest2 {
bail!("detected modified configuration - file changed by other user? Try again.");
}

View File

@ -59,7 +59,8 @@ impl<I: Send + 'static> ParallelHandler<I> {
/// Create a new thread pool, each thread processing incoming data
/// with 'handler_fn'.
pub fn new<F>(name: &str, threads: usize, handler_fn: F) -> Self
where F: Fn(I) -> Result<(), Error> + Send + Clone + 'static,
where
F: Fn(I) -> Result<(), Error> + Send + Clone + 'static,
{
let mut handles = Vec::new();
let (input_tx, input_rx) = bounded::<I>(threads);
@ -89,7 +90,7 @@ impl<I: Send + 'static> ParallelHandler<I> {
}
}
})
.unwrap()
.unwrap(),
);
}
Self {
@ -132,19 +133,17 @@ impl<I: Send + 'static> ParallelHandler<I> {
}
fn join_threads(&mut self) -> Vec<String> {
let mut msg_list = Vec::new();
let mut i = 0;
while let Some(handle) = self.handles.pop() {
if let Err(panic) = handle.join() {
match panic.downcast::<&str>() {
Ok(panic_msg) => msg_list.push(
format!("thread {} ({}) panicked: {}", self.name, i, panic_msg)
),
Err(_) => msg_list.push(
format!("thread {} ({}) panicked", self.name, i)
),
Ok(panic_msg) => msg_list.push(format!(
"thread {} ({}) panicked: {}",
self.name, i, panic_msg
)),
Err(_) => msg_list.push(format!("thread {} ({}) panicked", self.name, i)),
}
}
i += 1;

View File

@ -1,6 +1,6 @@
use std::path::PathBuf;
use std::mem::MaybeUninit;
use std::time::{Instant, Duration};
use std::path::PathBuf;
use std::time::{Duration, Instant};
use anyhow::{bail, Error};
use nix::sys::stat::Mode;
@ -8,11 +8,12 @@ use nix::sys::stat::Mode;
use proxmox_sys::fs::{create_path, CreateOptions};
use proxmox_http::client::{RateLimit, RateLimiter, ShareableRateLimit};
use proxmox_shared_memory::{Init, SharedMemory, SharedMutex};
use proxmox_shared_memory::{check_subtype, initialize_subtype};
use proxmox_shared_memory::{Init, SharedMemory, SharedMutex};
// openssl::sha::sha256(b"Proxmox Backup SharedRateLimiter v1.0")[0..8];
pub const PROXMOX_BACKUP_SHARED_RATE_LIMITER_MAGIC_1_0: [u8; 8] = [6, 58, 213, 96, 161, 122, 130, 117];
pub const PROXMOX_BACKUP_SHARED_RATE_LIMITER_MAGIC_1_0: [u8; 8] =
[6, 58, 213, 96, 161, 122, 130, 117];
const BASE_PATH: &str = pbs_buildcfg::rundir!("/shmem/tbf");
@ -61,11 +62,10 @@ impl Init for SharedRateLimiterData {
/// implements [Init]. This way we can share the limiter between
/// different processes.
pub struct SharedRateLimiter {
shmem: SharedMemory<SharedRateLimiterData>
shmem: SharedMemory<SharedRateLimiterData>,
}
impl SharedRateLimiter {
/// Creates a new mmap'ed instance.
///
/// Data is mapped in `/var/run/proxmox-backup/shmem/tbf/<name>` using
@ -80,10 +80,7 @@ impl SharedRateLimiter {
.owner(user.uid)
.group(user.gid);
create_path(
&path,
Some(dir_opts.clone()),
Some(dir_opts))?;
create_path(&path, Some(dir_opts.clone()), Some(dir_opts))?;
path.push(name);
@ -92,8 +89,7 @@ impl SharedRateLimiter {
.owner(user.uid)
.group(user.gid);
let shmem: SharedMemory<SharedRateLimiterData> =
SharedMemory::open(&path, file_opts)?;
let shmem: SharedMemory<SharedRateLimiterData> = SharedMemory::open(&path, file_opts)?;
shmem.data().tbf.lock().0.update_rate(rate, burst);
@ -103,17 +99,24 @@ impl SharedRateLimiter {
impl ShareableRateLimit for SharedRateLimiter {
fn update_rate(&self, rate: u64, bucket_size: u64) {
self.shmem.data().tbf.lock().0
self.shmem
.data()
.tbf
.lock()
.0
.update_rate(rate, bucket_size);
}
fn traffic(&self) -> u64 {
self.shmem.data().tbf.lock().0
.traffic()
self.shmem.data().tbf.lock().0.traffic()
}
fn register_traffic(&self, current_time: Instant, data_len: u64) -> Duration {
self.shmem.data().tbf.lock().0
self.shmem
.data()
.tbf
.lock()
.0
.register_traffic(current_time, data_len)
}
}

View File

@ -1,6 +1,6 @@
//! Helpers for common statistics tasks
use num_traits::NumAssignRef;
use num_traits::cast::ToPrimitive;
use num_traits::NumAssignRef;
/// Calculates the sum of a list of numbers
/// ```
@ -14,7 +14,7 @@ use num_traits::cast::ToPrimitive;
/// ```
pub fn sum<T>(list: &[T]) -> T
where
T: NumAssignRef + ToPrimitive
T: NumAssignRef + ToPrimitive,
{
let mut sum = T::zero();
for num in list {
@ -32,13 +32,13 @@ where
/// ```
pub fn mean<T>(list: &[T]) -> Option<f64>
where
T: NumAssignRef + ToPrimitive
T: NumAssignRef + ToPrimitive,
{
let len = list.len();
if len == 0 {
return None
return None;
}
Some(sum(list).to_f64()?/(list.len() as f64))
Some(sum(list).to_f64()? / (list.len() as f64))
}
/// Calculates the variance of a variable x
@ -50,13 +50,13 @@ where
/// ```
pub fn variance<T>(list: &[T]) -> Option<f64>
where
T: NumAssignRef + ToPrimitive
T: NumAssignRef + ToPrimitive,
{
covariance(list, list)
}
/// Calculates the (non-corrected) covariance of two variables x,y
pub fn covariance<X, Y> (x: &[X], y: &[Y]) -> Option<f64>
pub fn covariance<X, Y>(x: &[X], y: &[Y]) -> Option<f64>
where
X: NumAssignRef + ToPrimitive,
Y: NumAssignRef + ToPrimitive,
@ -64,19 +64,21 @@ where
let len_x = x.len();
let len_y = y.len();
if len_x == 0 || len_y == 0 || len_x != len_y {
return None
return None;
}
let mean_x = mean(x)?;
let mean_y = mean(y)?;
let covariance: f64 = (0..len_x).map(|i| {
let x = x[i].to_f64().unwrap_or(0.0);
let y = y[i].to_f64().unwrap_or(0.0);
(x - mean_x)*(y - mean_y)
}).sum();
let covariance: f64 = (0..len_x)
.map(|i| {
let x = x[i].to_f64().unwrap_or(0.0);
let y = y[i].to_f64().unwrap_or(0.0);
(x - mean_x) * (y - mean_y)
})
.sum();
Some(covariance/(len_x as f64))
Some(covariance / (len_x as f64))
}
/// Returns the factors `(a,b)` of a linear regression `y = a + bx`
@ -90,15 +92,15 @@ where
/// assert!((a - -4.0).abs() < 0.001);
/// assert!((b - 2.0).abs() < 0.001);
/// ```
pub fn linear_regression<X, Y> (x: &[X], y: &[Y]) -> Option<(f64, f64)>
pub fn linear_regression<X, Y>(x: &[X], y: &[Y]) -> Option<(f64, f64)>
where
X: NumAssignRef + ToPrimitive,
Y: NumAssignRef + ToPrimitive
Y: NumAssignRef + ToPrimitive,
{
let len_x = x.len();
let len_y = y.len();
if len_x == 0 || len_y == 0 || len_x != len_y {
return None
return None;
}
let mean_x = mean(x)?;
@ -113,11 +115,11 @@ where
let x_mean_x = x - mean_x;
covariance += x_mean_x*(y - mean_y);
covariance += x_mean_x * (y - mean_y);
variance += x_mean_x * x_mean_x;
}
let beta = covariance/variance;
let alpha = mean_y - beta*mean_x;
Some((alpha,beta))
let beta = covariance / variance;
let alpha = mean_y - beta * mean_x;
Some((alpha, beta))
}

View File

@ -1,4 +1,4 @@
use anyhow::{Error, format_err, bail};
use anyhow::{bail, format_err, Error};
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize, Serialize};
@ -6,16 +6,13 @@ use serde_json::json;
use proxmox_schema::api;
use proxmox_sys::fs::{replace_file, CreateOptions};
use proxmox_http::client::SimpleHttp;
use proxmox_sys::fs::{replace_file, CreateOptions};
use pbs_tools::json::json_object_to_query;
use crate::config::node;
use crate::tools::{
self,
pbs_simple_http,
};
use crate::tools::{self, pbs_simple_http};
/// How long the local key is valid for in between remote checks
pub const MAX_LOCAL_KEY_AGE: i64 = 15 * 24 * 3600;
@ -41,7 +38,9 @@ pub enum SubscriptionStatus {
INVALID,
}
impl Default for SubscriptionStatus {
fn default() -> Self { SubscriptionStatus::NOTFOUND }
fn default() -> Self {
SubscriptionStatus::NOTFOUND
}
}
impl std::fmt::Display for SubscriptionStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -62,41 +61,41 @@ impl std::fmt::Display for SubscriptionStatus {
},
)]
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all="kebab-case")]
#[serde(rename_all = "kebab-case")]
/// Proxmox subscription information
pub struct SubscriptionInfo {
/// Subscription status from the last check
pub status: SubscriptionStatus,
/// the server ID, if permitted to access
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub serverid: Option<String>,
/// timestamp of the last check done
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub checktime: Option<i64>,
/// the subscription key, if set and permitted to access
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
/// a more human readable status message
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
/// human readable productname of the set subscription
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub productname: Option<String>,
/// register date of the set subscription
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub regdate: Option<String>,
/// next due date of the set subscription
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub nextduedate: Option<String>,
/// URL to the web shop
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
async fn register_subscription(
key: &str,
server_id: &str,
checktime: i64
checktime: i64,
) -> Result<(String, String), Error> {
// WHCMS sample code feeds the key into this, but it's just a challenge, so keep it simple
let rand = hex::encode(&proxmox_sys::linux::random_data(16)?);
@ -120,7 +119,9 @@ async fn register_subscription(
let uri = "https://shop.proxmox.com/modules/servers/licensing/verify.php";
let query = json_object_to_query(params)?;
let response = client.post(uri, Some(query), Some("application/x-www-form-urlencoded")).await?;
let response = client
.post(uri, Some(query), Some("application/x-www-form-urlencoded"))
.await?;
let body = SimpleHttp::response_body_string(response).await?;
Ok((body, challenge))
@ -132,7 +133,7 @@ fn parse_status(value: &str) -> SubscriptionStatus {
"new" => SubscriptionStatus::NEW,
"notfound" => SubscriptionStatus::NOTFOUND,
"invalid" => SubscriptionStatus::INVALID,
_ => SubscriptionStatus::INVALID,
_ => SubscriptionStatus::INVALID,
}
}
@ -164,15 +165,16 @@ fn parse_register_response(
"productname" => info.productname = Some(value.into()),
"regdate" => info.regdate = Some(value.into()),
"nextduedate" => info.nextduedate = Some(value.into()),
"message" if value == "Directory Invalid" =>
info.message = Some("Invalid Server ID".into()),
"message" if value == "Directory Invalid" => {
info.message = Some("Invalid Server ID".into())
}
"message" => info.message = Some(value.into()),
"validdirectory" => {
if value.split(',').find(is_server_id) == None {
bail!("Server ID does not match");
}
info.serverid = Some(server_id.to_owned());
},
}
"md5hash" => md5hash = value.to_owned(),
_ => (),
}
@ -182,7 +184,11 @@ fn parse_register_response(
let response_raw = format!("{}{}", SHARED_KEY_DATA, challenge);
let expected = hex::encode(&tools::md5sum(response_raw.as_bytes())?);
if expected != md5hash {
bail!("Subscription API challenge failed, expected {} != got {}", expected, md5hash);
bail!(
"Subscription API challenge failed, expected {} != got {}",
expected,
md5hash
);
}
}
Ok(info)
@ -210,29 +216,38 @@ fn test_parse_register_response() -> Result<(), Error> {
let checktime = 1600000000;
let salt = "cf44486bddb6ad0145732642c45b2957";
let info = parse_register_response(response, key.to_owned(), server_id.to_owned(), checktime, salt)?;
let info = parse_register_response(
response,
key.to_owned(),
server_id.to_owned(),
checktime,
salt,
)?;
assert_eq!(info, SubscriptionInfo {
key: Some(key),
serverid: Some(server_id),
status: SubscriptionStatus::ACTIVE,
checktime: Some(checktime),
url: Some("https://www.proxmox.com/en/proxmox-backup-server/pricing".into()),
message: None,
nextduedate: Some("2021-09-19".into()),
regdate: Some("2020-09-19 00:00:00".into()),
productname: Some("Proxmox Backup Server Test Subscription -1 year".into()),
});
assert_eq!(
info,
SubscriptionInfo {
key: Some(key),
serverid: Some(server_id),
status: SubscriptionStatus::ACTIVE,
checktime: Some(checktime),
url: Some("https://www.proxmox.com/en/proxmox-backup-server/pricing".into()),
message: None,
nextduedate: Some("2021-09-19".into()),
regdate: Some("2020-09-19 00:00:00".into()),
productname: Some("Proxmox Backup Server Test Subscription -1 year".into()),
}
);
Ok(())
}
/// queries the up to date subscription status and parses the response
pub fn check_subscription(key: String, server_id: String) -> Result<SubscriptionInfo, Error> {
let now = proxmox_time::epoch_i64();
let (response, challenge) = proxmox_async::runtime::block_on(register_subscription(&key, &server_id, now))
.map_err(|err| format_err!("Error checking subscription: {}", err))?;
let (response, challenge) =
proxmox_async::runtime::block_on(register_subscription(&key, &server_id, now))
.map_err(|err| format_err!("Error checking subscription: {}", err))?;
parse_register_response(&response, key, server_id, now, &challenge)
.map_err(|err| format_err!("Error parsing subscription check response: {}", err))
@ -240,16 +255,27 @@ pub fn check_subscription(key: String, server_id: String) -> Result<Subscription
/// reads in subscription information and does a basic integrity verification
pub fn read_subscription() -> Result<Option<SubscriptionInfo>, Error> {
let cfg = proxmox_sys::fs::file_read_optional_string(&SUBSCRIPTION_FN)?;
let cfg = if let Some(cfg) = cfg { cfg } else { return Ok(None); };
let cfg = if let Some(cfg) = cfg {
cfg
} else {
return Ok(None);
};
let mut cfg = cfg.lines();
// first line is key in plain
let _key = if let Some(key) = cfg.next() { key } else { return Ok(None) };
let _key = if let Some(key) = cfg.next() {
key
} else {
return Ok(None);
};
// second line is checksum of encoded data
let checksum = if let Some(csum) = cfg.next() { csum } else { return Ok(None) };
let checksum = if let Some(csum) = cfg.next() {
csum
} else {
return Ok(None);
};
let encoded: String = cfg.collect::<String>();
let decoded = base64::decode(encoded.to_owned())?;
@ -257,11 +283,16 @@ pub fn read_subscription() -> Result<Option<SubscriptionInfo>, Error> {
let info: SubscriptionInfo = serde_json::from_str(decoded)?;
let new_checksum = format!("{}{}{}", info.checktime.unwrap_or(0), encoded, SHARED_KEY_DATA);
let new_checksum = format!(
"{}{}{}",
info.checktime.unwrap_or(0),
encoded,
SHARED_KEY_DATA
);
let new_checksum = base64::encode(tools::md5sum(new_checksum.as_bytes())?);
if checksum != new_checksum {
return Ok(Some( SubscriptionInfo {
return Ok(Some(SubscriptionInfo {
status: SubscriptionStatus::INVALID,
message: Some("checksum mismatch".to_string()),
..info
@ -269,15 +300,16 @@ pub fn read_subscription() -> Result<Option<SubscriptionInfo>, Error> {
}
let age = proxmox_time::epoch_i64() - info.checktime.unwrap_or(0);
if age < -5400 { // allow some delta for DST changes or time syncs, 1.5h
return Ok(Some( SubscriptionInfo {
if age < -5400 {
// allow some delta for DST changes or time syncs, 1.5h
return Ok(Some(SubscriptionInfo {
status: SubscriptionStatus::INVALID,
message: Some("last check date too far in the future".to_string()),
..info
}));
} else if age > MAX_LOCAL_KEY_AGE + MAX_KEY_CHECK_FAILURE_AGE {
if let SubscriptionStatus::ACTIVE = info.status {
return Ok(Some( SubscriptionInfo {
return Ok(Some(SubscriptionInfo {
status: SubscriptionStatus::INVALID,
message: Some("subscription information too old".to_string()),
..info
@ -299,7 +331,12 @@ pub fn write_subscription(info: SubscriptionInfo) -> Result<(), Error> {
format!("{}\n", info.key.unwrap())
} else {
let encoded = base64::encode(serde_json::to_string(&info)?);
let csum = format!("{}{}{}", info.checktime.unwrap_or(0), encoded, SHARED_KEY_DATA);
let csum = format!(
"{}{}{}",
info.checktime.unwrap_or(0),
encoded,
SHARED_KEY_DATA
);
let csum = base64::encode(tools::md5sum(csum.as_bytes())?);
format!("{}\n{}\n{}\n", info.key.unwrap(), csum, encoded)
};
@ -334,13 +371,10 @@ pub fn update_apt_auth(key: Option<String>, password: Option<String>) -> Result<
(Some(key), Some(password)) => {
let conf = format!(
"machine enterprise.proxmox.com/debian/pbs\n login {}\n password {}\n",
key,
password,
key, password,
);
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
let file_opts = CreateOptions::new()
.perm(mode)
.owner(nix::unistd::ROOT);
let file_opts = CreateOptions::new().perm(mode).owner(nix::unistd::ROOT);
// we use a namespaced .conf file, so just overwrite..
replace_file(auth_conf, conf.as_bytes(), file_opts, true)
@ -350,7 +384,8 @@ pub fn update_apt_auth(key: Option<String>, password: Option<String>) -> Result<
Ok(()) => Ok(()),
Err(nix::Error::Sys(nix::errno::Errno::ENOENT)) => Ok(()), // ignore not existing
Err(err) => Err(err),
}.map_err(|e| format_err!("Error clearing apt auth config - {}", e))?,
}
.map_err(|e| format_err!("Error clearing apt auth config - {}", e))?,
}
Ok(())
}

View File

@ -8,7 +8,6 @@ use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlug
use proxmox_sys::{fs::replace_file, fs::CreateOptions};
lazy_static! {
pub static ref SERVICE_CONFIG: SectionConfig = init_service();
pub static ref TIMER_CONFIG: SectionConfig = init_timer();
@ -16,25 +15,24 @@ lazy_static! {
}
fn init_service() -> SectionConfig {
let mut config = SectionConfig::with_systemd_syntax(&SYSTEMD_SECTION_NAME_SCHEMA);
match SystemdUnitSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Unit".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdInstallSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Install".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdServiceSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Service".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
@ -45,25 +43,24 @@ fn init_service() -> SectionConfig {
}
fn init_timer() -> SectionConfig {
let mut config = SectionConfig::with_systemd_syntax(&SYSTEMD_SECTION_NAME_SCHEMA);
match SystemdUnitSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Unit".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdInstallSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Install".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdTimerSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Timer".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
@ -74,25 +71,24 @@ fn init_timer() -> SectionConfig {
}
fn init_mount() -> SectionConfig {
let mut config = SectionConfig::with_systemd_syntax(&SYSTEMD_SECTION_NAME_SCHEMA);
match SystemdUnitSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Unit".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdInstallSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Install".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdMountSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Mount".to_string(), None, obj_schema);
config.register_plugin(plugin);
}
@ -102,8 +98,10 @@ fn init_mount() -> SectionConfig {
config
}
fn parse_systemd_config(config: &SectionConfig, filename: &str) -> Result<SectionConfigData, Error> {
fn parse_systemd_config(
config: &SectionConfig,
filename: &str,
) -> Result<SectionConfigData, Error> {
let raw = proxmox_sys::fs::file_get_contents(filename)?;
let input = String::from_utf8(raw)?;
@ -124,14 +122,16 @@ pub fn parse_systemd_mount(filename: &str) -> Result<SectionConfigData, Error> {
parse_systemd_config(&MOUNT_CONFIG, filename)
}
fn save_systemd_config(config: &SectionConfig, filename: &str, data: &SectionConfigData) -> Result<(), Error> {
fn save_systemd_config(
config: &SectionConfig,
filename: &str,
data: &SectionConfigData,
) -> Result<(), Error> {
let raw = config.write(filename, data)?;
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
// set the correct owner/group/permissions while saving file, owner(rw) = root
let options = CreateOptions::new()
.perm(mode)
.owner(nix::unistd::ROOT);
let options = CreateOptions::new().perm(mode).owner(nix::unistd::ROOT);
replace_file(filename, raw.as_bytes(), options, true)?;

View File

@ -1,34 +1,30 @@
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use proxmox_schema::*;
use pbs_api_types::SINGLE_LINE_COMMENT_FORMAT;
use proxmox_schema::*;
pub const SYSTEMD_SECTION_NAME_SCHEMA: Schema = StringSchema::new(
"Section name")
pub const SYSTEMD_SECTION_NAME_SCHEMA: Schema = StringSchema::new("Section name")
.format(&ApiStringFormat::Enum(&[
EnumEntry::new("Unit", "Unit"),
EnumEntry::new("Timer", "Timer"),
EnumEntry::new("Install", "Install"),
EnumEntry::new("Mount", "Mount"),
EnumEntry::new("Service", "Service")]))
EnumEntry::new("Service", "Service"),
]))
.schema();
pub const SYSTEMD_STRING_SCHEMA: Schema =
StringSchema::new("Systemd configuration value.")
pub const SYSTEMD_STRING_SCHEMA: Schema = StringSchema::new("Systemd configuration value.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.schema();
pub const SYSTEMD_STRING_ARRAY_SCHEMA: Schema = ArraySchema::new(
"Array of Strings", &SYSTEMD_STRING_SCHEMA)
.schema();
pub const SYSTEMD_STRING_ARRAY_SCHEMA: Schema =
ArraySchema::new("Array of Strings", &SYSTEMD_STRING_SCHEMA).schema();
pub const SYSTEMD_TIMESPAN_ARRAY_SCHEMA: Schema = ArraySchema::new(
"Array of time spans", &SYSTEMD_TIMESPAN_SCHEMA)
.schema();
pub const SYSTEMD_TIMESPAN_ARRAY_SCHEMA: Schema =
ArraySchema::new("Array of time spans", &SYSTEMD_TIMESPAN_SCHEMA).schema();
pub const SYSTEMD_CALENDAR_EVENT_ARRAY_SCHEMA: Schema = ArraySchema::new(
"Array of calendar events", &SYSTEMD_CALENDAR_EVENT_SCHEMA)
.schema();
pub const SYSTEMD_CALENDAR_EVENT_ARRAY_SCHEMA: Schema =
ArraySchema::new("Array of calendar events", &SYSTEMD_CALENDAR_EVENT_SCHEMA).schema();
#[api(
properties: {
@ -70,44 +66,44 @@ pub const SYSTEMD_CALENDAR_EVENT_ARRAY_SCHEMA: Schema = ArraySchema::new(
#[allow(non_snake_case)]
/// Systemd Timer Section
pub struct SystemdTimerSection {
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnCalendar: Option<Vec<String>>,
/// If true, the time when the service unit was last triggered is stored on disk.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Persistent: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnActiveSec: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnBootSec: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnStartupSec: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnUnitActiveSec: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnUnitInactiveSec: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub RandomizedDelaySec: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub AccuracySec: Option<String>,
/// Trigger when system clock jumps.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnClockChange: Option<bool>,
/// Trigger when time zone changes.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub OnTimezomeChange: Option<bool>,
/// The unit to activate when this timer elapses.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Unit: Option<String>,
/// If true, an elapsing timer will cause the system to resume from suspend.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub WakeSystem: Option<bool>,
/// If true, an elapsed timer will stay loaded, and its state remains queryable.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub RemainAfterElapse: Option<bool>,
}
@ -128,9 +124,9 @@ pub struct SystemdTimerSection {
/// Systemd Service Section
pub struct SystemdServiceSection {
/// The process start-up type for this service unit.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Type: Option<ServiceStartup>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ExecStart: Option<Vec<String>>,
}
@ -142,7 +138,7 @@ pub struct SystemdUnitSection {
/// A human readable name for the unit.
pub Description: String,
/// Check whether the system has AC power.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ConditionACPower: Option<bool>,
}
@ -173,16 +169,16 @@ pub struct SystemdUnitSection {
#[allow(non_snake_case)]
/// Systemd Install Section
pub struct SystemdInstallSection {
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Alias: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Also: Option<Vec<String>>,
/// DefaultInstance for template unit.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub DefaultInstance: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub WantedBy: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub RequiredBy: Option<Vec<String>>,
}
@ -203,27 +199,27 @@ pub struct SystemdMountSection {
/// absolute path of a file or directory for the mount point
pub Where: String,
/// Takes a string for the file system type. See mount(8) for details.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Type: Option<String>,
/// Mount options to use when mounting. This takes a comma-separated list of options.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub Options: Option<String>,
/// If true, parsing of the options specified in Options= is relaxed, and unknown mount options are tolerated.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub SloppyOptions: Option<bool>,
/// Use lazy unmount
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub LazyUnmount: Option<bool>,
/// Use forces unmount
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ForceUnmount: Option<bool>,
/// Directories of mount points (and any parent directories) are
/// automatically created if needed. Takes an access mode in octal
/// notation. Defaults to 0755.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub DirectoryMode: Option<String>,
/// Configures the time to wait for the mount command to finish.
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub TimeoutSec: Option<String>,
}
@ -246,12 +242,12 @@ pub enum ServiceStartup {
Notify,
}
pub const SYSTEMD_TIMESPAN_SCHEMA: Schema = StringSchema::new(
"systemd time span")
pub const SYSTEMD_TIMESPAN_SCHEMA: Schema = StringSchema::new("systemd time span")
.format(&ApiStringFormat::VerifyFn(proxmox_time::verify_time_span))
.schema();
pub const SYSTEMD_CALENDAR_EVENT_SCHEMA: Schema = StringSchema::new(
"systemd calendar event")
.format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event))
pub const SYSTEMD_CALENDAR_EVENT_SCHEMA: Schema = StringSchema::new("systemd calendar event")
.format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.schema();

View File

@ -20,7 +20,9 @@ fn run_command(mut command: Command) -> Result<(), Error> {
m
}
})
.unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
.unwrap_or_else(|_| {
String::from("non utf8 error message (suppressed)")
});
bail!("status code: {} - {}", code, msg);
}
@ -29,7 +31,8 @@ fn run_command(mut command: Command) -> Result<(), Error> {
}
}
Ok(())
}).map_err(|err| format_err!("command {:?} failed - {}", command, err))?;
})
.map_err(|err| format_err!("command {:?} failed - {}", command, err))?;
Ok(())
}
@ -96,7 +99,6 @@ pub fn reload_unit(unit: &str) -> Result<(), Error> {
#[test]
fn test_escape_unit() -> Result<(), Error> {
fn test_escape(i: &str, expected: &str, is_path: bool) {
use proxmox_sys::systemd::{escape_unit, unescape_unit};
let escaped = escape_unit(i, is_path);