all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Maximiliano Sandoval <m.sandoval@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [PATCH backup 1/2] fix #7175: api: time: use timedatectl instead of /etc/timezone
Date: Thu, 18 Jun 2026 09:49:00 +0200	[thread overview]
Message-ID: <20260618074926.1464533-1-m.sandoval@proxmox.com> (raw)

The Proxmox Backup Server 4.2 iso does not create /etc/timezone, so we
use timedatectl as recommended at [1].

As per timedatectl(1) set-timezone will alter the /etc/localtime
symlink, and we return early on set_timezone() if the command succeeds.

[1] https://salsa.debian.org/glibc-team/tzdata/-/blob/trixie/debian/README.Debian?ref_type=heads#L54

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
---

Tested:
    - proxmox-backup-debug api get /nodes/{node}/time
    - proxmox-backup-debug api set /nodes/{node}/time --timezone $tz
    - Checked that /etc/timezone is not created
    - Captured dbus output via busctl capture > output.pcapng and checked
      that the org.freedesktop.timedate1 interface (used by timedatectl) is
      being used for {g,s}etting the timezone.
    - Verified that the symlink at /etc/localtime points to the right place.
    
Note that at the moment both getting and setting the timezone work as
expected without this patch, in practice the only difference is that
/etc/timezone will not be created anymore after setting the timezone.

 src/api2/node/time.rs | 74 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 63 insertions(+), 11 deletions(-)

diff --git a/src/api2/node/time.rs b/src/api2/node/time.rs
index 6baca378e..9ab4e84d8 100644
--- a/src/api2/node/time.rs
+++ b/src/api2/node/time.rs
@@ -1,16 +1,16 @@
+use std::process::Command;
+
 use anyhow::{Error, bail, format_err};
 use serde_json::{Value, json};
 
 use proxmox_router::{Permission, Router};
 use proxmox_schema::api;
-use proxmox_sys::fs::{CreateOptions, file_read_firstline, replace_file};
 
 use pbs_api_types::{NODE_SCHEMA, PRIV_SYS_MODIFY, TIME_ZONE_SCHEMA};
 
 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());
+    if let Ok(timezone) = timedatectl_get_timezone() {
+        return Ok(timezone);
     }
 
     // otherwise guess from the /etc/localtime symlink
@@ -24,6 +24,61 @@ fn read_etc_localtime() -> Result<String, Error> {
     }
 }
 
+fn timedatectl_get_timezone() -> Result<String, Error> {
+    let output = Command::new("timedatectl")
+        .args(["show", "--property=Timezone", "--value"])
+        .output()
+        .map_err(|err| format_err!("failed to execute timedatectl show - {err}"))?;
+
+    if !output.status.success() {
+        if let Some(code) = output.status.code() {
+            let msg = String::from_utf8(output.stderr)
+                .map(|s| {
+                    if s.is_empty() {
+                        String::from("no error message")
+                    } else {
+                        s
+                    }
+                })
+                .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
+            bail!("timedatectl show failed with status code {code} - {msg}",);
+        } else {
+            bail!("timedatectl terminated by signal",);
+        }
+    }
+
+    let timezone = String::from_utf8(output.stdout)
+        .map_err(|err| format_err!("non utf8 timezone from timedatectl - {err}"))?;
+
+    Ok(timezone)
+}
+
+fn timedatectl_set_timezone(timezone: &str) -> Result<(), Error> {
+    let output = Command::new("timedatectl")
+        .args(["set-timezone", timezone])
+        .output()
+        .map_err(|err| format_err!("failed to execute timedatectl set-timezone - {err}"))?;
+
+    if !output.status.success() {
+        if let Some(code) = output.status.code() {
+            let msg = String::from_utf8(output.stderr)
+                .map(|s| {
+                    if s.is_empty() {
+                        String::from("no error message")
+                    } else {
+                        s
+                    }
+                })
+                .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
+            bail!("timedatectl set-timezone failed with status code {code} - {msg}",);
+        } else {
+            bail!("timedatectl terminated by signal",);
+        }
+    }
+
+    Ok(())
+}
+
 #[api(
     input: {
         properties: {
@@ -88,19 +143,16 @@ fn get_time(_param: Value) -> Result<Value, Error> {
 )]
 /// Set time zone
 fn set_timezone(timezone: String, _param: Value) -> Result<Value, Error> {
+    if timedatectl_set_timezone(&timezone).is_ok() {
+        return Ok(Value::Null);
+    }
+
     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(),
-        true,
-    )?;
-
     let _ = std::fs::remove_file("/etc/localtime");
 
     use std::os::unix::fs::symlink;
-- 
2.47.3





             reply	other threads:[~2026-06-18  7:49 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-18  7:49 Maximiliano Sandoval [this message]
2026-06-18  7:49 ` [PATCH backup 2/2] api: time: rename read_etc_localtime Maximiliano Sandoval
2026-06-18  7:51 ` [PATCH backup 1/2] fix #7175: api: time: use timedatectl instead of /etc/timezone Maximiliano Sandoval

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260618074926.1464533-1-m.sandoval@proxmox.com \
    --to=m.sandoval@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal