use chrono::prelude::*; use anyhow::{bail, format_err, Error}; use serde_json::{json, Value}; use proxmox::api::{api, Router, Permission}; use proxmox::tools::fs::{file_read_firstline, replace_file, CreateOptions}; use crate::config::acl::PRIV_SYS_MODIFY; use crate::api2::types::*; fn read_etc_localtime() -> Result { // use /etc/timezone if let Ok(line) = file_read_firstline("/etc/timezone") { return Ok(line.trim().to_owned()); } // otherwise guess from the /etc/localtime symlink let link = std::fs::read_link("/etc/localtime"). map_err(|err| format_err!("failed to guess timezone - {}", err))?; let link = link.to_string_lossy(); match link.rfind("/zoneinfo/") { Some(pos) => Ok(link[(pos + 10)..].to_string()), None => Ok(link.to_string()), } } #[api( input: { properties: { node: { schema: NODE_SCHEMA, }, }, }, returns: { description: "Returns server time and timezone.", properties: { timezone: { schema: TIME_ZONE_SCHEMA, }, time: { type: i64, description: "Seconds since 1970-01-01 00:00:00 UTC.", minimum: 1_297_163_644, }, localtime: { type: i64, description: "Seconds since 1970-01-01 00:00:00 UTC. (local time)", minimum: 1_297_163_644, }, } }, access: { permission: &Permission::Anybody, }, )] /// Read server time and time zone settings. fn get_time(_param: Value) -> Result { let datetime = Local::now(); let offset = datetime.offset(); let time = datetime.timestamp(); let localtime = time + (offset.fix().local_minus_utc() as i64); Ok(json!({ "timezone": read_etc_localtime()?, "time": time, "localtime": localtime, })) } #[api( protected: true, reload_timezone: true, input: { properties: { node: { schema: NODE_SCHEMA, }, timezone: { schema: TIME_ZONE_SCHEMA, }, }, }, access: { permission: &Permission::Privilege(&["system", "time"], PRIV_SYS_MODIFY, false), }, )] /// Set time zone fn set_timezone( timezone: String, _param: Value, ) -> Result { let path = std::path::PathBuf::from(format!("/usr/share/zoneinfo/{}", timezone)); if !path.exists() { bail!("No such timezone."); } replace_file("/etc/timezone", timezone.as_bytes(), CreateOptions::new())?; let _ = std::fs::remove_file("/etc/localtime"); use std::os::unix::fs::symlink; symlink(path, "/etc/localtime")?; Ok(Value::Null) } pub const ROUTER: Router = Router::new() .get(&API_METHOD_GET_TIME) .put(&API_METHOD_SET_TIMEZONE);