[systemd-devel] [PATCH 2/3] timedated: gather timezone from /etc/localtime sym target

Shawn Landden shawnlandden at gmail.com
Sat Aug 11 15:22:46 PDT 2012


From: Shawn Landen <shawnlandden at gmail.com>

/etc/localtime -> /usr/share/zoneinfo/...

or

/etc/localtime -> ../usr/share/zoneinfo/...

(note, ../usr is not the same if /etc is a symlink, as this isn't
using canonicalize_file_name())

keep other method for now, consider dropping later.

Supporting relative links here are problematic as timezones in
/usr/share/zoneinfo are often themselves symlinks (and symlinks to
symlinks), so this implamentation only supports absolute symlinks
"/usr/share/zoneinfo/" and relative symlinks starting with
"../usr/share/zoneinfo/"

>From TODO (kay sievers):
* kill /etc/timezone handling entirely? What does it provide?
  - /etc/localtime carries the same information already:
      $ ls -l /etc/localtime; cat /etc/timezone
      lrwxrwxrwx 1 root root 33 Jul 27 09:55 /etc/localtime -> /usr/share/zoneinfo/Europe/Berlin
      Europe/Berlin
  - systemd enforces /usr to be available at bootup, so we can
    enforce the use of the symlink
---
 src/timedate/timedated.c |   52 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 42 insertions(+), 10 deletions(-)

diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 09fd808..0f68274 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -74,6 +74,9 @@
         BUS_GENERIC_INTERFACES_LIST             \
         "org.freedesktop.timedate1\0"
 
+/* Must start and end with '/' */
+#define ZONEINFO_PATH "/usr/share/zoneinfo/"
+
 const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
 
 typedef struct TZ {
@@ -152,16 +155,14 @@ static void verify_timezone(void) {
                 return;
 
         p = strappend("/usr/share/zoneinfo/", tz.zone);
-        if (!p) {
-                log_oom();
-                return;
-        }
+        if (!p)
+                return (void)log_oom();
 
-        j = read_full_file("/etc/localtime", &a, &l);
         k = read_full_file(p, &b, &q);
-
         free(p);
 
+        j = read_full_file("/etc/localtime", &a, &l);
+
         if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) {
                 log_warning("/etc/localtime and /etc/timezone out of sync.");
                 free(tz.zone);
@@ -174,9 +175,36 @@ static void verify_timezone(void) {
 
 static int read_data(void) {
         int r;
+        char *t;
+        /* we only support the trivial relative link of (/etc/)..$ABSOLUTE */
+        bool relative_link = false;
 
         free_data();
 
+        r = readlink_malloc("/etc/localtime", &t);
+        if (r < 0) {
+                if (errno == EINVAL)
+                        log_warning("/etc/localtime should be a symbolic link to a timezone data file in %s", ZONEINFO_PATH);
+                else
+                        log_warning("Failed to get target of %s: %m", "/etc/localtime");
+        } else if (!startswith(t, ZONEINFO_PATH) && !(startswith(t, "..") && startswith(t + strlen(".."), ZONEINFO_PATH)))
+                        log_warning("/etc/localtime should be a symbolic link to a timezone data file in %s", ZONEINFO_PATH);
+        else {
+                if (startswith(t, ".."))
+                        relative_link = true;
+
+                tz.zone = strdup(t + (relative_link ? strlen("..") : 0) + strlen(ZONEINFO_PATH));
+                if (!tz.zone) {
+                        free(t);
+                        return log_oom();
+                }
+
+                free(t);
+                goto have_timezone;
+        }
+
+        free(t);
+
         r = read_one_line_file("/etc/timezone", &tz.zone);
         if (r < 0) {
                 if (r != -ENOENT)
@@ -192,6 +220,7 @@ static int read_data(void) {
 #endif
         }
 
+have_timezone:
         if (isempty(tz.zone)) {
                 free(tz.zone);
                 tz.zone = NULL;
@@ -207,6 +236,7 @@ static int read_data(void) {
 static int write_data_timezone(void) {
         int r = 0;
         char *p;
+        struct stat st;
 
         if (!tz.zone) {
                 if (unlink("/etc/timezone") < 0 && errno != ENOENT)
@@ -222,15 +252,17 @@ static int write_data_timezone(void) {
         if (!p)
                 return log_oom();
 
-        r = symlink_or_copy_atomic(p, "/etc/localtime");
+        r = symlink_atomic(p, "/etc/localtime");
         free(p);
 
         if (r < 0)
                 return r;
 
-        r = write_one_line_file_atomic("/etc/timezone", tz.zone);
-        if (r < 0)
-                return r;
+        if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) {
+                r = write_one_line_file_atomic("/etc/timezone", tz.zone);
+                if (r < 0)
+                        return r;
+        }
 
         return 0;
 }
-- 
1.7.9.5



More information about the systemd-devel mailing list