]>
Commit | Line | Data |
---|---|---|
b2b3485d | 1 | use failure::*; |
e18a6c9e | 2 | use proxmox::tools::fs::{file_read_firstline, file_set_contents}; |
b2b3485d | 3 | |
ef2f2efb | 4 | use crate::api_schema::*; |
dc9a007b | 5 | use crate::api_schema::router::*; |
4ebf0eab DM |
6 | use crate::api2::types::*; |
7 | ||
b2b3485d DM |
8 | use serde_json::{json, Value}; |
9 | ||
0463602a DM |
10 | use chrono::prelude::*; |
11 | ||
12 | fn read_etc_localtime() -> Result<String, Error> { | |
13f8310c | 13 | // use /etc/timezone |
e18a6c9e | 14 | if let Ok(line) = file_read_firstline("/etc/timezone") { |
13f8310c WB |
15 | return Ok(line.trim().to_owned()); |
16 | } | |
0463602a | 17 | |
13f8310c WB |
18 | // otherwise guess from the /etc/localtime symlink |
19 | let mut buf: [u8; 64] = unsafe { std::mem::uninitialized() }; | |
20 | let len = unsafe { | |
21 | libc::readlink("/etc/localtime".as_ptr() as *const _, buf.as_mut_ptr() as *mut _, buf.len()) | |
22 | }; | |
23 | if len <= 0 { | |
24 | bail!("failed to guess timezone"); | |
25 | } | |
26 | let len = len as usize; | |
27 | buf[len] = 0; | |
28 | let link = std::str::from_utf8(&buf[..len])?; | |
29 | match link.rfind("/zoneinfo/") { | |
30 | Some(pos) => Ok(link[(pos + 10)..].to_string()), | |
31 | None => Ok(link.to_string()), | |
32 | } | |
0463602a | 33 | } |
b2b3485d | 34 | |
6049b71f DM |
35 | fn get_time( |
36 | _param: Value, | |
37 | _info: &ApiMethod, | |
dd5495d6 | 38 | _rpcenv: &mut dyn RpcEnvironment, |
6049b71f | 39 | ) -> Result<Value, Error> { |
b2b3485d | 40 | |
0463602a DM |
41 | let datetime = Local::now(); |
42 | let offset = datetime.offset(); | |
43 | let time = datetime.timestamp(); | |
44 | let localtime = time + (offset.fix().local_minus_utc() as i64); | |
45 | ||
b2b3485d | 46 | Ok(json!({ |
0463602a DM |
47 | "timezone": read_etc_localtime()?, |
48 | "time": time, | |
49 | "localtime": localtime, | |
b2b3485d DM |
50 | })) |
51 | } | |
52 | ||
6049b71f DM |
53 | fn set_timezone( |
54 | param: Value, | |
55 | _info: &ApiMethod, | |
dd5495d6 | 56 | _rpcenv: &mut dyn RpcEnvironment, |
6049b71f | 57 | ) -> Result<Value, Error> { |
e6ffeb91 | 58 | |
e18a6c9e | 59 | let timezone = crate::tools::required_string_param(¶m, "timezone")?; |
e6ffeb91 DM |
60 | |
61 | let path = std::path::PathBuf::from(format!("/usr/share/zoneinfo/{}", timezone)); | |
62 | ||
63 | if !path.exists() { | |
64 | bail!("No such timezone."); | |
65 | } | |
66 | ||
e18a6c9e | 67 | file_set_contents("/etc/timezone", timezone.as_bytes(), None)?; |
e6ffeb91 DM |
68 | |
69 | let _ = std::fs::remove_file("/etc/localtime"); | |
70 | ||
71 | use std::os::unix::fs::symlink; | |
72 | symlink(path, "/etc/localtime")?; | |
73 | ||
e6ffeb91 DM |
74 | Ok(Value::Null) |
75 | } | |
76 | ||
b2b3485d DM |
77 | pub fn router() -> Router { |
78 | ||
79 | let route = Router::new() | |
cd8b91f7 DM |
80 | .get( |
81 | ApiMethod::new( | |
e6ffeb91 DM |
82 | get_time, |
83 | ObjectSchema::new("Read server time and time zone settings.") | |
4ebf0eab | 84 | .required("node", NODE_SCHEMA.clone()) |
d8d40dd0 | 85 | ).returns( |
e6ffeb91 DM |
86 | ObjectSchema::new("Returns server time and timezone.") |
87 | .required("timezone", StringSchema::new("Time zone")) | |
88 | .required("time", IntegerSchema::new("Seconds since 1970-01-01 00:00:00 UTC.") | |
89 | .minimum(1297163644)) | |
90 | .required("localtime", IntegerSchema::new("Seconds since 1970-01-01 00:00:00 UTC. (local time)") | |
91 | .minimum(1297163644)) | |
92 | ) | |
93 | ) | |
94 | .put( | |
95 | ApiMethod::new( | |
96 | set_timezone, | |
97 | ObjectSchema::new("Set time zone.") | |
4ebf0eab | 98 | .required("node", NODE_SCHEMA.clone()) |
d8d40dd0 DM |
99 | .required("timezone", StringSchema::new( |
100 | "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")) | |
4b2cdeb9 | 101 | ).protected(true).reload_timezone(true) |
cd8b91f7 DM |
102 | ); |
103 | ||
b2b3485d DM |
104 | |
105 | route | |
106 | } |