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 7A1286C688 for ; Thu, 23 Sep 2021 12:10:38 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 75A8E23D28 for ; Thu, 23 Sep 2021 12:10:08 +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) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id F1C3D23D12 for ; Thu, 23 Sep 2021 12:10:06 +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 6BCEA44A76; Thu, 23 Sep 2021 12:10:00 +0200 (CEST) From: Dietmar Maurer To: pbs-devel@lists.proxmox.com Date: Thu, 23 Sep 2021 12:09:57 +0200 Message-Id: <20210923100958.948762-2-dietmar@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210923100958.948762-1-dietmar@proxmox.com> References: <20210923100958.948762-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.586 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% 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] [PATCH proxmox 2/3] add UPID api type 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: Thu, 23 Sep 2021 10:10:38 -0000 --- proxmox/src/api/mod.rs | 2 + proxmox/src/api/upid.rs | 149 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 proxmox/src/api/upid.rs diff --git a/proxmox/src/api/mod.rs b/proxmox/src/api/mod.rs index aaec7df..18b5cd2 100644 --- a/proxmox/src/api/mod.rs +++ b/proxmox/src/api/mod.rs @@ -53,3 +53,5 @@ pub use router::{ pub mod cli; pub mod de; + +pub mod upid; diff --git a/proxmox/src/api/upid.rs b/proxmox/src/api/upid.rs new file mode 100644 index 0000000..fd3e9c9 --- /dev/null +++ b/proxmox/src/api/upid.rs @@ -0,0 +1,149 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use anyhow::{bail, Error}; + +use crate::api::schema::{ApiStringFormat, ApiType, Schema, StringSchema}; +use crate::const_regex; +use crate::sys::linux::procfs; + +/// Unique Process/Task Identifier +/// +/// We use this to uniquely identify worker task. UPIDs have a short +/// string repesentaion, which gives additional information about the +/// type of the task. for example: +/// ```text +/// UPID:{node}:{pid}:{pstart}:{task_id}:{starttime}:{worker_type}:{worker_id}:{userid}: +/// UPID:elsa:00004F37:0039E469:00000000:5CA78B83:garbage_collection::root@pam: +/// ``` +/// Please note that we use tokio, so a single thread can run multiple +/// tasks. +// #[api] - manually implemented API type +#[derive(Debug, Clone)] +pub struct UPID { + /// The Unix PID + pub pid: libc::pid_t, + /// The Unix process start time from `/proc/pid/stat` + pub pstart: u64, + /// The task start time (Epoch) + pub starttime: i64, + /// The task ID (inside the process/thread) + pub task_id: usize, + /// Worker type (arbitrary ASCII string) + pub worker_type: String, + /// Worker ID (arbitrary ASCII string) + pub worker_id: Option, + /// The authenticated entity who started the task + pub auth_id: String, + /// The node name. + pub node: String, +} + +crate::forward_serialize_to_display!(UPID); +crate::forward_deserialize_to_from_str!(UPID); + +const_regex! { + pub PROXMOX_UPID_REGEX = concat!( + r"^UPID:(?P[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?):(?P[0-9A-Fa-f]{8}):", + r"(?P[0-9A-Fa-f]{8,9}):(?P[0-9A-Fa-f]{8,16}):(?P[0-9A-Fa-f]{8}):", + r"(?P[^:\s]+):(?P[^:\s]*):(?P[^:\s]+):$" + ); +} + +pub const PROXMOX_UPID_FORMAT: ApiStringFormat = + ApiStringFormat::Pattern(&PROXMOX_UPID_REGEX); + +pub const UPID_SCHEMA: Schema = StringSchema::new("Unique Process/Task Identifier") + .min_length("UPID:N:12345678:12345678:12345678:::".len()) + .max_length(128) // arbitrary + .format(&PROXMOX_UPID_FORMAT) + .schema(); + +impl ApiType for UPID { + const API_SCHEMA: Schema = UPID_SCHEMA; +} + +impl UPID { + /// Create a new UPID + pub fn new( + worker_type: &str, + worker_id: Option, + auth_id: String, + ) -> Result { + + let pid = unsafe { libc::getpid() }; + + let bad: &[_] = &['/', ':', ' ']; + + if worker_type.contains(bad) { + bail!("illegal characters in worker type '{}'", worker_type); + } + + if auth_id.contains(bad) { + bail!("illegal characters in auth_id '{}'", auth_id); + } + + static WORKER_TASK_NEXT_ID: AtomicUsize = AtomicUsize::new(0); + + let task_id = WORKER_TASK_NEXT_ID.fetch_add(1, Ordering::SeqCst); + + Ok(UPID { + pid, + pstart: procfs::PidStat::read_from_pid(nix::unistd::Pid::from_raw(pid))?.starttime, + starttime: crate::tools::time::epoch_i64(), + task_id, + worker_type: worker_type.to_owned(), + worker_id, + auth_id, + node: crate::tools::nodename().to_owned(), + }) + } +} + + +impl std::str::FromStr for UPID { + type Err = Error; + + fn from_str(s: &str) -> Result { + if let Some(cap) = PROXMOX_UPID_REGEX.captures(s) { + + let worker_id = if cap["wid"].is_empty() { + None + } else { + let wid = crate::tools::systemd::unescape_unit(&cap["wid"])?; + Some(wid) + }; + + Ok(UPID { + pid: i32::from_str_radix(&cap["pid"], 16).unwrap(), + pstart: u64::from_str_radix(&cap["pstart"], 16).unwrap(), + starttime: i64::from_str_radix(&cap["starttime"], 16).unwrap(), + task_id: usize::from_str_radix(&cap["task_id"], 16).unwrap(), + worker_type: cap["wtype"].to_string(), + worker_id, + auth_id: cap["authid"].to_string(), + node: cap["node"].to_string(), + }) + } else { + bail!("unable to parse UPID '{}'", s); + } + + } +} + +impl std::fmt::Display for UPID { + + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + + let wid = if let Some(ref id) = self.worker_id { + crate::tools::systemd::escape_unit(id, false) + } else { + String::new() + }; + + // Note: pstart can be > 32bit if uptime > 497 days, so this can result in + // more that 8 characters for pstart + + write!(f, "UPID:{}:{:08X}:{:08X}:{:08X}:{:08X}:{}:{}:{}:", + self.node, self.pid, self.pstart, self.task_id, self.starttime, self.worker_type, wid, self.auth_id) + } +} -- 2.30.2