all lists on lists.proxmox.com
 help / color / mirror / Atom feed
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





  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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal