]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/time.rs
a87e8a0e908874e6eb604c920a8797473fb4b740
[proxmox-backup.git] / src / api2 / node / time.rs
1 use std::mem::{self, MaybeUninit};
2
3 use chrono::prelude::*;
4 use failure::*;
5 use serde_json::{json, Value};
6
7 use proxmox::api::{api, Router, Permission};
8 use proxmox::tools::fs::{file_read_firstline, replace_file, CreateOptions};
9
10 use crate::api2::types::*;
11
12 fn read_etc_localtime() -> Result<String, Error> {
13 // use /etc/timezone
14 if let Ok(line) = file_read_firstline("/etc/timezone") {
15 return Ok(line.trim().to_owned());
16 }
17
18 // otherwise guess from the /etc/localtime symlink
19 let mut buf = MaybeUninit::<[u8; 64]>::uninit();
20 let len = unsafe {
21 libc::readlink(
22 "/etc/localtime".as_ptr() as *const _,
23 buf.as_mut_ptr() as *mut _,
24 mem::size_of_val(&buf),
25 )
26 };
27 if len <= 0 {
28 bail!("failed to guess timezone");
29 }
30 let len = len as usize;
31 let buf = unsafe {
32 (*buf.as_mut_ptr())[len] = 0;
33 buf.assume_init()
34 };
35 let link = std::str::from_utf8(&buf[..len])?;
36 match link.rfind("/zoneinfo/") {
37 Some(pos) => Ok(link[(pos + 10)..].to_string()),
38 None => Ok(link.to_string()),
39 }
40 }
41
42 #[api(
43 input: {
44 properties: {
45 node: {
46 schema: NODE_SCHEMA,
47 },
48 },
49 },
50 returns: {
51 description: "Returns server time and timezone.",
52 properties: {
53 timezone: {
54 schema: TIME_ZONE_SCHEMA,
55 },
56 time: {
57 type: i64,
58 description: "Seconds since 1970-01-01 00:00:00 UTC.",
59 minimum: 1_297_163_644,
60 },
61 localtime: {
62 type: i64,
63 description: "Seconds since 1970-01-01 00:00:00 UTC. (local time)",
64 minimum: 1_297_163_644,
65 },
66 }
67 },
68 access: {
69 permission: &Permission::Anybody,
70 },
71 )]
72 /// Read server time and time zone settings.
73 fn get_time(_param: Value) -> Result<Value, Error> {
74 let datetime = Local::now();
75 let offset = datetime.offset();
76 let time = datetime.timestamp();
77 let localtime = time + (offset.fix().local_minus_utc() as i64);
78
79 Ok(json!({
80 "timezone": read_etc_localtime()?,
81 "time": time,
82 "localtime": localtime,
83 }))
84 }
85
86 #[api(
87 protected: true,
88 reload_timezone: true,
89 input: {
90 properties: {
91 node: {
92 schema: NODE_SCHEMA,
93 },
94 timezone: {
95 schema: TIME_ZONE_SCHEMA,
96 },
97 },
98 },
99 )]
100 /// Set time zone
101 fn set_timezone(
102 timezone: String,
103 _param: Value,
104 ) -> Result<Value, Error> {
105 let path = std::path::PathBuf::from(format!("/usr/share/zoneinfo/{}", timezone));
106
107 if !path.exists() {
108 bail!("No such timezone.");
109 }
110
111 replace_file("/etc/timezone", timezone.as_bytes(), CreateOptions::new())?;
112
113 let _ = std::fs::remove_file("/etc/localtime");
114
115 use std::os::unix::fs::symlink;
116 symlink(path, "/etc/localtime")?;
117
118 Ok(Value::Null)
119 }
120
121 pub const ROUTER: Router = Router::new()
122 .get(&API_METHOD_GET_TIME)
123 .put(&API_METHOD_SET_TIMEZONE);