system-config-api: add functions to read/write time and timezone

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
Dietmar Maurer 2024-05-07 14:04:53 +02:00
parent 751b578d6e
commit 770c5dbd03
4 changed files with 87 additions and 4 deletions

View File

@ -19,9 +19,10 @@ serde_json = { workspace = true }
proxmox-sys = { workspace = true, optional = true }
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] }
proxmox-time = { workspace = true, optional = true }
proxmox-product-config = { workspace = true, optional = true }
[features]
default = [ "proxmox-product-config/default" ]
impl = [ "dep:proxmox-sys", "proxmox-product-config/impl"]
impl = [ "dep:proxmox-sys", "dep:proxmox-time", "proxmox-product-config/impl"]

View File

@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
use proxmox_schema::api;
use proxmox_schema::api_types::IP_FORMAT;
use proxmox_schema::api_types::TIME_ZONE_SCHEMA;
use proxmox_schema::Schema;
use proxmox_schema::StringSchema;
@ -80,7 +81,6 @@ pub struct ResolvConfWithDigest {
pub digest: ConfigDigest,
}
#[api()]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
@ -93,3 +93,28 @@ pub enum DeletableResolvConfProperty {
/// Delete third nameserver entry
Dns3,
}
#[api(
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,
},
}
)]
#[derive(Serialize, Deserialize)]
/// Server time and timezone.
pub struct ServerTimeInfo {
pub timezone: String,
pub time: i64,
pub localtime: i64,
}

View File

@ -1,4 +1,5 @@
mod api_types;
pub use api_types::ServerTimeInfo;
pub use api_types::{DeletableResolvConfProperty, ResolvConf, ResolvConfWithDigest};
pub use api_types::{
FIRST_DNS_SERVER_SCHEMA, SEARCH_DOMAIN_SCHEMA, SECOND_DNS_SERVER_SCHEMA,
@ -6,6 +7,7 @@ pub use api_types::{
};
#[cfg(feature = "impl")]
mod resolv_conf;
pub mod resolv_conf;
#[cfg(feature = "impl")]
pub use resolv_conf::*;
pub mod time;

View File

@ -0,0 +1,55 @@
use anyhow::{bail, format_err, Error};
use proxmox_product_config::replace_system_config;
use proxmox_sys::fs::file_read_firstline;
use crate::api_types::ServerTimeInfo;
pub fn read_etc_localtime() -> Result<String, Error> {
// 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()),
}
}
pub fn set_timezone(timezone: String) -> Result<(), Error> {
let path = std::path::PathBuf::from(format!("/usr/share/zoneinfo/{}", timezone));
if !path.exists() {
bail!("No such timezone.");
}
replace_system_config("/etc/timezone", timezone.as_bytes())?;
let _ = std::fs::remove_file("/etc/localtime");
use std::os::unix::fs::symlink;
symlink(path, "/etc/localtime")?;
Ok(())
}
/// Read server time and time zone settings.
pub fn get_server_time_info() -> Result<ServerTimeInfo, Error> {
let time = proxmox_time::epoch_i64();
let tm = proxmox_time::localtime(time)?;
let offset = tm.tm_gmtoff;
let localtime = time + offset;
Ok(ServerTimeInfo {
timezone: read_etc_localtime()?,
time: time,
localtime: localtime,
})
}