]>
Commit | Line | Data |
---|---|---|
e7cb4dc5 WB |
1 | use std::sync::atomic::{AtomicUsize, Ordering}; |
2 | ||
f7d4e4b5 | 3 | use anyhow::{bail, Error}; |
e7cb4dc5 | 4 | use chrono::Local; |
634132fe DM |
5 | use lazy_static::lazy_static; |
6 | use regex::Regex; | |
634132fe | 7 | |
619495b2 WB |
8 | use proxmox::sys::linux::procfs; |
9 | ||
e7cb4dc5 WB |
10 | use crate::api2::types::Userid; |
11 | ||
634132fe DM |
12 | /// Unique Process/Task Identifier |
13 | /// | |
14 | /// We use this to uniquely identify worker task. UPIDs have a short | |
15 | /// string repesentaion, which gives additional information about the | |
16 | /// type of the task. for example: | |
17 | /// ```text | |
e7cb4dc5 | 18 | /// UPID:{node}:{pid}:{pstart}:{task_id}:{starttime}:{worker_type}:{worker_id}:{userid}: |
634132fe DM |
19 | /// UPID:elsa:00004F37:0039E469:00000000:5CA78B83:garbage_collection::root@pam: |
20 | /// ``` | |
21 | /// Please note that we use tokio, so a single thread can run multiple | |
22 | /// tasks. | |
23 | #[derive(Debug, Clone)] | |
24 | pub struct UPID { | |
25 | /// The Unix PID | |
26 | pub pid: libc::pid_t, | |
27 | /// The Unix process start time from `/proc/pid/stat` | |
28 | pub pstart: u64, | |
29 | /// The task start time (Epoch) | |
30 | pub starttime: i64, | |
31 | /// The task ID (inside the process/thread) | |
32 | pub task_id: usize, | |
33 | /// Worker type (arbitrary ASCII string) | |
34 | pub worker_type: String, | |
35 | /// Worker ID (arbitrary ASCII string) | |
36 | pub worker_id: Option<String>, | |
37 | /// The user who started the task | |
e7cb4dc5 | 38 | pub userid: Userid, |
634132fe DM |
39 | /// The node name. |
40 | pub node: String, | |
41 | } | |
42 | ||
43 | impl UPID { | |
44 | ||
45 | /// Create a new UPID | |
e7cb4dc5 WB |
46 | pub fn new( |
47 | worker_type: &str, | |
48 | worker_id: Option<String>, | |
49 | userid: Userid, | |
50 | ) -> Result<Self, Error> { | |
634132fe DM |
51 | |
52 | let pid = unsafe { libc::getpid() }; | |
53 | ||
37b87869 DM |
54 | let bad: &[_] = &['/', ':', ' ']; |
55 | ||
56 | if worker_type.contains(bad) { | |
57 | bail!("illegal characters in worker type '{}'", worker_type); | |
58 | } | |
59 | if let Some(ref worker_id) = worker_id { | |
60 | if worker_id.contains(bad) { | |
61 | bail!("illegal characters in worker id '{}'", worker_id); | |
62 | } | |
63 | } | |
64 | ||
65 | static WORKER_TASK_NEXT_ID: AtomicUsize = AtomicUsize::new(0); | |
634132fe DM |
66 | |
67 | let task_id = WORKER_TASK_NEXT_ID.fetch_add(1, Ordering::SeqCst); | |
68 | ||
69 | Ok(UPID { | |
70 | pid, | |
6a0dc4a5 | 71 | pstart: procfs::PidStat::read_from_pid(nix::unistd::Pid::from_raw(pid))?.starttime, |
634132fe DM |
72 | starttime: Local::now().timestamp(), |
73 | task_id, | |
74 | worker_type: worker_type.to_owned(), | |
75 | worker_id, | |
e7cb4dc5 | 76 | userid, |
f69adc81 | 77 | node: proxmox::tools::nodename().to_owned(), |
634132fe DM |
78 | }) |
79 | } | |
80 | ||
81 | /// Returns the absolute path to the task log file | |
82 | pub fn log_path(&self) -> std::path::PathBuf { | |
83 | let mut path = std::path::PathBuf::from(super::PROXMOX_BACKUP_TASK_DIR); | |
84 | path.push(format!("{:02X}", self.pstart % 256)); | |
85 | path.push(self.to_string()); | |
86 | path | |
87 | } | |
88 | } | |
89 | ||
90 | ||
91 | impl std::str::FromStr for UPID { | |
92 | type Err = Error; | |
93 | ||
94 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
95 | ||
96 | lazy_static! { | |
97 | static ref REGEX: Regex = Regex::new(concat!( | |
98 | r"^UPID:(?P<node>[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?):(?P<pid>[0-9A-Fa-f]{8}):", | |
99 | r"(?P<pstart>[0-9A-Fa-f]{8,9}):(?P<task_id>[0-9A-Fa-f]{8,16}):(?P<starttime>[0-9A-Fa-f]{8}):", | |
e7cb4dc5 | 100 | r"(?P<wtype>[^:\s]+):(?P<wid>[^:\s]*):(?P<userid>[^:\s]+):$" |
634132fe DM |
101 | )).unwrap(); |
102 | } | |
103 | ||
104 | if let Some(cap) = REGEX.captures(s) { | |
105 | ||
834a2f95 | 106 | Ok(UPID { |
634132fe DM |
107 | pid: i32::from_str_radix(&cap["pid"], 16).unwrap(), |
108 | pstart: u64::from_str_radix(&cap["pstart"], 16).unwrap(), | |
109 | starttime: i64::from_str_radix(&cap["starttime"], 16).unwrap(), | |
110 | task_id: usize::from_str_radix(&cap["task_id"], 16).unwrap(), | |
111 | worker_type: cap["wtype"].to_string(), | |
112 | worker_id: if cap["wid"].is_empty() { None } else { Some(cap["wid"].to_string()) }, | |
e7cb4dc5 | 113 | userid: cap["userid"].parse()?, |
634132fe | 114 | node: cap["node"].to_string(), |
834a2f95 | 115 | }) |
634132fe DM |
116 | } else { |
117 | bail!("unable to parse UPID '{}'", s); | |
118 | } | |
119 | ||
120 | } | |
121 | } | |
122 | ||
123 | impl std::fmt::Display for UPID { | |
124 | ||
125 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
126 | ||
127 | let wid = if let Some(ref id) = self.worker_id { id } else { "" }; | |
128 | ||
129 | // Note: pstart can be > 32bit if uptime > 497 days, so this can result in | |
130 | // more that 8 characters for pstart | |
131 | ||
132 | write!(f, "UPID:{}:{:08X}:{:08X}:{:08X}:{:08X}:{}:{}:{}:", | |
e7cb4dc5 | 133 | self.node, self.pid, self.pstart, self.task_id, self.starttime, self.worker_type, wid, self.userid) |
634132fe DM |
134 | } |
135 | } |