From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 5CC7C9B6D5 for ; Wed, 18 Oct 2023 12:39:47 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 37016D486 for ; Wed, 18 Oct 2023 12:39:17 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Wed, 18 Oct 2023 12:39:14 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B67BA42A45 for ; Wed, 18 Oct 2023 12:39:14 +0200 (CEST) From: Dominik Csapak To: pbs-devel@lists.proxmox.com Date: Wed, 18 Oct 2023 12:39:11 +0200 Message-Id: <20231018103911.3798182-5-d.csapak@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20231018103911.3798182-1-d.csapak@proxmox.com> References: <20231018103911.3798182-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.012 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [RFC proxmox-backup 1/1] use proxmox_jobstate and proxmox_cert_management crates X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 18 Oct 2023 10:39:47 -0000 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 --- 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, - #[serde(skip_serializing_if = "Option::is_none")] - pub last_run_state: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub last_run_upid: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub last_run_endtime: Option, -} - #[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 { - 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 = file_get_contents(configdir!("/csrf.key")).unwrap(); - } - - &SECRET -} - -fn load_public_auth_key() -> Result, 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 { - lazy_static! { - static ref KEY: PKey = load_public_auth_key().unwrap(); - } - - &KEY -} - -fn load_private_auth_key() -> Result, 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 { - lazy_static! { - static ref KEY: PKey = 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, - }, -} - -/// 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

(path: P) -> Result -where - P: AsRef, -{ - 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 { - 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 { - 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 { - 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 { - 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::() { - // 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