2 use std
::io
::{BufRead, BufReader}
;
5 use serde_json
::{json, Value}
;
7 use proxmox
::api
::{api, Router, RpcEnvironment, Permission}
;
8 use proxmox
::api
::router
::SubdirMap
;
9 use proxmox
::{identity, list_subdirs_api_method, sortable}
;
12 use crate::api2
::types
::*;
13 use crate::server
::{self, UPID, TaskState, TaskListInfoIterator}
;
14 use crate::config
::acl
::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}
;
15 use crate::config
::cached_user_info
::CachedUserInfo
;
30 description
: "Task status nformation.",
40 description
: "The Unix PID.",
44 description
: "The Unix process start time from `/proc/pid/stat`",
48 description
: "The task start time (Epoch)",
52 description
: "Worker type (arbitrary ASCII string)",
57 description
: "Worker ID (arbitrary ASCII string)",
61 description
: "The user who started the task.",
65 description
: "'running' or 'stopped'",
70 description
: "'OK', 'Error: <msg>', or 'unkwown'.",
75 description
: "Users can access there own tasks, or need Sys.Audit on /system/tasks.",
76 permission
: &Permission
::Anybody
,
80 async
fn get_task_status(
82 rpcenv
: &mut dyn RpcEnvironment
,
83 ) -> Result
<Value
, Error
> {
85 let upid
= extract_upid(¶m
)?
;
87 let userid
: Userid
= rpcenv
.get_user().unwrap().parse()?
;
89 if userid
!= upid
.userid
{
90 let user_info
= CachedUserInfo
::new()?
;
91 user_info
.check_privs(&userid
, &["system", "tasks"], PRIV_SYS_AUDIT
, false)?
;
94 let mut result
= json
!({
95 "upid": param
["upid"],
98 "pstart": upid
.pstart
,
99 "starttime": upid
.starttime
,
100 "type": upid
.worker_type
,
101 "id": upid
.worker_id
,
105 if crate::server
::worker_is_active(&upid
).await?
{
106 result
["status"] = Value
::from("running");
108 let exitstatus
= crate::server
::upid_read_status(&upid
).unwrap_or(TaskState
::Unknown { endtime: 0 }
);
109 result
["status"] = Value
::from("stopped");
110 result
["exitstatus"] = Value
::from(exitstatus
.to_string());
116 fn extract_upid(param
: &Value
) -> Result
<UPID
, Error
> {
118 let upid_str
= tools
::required_string_param(¶m
, "upid")?
;
120 upid_str
.parse
::<UPID
>()
135 description
: "Test task status, and set result attribute \"active\" accordingly.",
140 description
: "Start at this line.",
146 description
: "Only list this amount of lines.",
152 description
: "Users can access there own tasks, or need Sys.Audit on /system/tasks.",
153 permission
: &Permission
::Anybody
,
157 async
fn read_task_log(
159 mut rpcenv
: &mut dyn RpcEnvironment
,
160 ) -> Result
<Value
, Error
> {
162 let upid
= extract_upid(¶m
)?
;
164 let userid
: Userid
= rpcenv
.get_user().unwrap().parse()?
;
166 if userid
!= upid
.userid
{
167 let user_info
= CachedUserInfo
::new()?
;
168 user_info
.check_privs(&userid
, &["system", "tasks"], PRIV_SYS_AUDIT
, false)?
;
171 let test_status
= param
["test-status"].as_bool().unwrap_or(false);
173 let start
= param
["start"].as_u64().unwrap_or(0);
174 let mut limit
= param
["limit"].as_u64().unwrap_or(50);
176 let mut count
: u64 = 0;
178 let path
= upid
.log_path();
180 let file
= File
::open(path
)?
;
182 let mut lines
: Vec
<Value
> = vec
![];
184 for line
in BufReader
::new(file
).lines() {
188 if count
< start { continue }
;
189 if limit
== 0 { continue }
;
191 lines
.push(json
!({ "n": count, "t": line }
));
196 log
::error
!("reading task log failed: {}", err
);
202 rpcenv
["total"] = Value
::from(count
);
205 let active
= crate::server
::worker_is_active(&upid
).await?
;
206 rpcenv
["active"] = Value
::from(active
);
225 description
: "Users can stop there own tasks, or need Sys.Modify on /system/tasks.",
226 permission
: &Permission
::Anybody
,
229 /// Try to stop a task.
232 rpcenv
: &mut dyn RpcEnvironment
,
233 ) -> Result
<Value
, Error
> {
235 let upid
= extract_upid(¶m
)?
;
237 let userid
: Userid
= rpcenv
.get_user().unwrap().parse()?
;
239 if userid
!= upid
.userid
{
240 let user_info
= CachedUserInfo
::new()?
;
241 user_info
.check_privs(&userid
, &["system", "tasks"], PRIV_SYS_MODIFY
, false)?
;
244 server
::abort_worker_async(upid
);
257 description
: "List tasks beginning from this offset.",
263 description
: "Only list this amount of tasks.",
268 schema
: DATASTORE_SCHEMA
,
273 description
: "Only list running tasks.",
279 description
: "Only list erroneous tasks.",
286 description
: "Only list tasks from this user.",
291 description
: "A list of tasks.",
293 items
: { type: TaskListItem }
,
296 description
: "Users can only see there own tasks, unless the have Sys.Audit on /system/tasks.",
297 permission
: &Permission
::Anybody
,
306 userfilter
: Option
<String
>,
308 mut rpcenv
: &mut dyn RpcEnvironment
,
309 ) -> Result
<Vec
<TaskListItem
>, Error
> {
311 let userid
: Userid
= rpcenv
.get_user().unwrap().parse()?
;
312 let user_info
= CachedUserInfo
::new()?
;
313 let user_privs
= user_info
.lookup_privs(&userid
, &["system", "tasks"]);
315 let list_all
= (user_privs
& PRIV_SYS_AUDIT
) != 0;
317 let store
= param
["store"].as_str();
319 let list
= TaskListInfoIterator
::new(running
)?
;
321 let result
: Vec
<TaskListItem
> = list
322 .take_while(|info
| !info
.is_err())
324 let info
= match info
{
326 Err(_
) => return None
,
329 if !list_all
&& info
.upid
.userid
!= userid { return None; }
331 if let Some(userid
) = &userfilter
{
332 if !info
.upid
.userid
.as_str().contains(userid
) { return None; }
335 if let Some(store
) = store
{
336 // Note: useful to select all tasks spawned by proxmox-backup-client
337 let worker_id
= match &info
.upid
.worker_id
{
339 None
=> return None
, // skip
342 if info
.upid
.worker_type
== "backup" || info
.upid
.worker_type
== "restore" ||
343 info
.upid
.worker_type
== "prune"
345 let prefix
= format
!("{}_", store
);
346 if !worker_id
.starts_with(&prefix
) { return None; }
347 } else if info
.upid
.worker_type
== "garbage_collection" {
348 if worker_id
!= store { return None; }
355 Some(_
) if running
=> return None
,
356 Some(crate::server
::TaskState
::OK { .. }
) if errors
=> return None
,
361 }).skip(start
as usize)
362 .take(limit
as usize)
365 let mut count
= result
.len() + start
as usize;
366 if result
.len() > 0 && result
.len() >= limit
as usize { // we have a 'virtual' entry as long as we have any new
370 rpcenv
["total"] = Value
::from(count
);
376 const UPID_API_SUBDIRS
: SubdirMap
= &sorted
!([
378 "log", &Router
::new()
379 .get(&API_METHOD_READ_TASK_LOG
)
382 "status", &Router
::new()
383 .get(&API_METHOD_GET_TASK_STATUS
)
387 pub const UPID_API_ROUTER
: Router
= Router
::new()
388 .get(&list_subdirs_api_method
!(UPID_API_SUBDIRS
))
389 .delete(&API_METHOD_STOP_TASK
)
390 .subdirs(&UPID_API_SUBDIRS
);
392 pub const ROUTER
: Router
= Router
::new()
393 .get(&API_METHOD_LIST_TASKS
)
394 .match_all("upid", &UPID_API_ROUTER
);