From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [RFC proxmox-backup 1/1] use proxmox_jobstate and proxmox_cert_management crates
Date: Wed, 18 Oct 2023 12:39:11 +0200 [thread overview]
Message-ID: <20231018103911.3798182-5-d.csapak@proxmox.com> (raw)
In-Reply-To: <20231018103911.3798182-1-d.csapak@proxmox.com>
and remove the old code here.
For this to work now, we have to use the ServerConfig setup, so that the
directories/users are accessible globally.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
Cargo.toml | 8 +
pbs-api-types/Cargo.toml | 1 +
pbs-api-types/src/jobs.rs | 41 +---
src/auth_helpers.rs | 184 +---------------
src/bin/proxmox-backup-api.rs | 8 +-
src/bin/proxmox-backup-manager.rs | 2 +
src/bin/proxmox-backup-proxy.rs | 2 +
src/server/jobstate.rs | 347 +-----------------------------
src/server/mod.rs | 21 ++
9 files changed, 52 insertions(+), 562 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index cfbf2ba1..de0c8138 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -58,11 +58,13 @@ proxmox-apt = "0.10.5"
proxmox-async = "0.4"
proxmox-auth-api = "0.3"
proxmox-borrow = "1"
+proxmox-cert-management = "0.1"
proxmox-compression = "0.2"
proxmox-fuse = "0.1.3"
proxmox-http = { version = "0.9.0", features = [ "client", "http-helpers", "websocket" ] } # see below
proxmox-human-byte = "0.1"
proxmox-io = "1.0.1" # tools and client use "tokio" feature
+proxmox-jobstate = "0.1"
proxmox-lang = "1.1"
proxmox-ldap = "0.2.1"
proxmox-metrics = "0.3"
@@ -74,6 +76,7 @@ proxmox-router = { version = "2.0.0", default_features = false }
proxmox-schema = "2.0.0"
proxmox-section-config = "2"
proxmox-serde = "0.1.1"
+proxmox-server-config = "0.1"
proxmox-shared-memory = "0.3.0"
proxmox-sortable-macro = "0.1.2"
proxmox-subscription = { version = "0.4", features = [ "api-types" ] }
@@ -202,10 +205,12 @@ zstd.workspace = true
proxmox-apt.workspace = true
proxmox-async.workspace = true
proxmox-auth-api = { workspace = true, features = [ "api", "pam-authenticator" ] }
+proxmox-cert-management.workspace = true
proxmox-compression.workspace = true
proxmox-http = { workspace = true, features = [ "client-trait", "proxmox-async", "rate-limited-stream" ] } # pbs-client doesn't use these
proxmox-human-byte.workspace = true
proxmox-io.workspace = true
+proxmox-jobstate.workspace = true
proxmox-lang.workspace = true
proxmox-ldap.workspace = true
proxmox-metrics.workspace = true
@@ -215,6 +220,7 @@ proxmox-router = { workspace = true, features = [ "cli", "server"] }
proxmox-schema = { workspace = true, features = [ "api-macro" ] }
proxmox-section-config.workspace = true
proxmox-serde = { workspace = true, features = [ "serde_json" ] }
+proxmox-server-config.workspace = true
proxmox-shared-memory.workspace = true
proxmox-sortable-macro.workspace = true
proxmox-subscription.workspace = true
@@ -252,6 +258,7 @@ proxmox-rrd.workspace = true
#proxmox-http = { path = "../proxmox/proxmox-http" }
#proxmox-human-byte = { path = "../proxmox/proxmox-human-byte" }
#proxmox-io = { path = "../proxmox/proxmox-io" }
+#proxmox-jobstate = { path = "../proxmox/proxmox-jobstate" }
#proxmox-lang = { path = "../proxmox/proxmox-lang" }
#proxmox-ldap = { path = "../proxmox/proxmox-ldap" }
#proxmox-metrics = { path = "../proxmox/proxmox-metrics" }
@@ -261,6 +268,7 @@ proxmox-rrd.workspace = true
#proxmox-schema = { path = "../proxmox/proxmox-schema" }
#proxmox-section-config = { path = "../proxmox/proxmox-section-config" }
#proxmox-serde = { path = "../proxmox/proxmox-serde" }
+#proxmox-server-config = { path = "../proxmox/proxmox-server-config" }
#proxmox-shared-memory = { path = "../proxmox/proxmox-shared-memory" }
#proxmox-sortable-macro = { path = "../proxmox/proxmox-sortable-macro" }
#proxmox-subscription = { path = "../proxmox/proxmox-subscription" }
diff --git a/pbs-api-types/Cargo.toml b/pbs-api-types/Cargo.toml
index 31b69f62..13f7d76b 100644
--- a/pbs-api-types/Cargo.toml
+++ b/pbs-api-types/Cargo.toml
@@ -16,6 +16,7 @@ serde_plain.workspace = true
proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
proxmox-human-byte.workspace = true
+proxmox-jobstate.workspace=true
proxmox-lang.workspace=true
proxmox-schema = { workspace = true, features = [ "api-macro" ] }
proxmox-serde.workspace = true
diff --git a/pbs-api-types/src/jobs.rs b/pbs-api-types/src/jobs.rs
index 23e19b7b..a932c29d 100644
--- a/pbs-api-types/src/jobs.rs
+++ b/pbs-api-types/src/jobs.rs
@@ -13,6 +13,9 @@ use crate::{
SINGLE_LINE_COMMENT_SCHEMA,
};
+// re-exported for compatibility
+pub use proxmox_jobstate::JobScheduleStatus;
+
const_regex! {
/// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
@@ -63,44 +66,6 @@ pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
.default(false)
.schema();
-#[api(
- properties: {
- "next-run": {
- description: "Estimated time of the next run (UNIX epoch).",
- optional: true,
- type: Integer,
- },
- "last-run-state": {
- description: "Result of the last run.",
- optional: true,
- type: String,
- },
- "last-run-upid": {
- description: "Task UPID of the last run.",
- optional: true,
- type: String,
- },
- "last-run-endtime": {
- description: "Endtime of the last run.",
- optional: true,
- type: Integer,
- },
- }
-)]
-#[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
-#[serde(rename_all = "kebab-case")]
-/// Job Scheduling Status
-pub struct JobScheduleStatus {
- #[serde(skip_serializing_if = "Option::is_none")]
- pub next_run: Option<i64>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub last_run_state: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub last_run_upid: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub last_run_endtime: Option<i64>,
-}
-
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
diff --git a/src/auth_helpers.rs b/src/auth_helpers.rs
index c2eaaef1..41c3c569 100644
--- a/src/auth_helpers.rs
+++ b/src/auth_helpers.rs
@@ -1,189 +1,17 @@
-use std::path::PathBuf;
-
-use anyhow::{bail, format_err, Error};
-use lazy_static::lazy_static;
-use openssl::pkey::{PKey, Private, Public};
-use openssl::rsa::Rsa;
-use openssl::sha;
+use anyhow::Error;
use pbs_config::BackupLockGuard;
-use proxmox_lang::try_block;
-use proxmox_sys::fs::{file_get_contents, replace_file, CreateOptions};
-use pbs_api_types::Userid;
use pbs_buildcfg::configdir;
use serde_json::json;
pub use crate::auth::setup_auth_context;
-fn compute_csrf_secret_digest(timestamp: i64, secret: &[u8], userid: &Userid) -> String {
- let mut hasher = sha::Sha256::new();
- let data = format!("{:08X}:{}:", timestamp, userid);
- hasher.update(data.as_bytes());
- hasher.update(secret);
-
- base64::encode_config(hasher.finish(), base64::STANDARD_NO_PAD)
-}
-
-pub fn assemble_csrf_prevention_token(secret: &[u8], userid: &Userid) -> String {
- let epoch = proxmox_time::epoch_i64();
-
- let digest = compute_csrf_secret_digest(epoch, secret, userid);
-
- format!("{:08X}:{}", epoch, digest)
-}
-
-pub fn verify_csrf_prevention_token(
- secret: &[u8],
- userid: &Userid,
- token: &str,
- min_age: i64,
- max_age: i64,
-) -> Result<i64, Error> {
- use std::collections::VecDeque;
-
- let mut parts: VecDeque<&str> = token.split(':').collect();
-
- try_block!({
- if parts.len() != 2 {
- bail!("format error - wrong number of parts.");
- }
-
- let timestamp = parts.pop_front().unwrap();
- let sig = parts.pop_front().unwrap();
-
- let ttime = i64::from_str_radix(timestamp, 16)
- .map_err(|err| format_err!("timestamp format error - {}", err))?;
-
- let digest = compute_csrf_secret_digest(ttime, secret, userid);
-
- if digest != sig {
- bail!("invalid signature.");
- }
-
- let now = proxmox_time::epoch_i64();
-
- let age = now - ttime;
- if age < min_age {
- bail!("timestamp newer than expected.");
- }
-
- if age > max_age {
- bail!("timestamp too old.");
- }
-
- Ok(age)
- })
- .map_err(|err| format_err!("invalid csrf token - {}", err))
-}
-
-pub fn generate_csrf_key() -> Result<(), Error> {
- let path = PathBuf::from(configdir!("/csrf.key"));
-
- if path.exists() {
- return Ok(());
- }
-
- let rsa = Rsa::generate(2048).unwrap();
-
- let pem = rsa.private_key_to_pem()?;
-
- use nix::sys::stat::Mode;
-
- let backup_user = pbs_config::backup_user()?;
-
- replace_file(
- &path,
- &pem,
- CreateOptions::new()
- .perm(Mode::from_bits_truncate(0o0640))
- .owner(nix::unistd::ROOT)
- .group(backup_user.gid),
- true,
- )?;
-
- Ok(())
-}
-
-pub fn generate_auth_key() -> Result<(), Error> {
- let priv_path = PathBuf::from(configdir!("/authkey.key"));
-
- let mut public_path = priv_path.clone();
- public_path.set_extension("pub");
-
- if priv_path.exists() && public_path.exists() {
- return Ok(());
- }
-
- let rsa = Rsa::generate(4096).unwrap();
-
- let priv_pem = rsa.private_key_to_pem()?;
-
- use nix::sys::stat::Mode;
-
- replace_file(
- &priv_path,
- &priv_pem,
- CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)),
- true,
- )?;
-
- let public_pem = rsa.public_key_to_pem()?;
-
- let backup_user = pbs_config::backup_user()?;
-
- replace_file(
- &public_path,
- &public_pem,
- CreateOptions::new()
- .perm(Mode::from_bits_truncate(0o0640))
- .owner(nix::unistd::ROOT)
- .group(backup_user.gid),
- true,
- )?;
-
- Ok(())
-}
-
-pub fn csrf_secret() -> &'static [u8] {
- lazy_static! {
- static ref SECRET: Vec<u8> = file_get_contents(configdir!("/csrf.key")).unwrap();
- }
-
- &SECRET
-}
-
-fn load_public_auth_key() -> Result<PKey<Public>, Error> {
- let pem = file_get_contents(configdir!("/authkey.pub"))?;
- let rsa = Rsa::public_key_from_pem(&pem)?;
- let key = PKey::from_rsa(rsa)?;
-
- Ok(key)
-}
-
-pub fn public_auth_key() -> &'static PKey<Public> {
- lazy_static! {
- static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
- }
-
- &KEY
-}
-
-fn load_private_auth_key() -> Result<PKey<Private>, Error> {
- let pem = file_get_contents(configdir!("/authkey.key"))?;
- let rsa = Rsa::private_key_from_pem(&pem)?;
- let key = PKey::from_rsa(rsa)?;
-
- Ok(key)
-}
-
-pub fn private_auth_key() -> &'static PKey<Private> {
- lazy_static! {
- static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
- }
-
- &KEY
-}
+// re-exported
+pub use proxmox_cert_management::{
+ assemble_csrf_prevention_token, csrf_secret, generate_auth_key, generate_csrf_key,
+ private_auth_key, public_auth_key,
+};
const LDAP_PASSWORDS_FILENAME: &str = configdir!("/ldap_passwords.json");
diff --git a/src/bin/proxmox-backup-api.rs b/src/bin/proxmox-backup-api.rs
index c6c24449..4d0837eb 100644
--- a/src/bin/proxmox-backup-api.rs
+++ b/src/bin/proxmox-backup-api.rs
@@ -6,6 +6,7 @@ use futures::*;
use http::Response;
use hyper::{Body, StatusCode};
+use proxmox_backup::server::setup_server_config;
use proxmox_lang::try_block;
use proxmox_router::RpcEnvironmentType;
use proxmox_sys::fs::CreateOptions;
@@ -48,13 +49,12 @@ async fn run() -> Result<(), Error> {
bail!("unable to inititialize syslog - {}", err);
}
- config::create_configdir()?;
+ setup_server_config()?;
+
+ config::check_configdir_permissions()?;
config::update_self_signed_cert(false)?;
- proxmox_backup::server::create_run_dir()?;
- proxmox_backup::server::create_state_dir()?;
- proxmox_backup::server::create_active_operations_dir()?;
proxmox_backup::server::jobstate::create_jobstate_dir()?;
proxmox_backup::tape::create_tape_status_dir()?;
proxmox_backup::tape::create_drive_state_dir()?;
diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs
index b4cb6cb3..a5d777bb 100644
--- a/src/bin/proxmox-backup-manager.rs
+++ b/src/bin/proxmox-backup-manager.rs
@@ -507,6 +507,8 @@ async fn run() -> Result<(), Error> {
fn main() -> Result<(), Error> {
proxmox_backup::tools::setup_safe_path_env();
+ proxmox_backup::server::setup_server_config()?;
+
proxmox_async::runtime::main(run())
}
diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
index f38a02bd..7eafdbc4 100644
--- a/src/bin/proxmox-backup-proxy.rs
+++ b/src/bin/proxmox-backup-proxy.rs
@@ -197,6 +197,8 @@ async fn run() -> Result<(), Error> {
bail!("unable to inititialize syslog - {err}");
}
+ server::setup_server_config()?;
+
proxmox_backup::auth_helpers::setup_auth_context(false);
let rrd_cache = initialize_rrd_cache()?;
diff --git a/src/server/jobstate.rs b/src/server/jobstate.rs
index be9dac42..e4a74c65 100644
--- a/src/server/jobstate.rs
+++ b/src/server/jobstate.rs
@@ -1,343 +1,6 @@
-//! Generic JobState handling
-//!
-//! A 'Job' can have 3 states
-//! - Created, when a schedule was created but never executed
-//! - Started, when a job is running right now
-//! - Finished, when a job was running in the past
-//!
-//! and is identified by 2 values: jobtype and jobname (e.g. 'syncjob' and 'myfirstsyncjob')
-//!
-//! This module Provides 2 helper structs to handle those coniditons
-//! 'Job' which handles locking and writing to a file
-//! 'JobState' which is the actual state
-//!
-//! an example usage would be
-//! ```no_run
-//! # use anyhow::{bail, Error};
-//! # use proxmox_rest_server::TaskState;
-//! # use proxmox_backup::server::jobstate::*;
-//! # fn some_code() -> TaskState { TaskState::OK { endtime: 0 } }
-//! # fn code() -> Result<(), Error> {
-//! // locks the correct file under /var/lib
-//! // or fails if someone else holds the lock
-//! let mut job = match Job::new("jobtype", "jobname") {
-//! Ok(job) => job,
-//! Err(err) => bail!("could not lock jobstate"),
-//! };
-//!
-//! // job holds the lock, we can start it
-//! job.start("someupid")?;
-//! // do something
-//! let task_state = some_code();
-//! job.finish(task_state)?;
-//!
-//! // release the lock
-//! drop(job);
-//! # Ok(())
-//! # }
-//!
-//! ```
-use std::path::{Path, PathBuf};
+//! Generic JobState handling. Deprecated and reexported from [proxmox_jobstate](proxmox_jobstate)
-use anyhow::{bail, format_err, Error};
-use serde::{Deserialize, Serialize};
-
-use proxmox_sys::fs::{create_path, file_read_optional_string, replace_file, CreateOptions};
-
-use proxmox_time::CalendarEvent;
-
-use pbs_api_types::{JobScheduleStatus, UPID};
-use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
-use pbs_config::{open_backup_lockfile, BackupLockGuard};
-
-use proxmox_rest_server::{upid_read_status, worker_is_active_local, TaskState};
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "kebab-case")]
-/// Represents the State of a specific Job
-pub enum JobState {
- /// A job was created at 'time', but never started/finished
- Created { time: i64 },
- /// The Job was last started in 'upid',
- Started { upid: String },
- /// The Job was last started in 'upid', which finished with 'state', and was last updated at 'updated'
- Finished {
- upid: String,
- state: TaskState,
- updated: Option<i64>,
- },
-}
-
-/// Represents a Job and holds the correct lock
-pub struct Job {
- jobtype: String,
- jobname: String,
- /// The State of the job
- pub state: JobState,
- _lock: BackupLockGuard,
-}
-
-const JOB_STATE_BASEDIR: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/jobstates");
-
-/// Create jobstate stat dir with correct permission
-pub fn create_jobstate_dir() -> Result<(), Error> {
- let backup_user = pbs_config::backup_user()?;
-
- let opts = CreateOptions::new()
- .owner(backup_user.uid)
- .group(backup_user.gid);
-
- create_path(JOB_STATE_BASEDIR, Some(opts.clone()), Some(opts))
- .map_err(|err: Error| format_err!("unable to create job state dir - {err}"))?;
-
- Ok(())
-}
-
-fn get_path(jobtype: &str, jobname: &str) -> PathBuf {
- let mut path = PathBuf::from(JOB_STATE_BASEDIR);
- path.push(format!("{jobtype}-{jobname}.json"));
- path
-}
-
-fn get_lock<P>(path: P) -> Result<BackupLockGuard, Error>
-where
- P: AsRef<Path>,
-{
- let mut path = path.as_ref().to_path_buf();
- path.set_extension("lck");
- open_backup_lockfile(&path, None, true)
-}
-
-/// Removes the statefile of a job, this is useful if we delete a job
-pub fn remove_state_file(jobtype: &str, jobname: &str) -> Result<(), Error> {
- let mut path = get_path(jobtype, jobname);
- let _lock = get_lock(&path)?;
- if let Err(err) = std::fs::remove_file(&path) {
- if err.kind() != std::io::ErrorKind::NotFound {
- bail!("cannot remove statefile for {jobtype} - {jobname}: {err}");
- }
- }
- path.set_extension("lck");
- if let Err(err) = std::fs::remove_file(&path) {
- if err.kind() != std::io::ErrorKind::NotFound {
- bail!("cannot remove lockfile for {jobtype} - {jobname}: {err}");
- }
- }
- Ok(())
-}
-
-/// Creates the statefile with the state 'Created'
-/// overwrites if it exists already
-pub fn create_state_file(jobtype: &str, jobname: &str) -> Result<(), Error> {
- let mut job = Job::new(jobtype, jobname)?;
- job.write_state()
-}
-
-/// Tries to update the state file with the current time
-/// if the job is currently running, does nothing.
-/// Intended for use when the schedule changes.
-pub fn update_job_last_run_time(jobtype: &str, jobname: &str) -> Result<(), Error> {
- let mut job = match Job::new(jobtype, jobname) {
- Ok(job) => job,
- Err(_) => return Ok(()), // was locked (running), so do not update
- };
- let time = proxmox_time::epoch_i64();
-
- job.state = match JobState::load(jobtype, jobname)? {
- JobState::Created { .. } => JobState::Created { time },
- JobState::Started { .. } => return Ok(()), // currently running (without lock?)
- JobState::Finished {
- upid,
- state,
- updated: _,
- } => JobState::Finished {
- upid,
- state,
- updated: Some(time),
- },
- };
- job.write_state()
-}
-
-/// Returns the last run time of a job by reading the statefile
-/// Note that this is not locked
-pub fn last_run_time(jobtype: &str, jobname: &str) -> Result<i64, Error> {
- match JobState::load(jobtype, jobname)? {
- JobState::Created { time } => Ok(time),
- JobState::Finished {
- updated: Some(time),
- ..
- } => Ok(time),
- JobState::Started { upid }
- | JobState::Finished {
- upid,
- state: _,
- updated: None,
- } => {
- let upid: UPID = upid
- .parse()
- .map_err(|err| format_err!("could not parse upid from state: {err}"))?;
- Ok(upid.starttime)
- }
- }
-}
-
-impl JobState {
- /// Loads and deserializes the jobstate from type and name.
- /// When the loaded state indicates a started UPID,
- /// we go and check if it has already stopped, and
- /// returning the correct state.
- ///
- /// This does not update the state in the file.
- pub fn load(jobtype: &str, jobname: &str) -> Result<Self, Error> {
- if let Some(state) = file_read_optional_string(get_path(jobtype, jobname))? {
- match serde_json::from_str(&state)? {
- JobState::Started { upid } => {
- let parsed: UPID = upid
- .parse()
- .map_err(|err| format_err!("error parsing upid: {err}"))?;
-
- if !worker_is_active_local(&parsed) {
- let state = upid_read_status(&parsed).unwrap_or(TaskState::Unknown {
- endtime: parsed.starttime,
- });
-
- Ok(JobState::Finished {
- upid,
- state,
- updated: None,
- })
- } else {
- Ok(JobState::Started { upid })
- }
- }
- other => Ok(other),
- }
- } else {
- Ok(JobState::Created {
- time: proxmox_time::epoch_i64() - 30,
- })
- }
- }
-}
-
-impl Job {
- /// Creates a new instance of a job with the correct lock held
- /// (will be hold until the job is dropped again).
- ///
- /// This does not load the state from the file, to do that,
- /// 'load' must be called
- pub fn new(jobtype: &str, jobname: &str) -> Result<Self, Error> {
- let path = get_path(jobtype, jobname);
-
- let _lock = get_lock(path)?;
-
- Ok(Self {
- jobtype: jobtype.to_string(),
- jobname: jobname.to_string(),
- state: JobState::Created {
- time: proxmox_time::epoch_i64(),
- },
- _lock,
- })
- }
-
- /// Start the job and update the statefile accordingly
- /// Fails if the job was already started
- pub fn start(&mut self, upid: &str) -> Result<(), Error> {
- if let JobState::Started { .. } = self.state {
- bail!("cannot start job that is started!");
- }
-
- self.state = JobState::Started {
- upid: upid.to_string(),
- };
-
- self.write_state()
- }
-
- /// Finish the job and update the statefile accordingly with the given taskstate
- /// Fails if the job was not yet started
- pub fn finish(&mut self, state: TaskState) -> Result<(), Error> {
- let upid = match &self.state {
- JobState::Created { .. } => bail!("cannot finish when not started"),
- JobState::Started { upid } => upid,
- JobState::Finished { upid, .. } => upid,
- }
- .to_string();
-
- self.state = JobState::Finished {
- upid,
- state,
- updated: None,
- };
-
- self.write_state()
- }
-
- pub fn jobtype(&self) -> &str {
- &self.jobtype
- }
-
- pub fn jobname(&self) -> &str {
- &self.jobname
- }
-
- fn write_state(&mut self) -> Result<(), Error> {
- let serialized = serde_json::to_string(&self.state)?;
- let path = get_path(&self.jobtype, &self.jobname);
-
- let backup_user = pbs_config::backup_user()?;
- let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
- // set the correct owner/group/permissions while saving file
- // owner(rw) = backup, group(r)= backup
- let options = CreateOptions::new()
- .perm(mode)
- .owner(backup_user.uid)
- .group(backup_user.gid);
-
- replace_file(path, serialized.as_bytes(), options, false)
- }
-}
-
-pub fn compute_schedule_status(
- job_state: &JobState,
- schedule: Option<&str>,
-) -> Result<JobScheduleStatus, Error> {
- let (upid, endtime, state, last) = match job_state {
- JobState::Created { time } => (None, None, None, *time),
- JobState::Started { upid } => {
- let parsed_upid: UPID = upid.parse()?;
- (Some(upid), None, None, parsed_upid.starttime)
- }
- JobState::Finished {
- upid,
- state,
- updated,
- } => {
- let last = updated.unwrap_or_else(|| state.endtime());
- (
- Some(upid),
- Some(state.endtime()),
- Some(state.to_string()),
- last,
- )
- }
- };
-
- let mut status = JobScheduleStatus {
- last_run_upid: upid.map(String::from),
- last_run_state: state,
- last_run_endtime: endtime,
- ..Default::default()
- };
-
- if let Some(schedule) = schedule {
- if let Ok(event) = schedule.parse::<CalendarEvent>() {
- // ignore errors
- status.next_run = event.compute_next_event(last).unwrap_or(None);
- }
- }
-
- Ok(status)
-}
+pub use proxmox_jobstate::{
+ compute_schedule_status, create_jobstate_dir, create_state_file, last_run_time,
+ remove_state_file, update_job_last_run_time, Job, JobState,
+};
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 4e3b68ac..06b1d92e 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -5,6 +5,7 @@
//! tokio/hyper.
use anyhow::{format_err, Error};
+use proxmox_server_config::ServerConfig;
use serde_json::Value;
use proxmox_sys::fs::{create_path, CreateOptions};
@@ -92,3 +93,23 @@ pub fn create_active_operations_dir() -> Result<(), Error> {
.map_err(|err: Error| format_err!("unable to create active operations dir - {err}"))?;
Ok(())
}
+
+/// Setup basic directories and users configs
+pub fn setup_server_config() -> Result<(), Error> {
+ let root = nix::unistd::User::from_uid(nix::unistd::ROOT)
+ .unwrap()
+ .unwrap();
+
+ ServerConfig::new(
+ "proxmox-backup-api",
+ "/var/lib/proxmox-backup",
+ pbs_config::backup_user()?,
+ )?
+ .with_privileged_user(root)?
+ .with_log_dir(pbs_buildcfg::PROXMOX_BACKUP_LOG_DIR_M!())?
+ .with_state_dir(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M!())?
+ .with_run_dir(pbs_buildcfg::PROXMOX_BACKUP_RUN_DIR_M!())?
+ .with_config_dir(pbs_buildcfg::CONFIGDIR)?
+ .with_cert_dir(pbs_buildcfg::CONFIGDIR)?
+ .setup()
+}
--
2.30.2
next prev parent reply other threads:[~2023-10-18 10:39 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-18 10:39 [pbs-devel] [RFC proxmox/proxmox-backup] refactor common server code from pbs Dominik Csapak
2023-10-18 10:39 ` [pbs-devel] [RFC proxmox 1/3] new proxmox-server-config crate Dominik Csapak
2023-10-25 16:38 ` Thomas Lamprecht
2023-10-30 11:05 ` Dominik Csapak
2023-10-30 11:41 ` Thomas Lamprecht
2023-10-18 10:39 ` [pbs-devel] [RFC proxmox 2/3] new proxmox-jobstate crate Dominik Csapak
2023-10-18 10:39 ` [pbs-devel] [RFC proxmox 3/3] new proxmox-cert-management crate Dominik Csapak
2023-10-18 10:39 ` Dominik Csapak [this message]
2023-10-24 8:17 ` [pbs-devel] [RFC proxmox/proxmox-backup] refactor common server code from pbs Lukas Wagner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231018103911.3798182-5-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox