1 use std
::sync
::atomic
::{AtomicUsize, Ordering}
;
3 use anyhow
::{bail, Error}
;
5 use proxmox
::api
::schema
::{ApiStringFormat, Schema, StringSchema}
;
6 use proxmox
::const_regex
;
7 use proxmox
::sys
::linux
::procfs
;
9 use crate::api2
::types
::Userid
;
11 /// Unique Process/Task Identifier
13 /// We use this to uniquely identify worker task. UPIDs have a short
14 /// string repesentaion, which gives additional information about the
15 /// type of the task. for example:
17 /// UPID:{node}:{pid}:{pstart}:{task_id}:{starttime}:{worker_type}:{worker_id}:{userid}:
18 /// UPID:elsa:00004F37:0039E469:00000000:5CA78B83:garbage_collection::root@pam:
20 /// Please note that we use tokio, so a single thread can run multiple
22 // #[api] - manually implemented API type
23 #[derive(Debug, Clone)]
27 /// The Unix process start time from `/proc/pid/stat`
29 /// The task start time (Epoch)
31 /// The task ID (inside the process/thread)
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
43 proxmox
::forward_serialize_to_display
!(UPID
);
44 proxmox
::forward_deserialize_to_from_str
!(UPID
);
47 pub PROXMOX_UPID_REGEX
= concat
!(
48 r
"^UPID:(?P<node>[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?):(?P<pid>[0-9A-Fa-f]{8}):",
49 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}):",
50 r
"(?P<wtype>[^:\s]+):(?P<wid>[^:\s]*):(?P<userid>[^:\s]+):$"
54 pub const PROXMOX_UPID_FORMAT
: ApiStringFormat
=
55 ApiStringFormat
::Pattern(&PROXMOX_UPID_REGEX
);
58 pub const API_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task Identifier")
59 .min_length("UPID:N:12345678:12345678:12345678:::".len())
60 .max_length(128) // arbitrary
61 .format(&PROXMOX_UPID_FORMAT
)
67 worker_id
: Option
<String
>,
69 ) -> Result
<Self, Error
> {
71 let pid
= unsafe { libc::getpid() }
;
73 let bad
: &[_
] = &['
/'
, '
:'
, ' '
];
75 if worker_type
.contains(bad
) {
76 bail
!("illegal characters in worker type '{}'", worker_type
);
78 if let Some(ref worker_id
) = worker_id
{
79 if worker_id
.contains(bad
) {
80 bail
!("illegal characters in worker id '{}'", worker_id
);
84 static WORKER_TASK_NEXT_ID
: AtomicUsize
= AtomicUsize
::new(0);
86 let task_id
= WORKER_TASK_NEXT_ID
.fetch_add(1, Ordering
::SeqCst
);
90 pstart
: procfs
::PidStat
::read_from_pid(nix
::unistd
::Pid
::from_raw(pid
))?
.starttime
,
91 starttime
: proxmox
::tools
::time
::epoch_i64(),
93 worker_type
: worker_type
.to_owned(),
96 node
: proxmox
::tools
::nodename().to_owned(),
100 /// Returns the absolute path to the task log file
101 pub fn log_path(&self) -> std
::path
::PathBuf
{
102 let mut path
= std
::path
::PathBuf
::from(super::PROXMOX_BACKUP_TASK_DIR
);
103 path
.push(format
!("{:02X}", self.pstart
% 256));
104 path
.push(self.to_string());
110 impl std
::str::FromStr
for UPID
{
113 fn from_str(s
: &str) -> Result
<Self, Self::Err
> {
114 if let Some(cap
) = PROXMOX_UPID_REGEX
.captures(s
) {
116 pid
: i32::from_str_radix(&cap
["pid"], 16).unwrap(),
117 pstart
: u64::from_str_radix(&cap
["pstart"], 16).unwrap(),
118 starttime
: i64::from_str_radix(&cap
["starttime"], 16).unwrap(),
119 task_id
: usize::from_str_radix(&cap
["task_id"], 16).unwrap(),
120 worker_type
: cap
["wtype"].to_string(),
121 worker_id
: if cap
["wid"].is_empty() { None }
else { Some(cap["wid"].to_string()) }
,
122 userid
: cap
["userid"].parse()?
,
123 node
: cap
["node"].to_string(),
126 bail
!("unable to parse UPID '{}'", s
);
132 impl std
::fmt
::Display
for UPID
{
134 fn fmt(&self, f
: &mut std
::fmt
::Formatter
) -> std
::fmt
::Result
{
136 let wid
= if let Some(ref id
) = self.worker_id { id }
else { "" }
;
138 // Note: pstart can be > 32bit if uptime > 497 days, so this can result in
139 // more that 8 characters for pstart
141 write
!(f
, "UPID:{}:{:08X}:{:08X}:{:08X}:{:08X}:{}:{}:{}:",
142 self.node
, self.pid
, self.pstart
, self.task_id
, self.starttime
, self.worker_type
, wid
, self.userid
)