1 use std
::sync
::atomic
::{AtomicUsize, Ordering}
;
3 use anyhow
::{bail, Error}
;
4 use serde
::{Deserialize, Serialize}
;
7 use proxmox
::api
::schema
::{ApiStringFormat, ApiType, Schema, StringSchema, ArraySchema, ReturnType}
;
8 use proxmox
::const_regex
;
9 use proxmox
::sys
::linux
::procfs
;
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 authenticated entity 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<authid>[^:\s]+):$"
54 pub const PROXMOX_UPID_FORMAT
: ApiStringFormat
=
55 ApiStringFormat
::Pattern(&PROXMOX_UPID_REGEX
);
57 pub const UPID_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task Identifier")
58 .min_length("UPID:N:12345678:12345678:12345678:::".len())
59 .max_length(128) // arbitrary
60 .format(&PROXMOX_UPID_FORMAT
)
63 impl ApiType
for UPID
{
64 const API_SCHEMA
: Schema
= UPID_SCHEMA
;
71 worker_id
: Option
<String
>,
73 ) -> Result
<Self, Error
> {
75 let pid
= unsafe { libc::getpid() }
;
77 let bad
: &[_
] = &['
/'
, '
:'
, ' '
];
79 if worker_type
.contains(bad
) {
80 bail
!("illegal characters in worker type '{}'", worker_type
);
83 if auth_id
.contains(bad
) {
84 bail
!("illegal characters in auth_id '{}'", auth_id
);
87 static WORKER_TASK_NEXT_ID
: AtomicUsize
= AtomicUsize
::new(0);
89 let task_id
= WORKER_TASK_NEXT_ID
.fetch_add(1, Ordering
::SeqCst
);
93 pstart
: procfs
::PidStat
::read_from_pid(nix
::unistd
::Pid
::from_raw(pid
))?
.starttime
,
94 starttime
: proxmox
::tools
::time
::epoch_i64(),
96 worker_type
: worker_type
.to_owned(),
99 node
: proxmox
::tools
::nodename().to_owned(),
105 impl std
::str::FromStr
for UPID
{
108 fn from_str(s
: &str) -> Result
<Self, Self::Err
> {
109 if let Some(cap
) = PROXMOX_UPID_REGEX
.captures(s
) {
111 let worker_id
= if cap
["wid"].is_empty() {
114 let wid
= proxmox_systemd
::unescape_unit(&cap
["wid"])?
;
119 pid
: i32::from_str_radix(&cap
["pid"], 16).unwrap(),
120 pstart
: u64::from_str_radix(&cap
["pstart"], 16).unwrap(),
121 starttime
: i64::from_str_radix(&cap
["starttime"], 16).unwrap(),
122 task_id
: usize::from_str_radix(&cap
["task_id"], 16).unwrap(),
123 worker_type
: cap
["wtype"].to_string(),
125 auth_id
: cap
["authid"].parse()?
,
126 node
: cap
["node"].to_string(),
129 bail
!("unable to parse UPID '{}'", s
);
135 impl std
::fmt
::Display
for UPID
{
137 fn fmt(&self, f
: &mut std
::fmt
::Formatter
) -> std
::fmt
::Result
{
139 let wid
= if let Some(ref id
) = self.worker_id
{
140 proxmox_systemd
::escape_unit(id
, false)
145 // Note: pstart can be > 32bit if uptime > 497 days, so this can result in
146 // more that 8 characters for pstart
148 write
!(f
, "UPID:{}:{:08X}:{:08X}:{:08X}:{:08X}:{}:{}:{}:",
149 self.node
, self.pid
, self.pstart
, self.task_id
, self.starttime
, self.worker_type
, wid
, self.auth_id
)
154 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
155 #[serde(rename_all = "lowercase")]
156 pub enum TaskStateType
{
169 upid
: { schema: UPID::API_SCHEMA }
,
172 #[derive(Serialize, Deserialize)]
174 pub struct TaskListItem
{
176 /// The node name where the task is running on.
180 /// The task start time (Epoch)
182 /// The task start time (Epoch)
184 /// Worker type (arbitrary ASCII string)
185 pub worker_type
: String
,
186 /// Worker ID (arbitrary ASCII string)
187 pub worker_id
: Option
<String
>,
188 /// The authenticated entity who started the task
190 /// The task end time (Epoch)
191 #[serde(skip_serializing_if="Option::is_none")]
192 pub endtime
: Option
<i64>,
194 #[serde(skip_serializing_if="Option::is_none")]
195 pub status
: Option
<String
>,
198 pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE
: ReturnType
= ReturnType
{
200 schema
: &ArraySchema
::new(
202 &TaskListItem
::API_SCHEMA
,