chiark / gitweb /
timedated: gather timezone from /etc/localtime sym target
[elogind.git] / src / timedate / timedated.c
index 3fef9e8844bc09225b3dab162c597d71004737d3..9ca2eec5a8aa18ea3ae7ad0443c8c5721a3f14ff 100644 (file)
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
 #include "util.h"
 #include "strv.h"
 #include "dbus-common.h"
@@ -74,6 +76,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 {
@@ -127,7 +132,7 @@ static bool valid_timezone(const char *name) {
         if (slash)
                 return false;
 
-        t = strappend("/usr/share/zoneinfo/", name);
+        t = strappend(ZONEINFO_PATH, name);
         if (!t)
                 return false;
 
@@ -151,17 +156,17 @@ static void verify_timezone(void) {
         if (!tz.zone)
                 return;
 
-        p = strappend("/usr/share/zoneinfo/", tz.zone);
+        p = strappend(ZONEINFO_PATH, tz.zone);
         if (!p) {
-                log_error("Out of memory.");
+                log_oom();
                 return;
         }
 
-        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 +179,34 @@ static void verify_timezone(void) {
 
 static int read_data(void) {
         int r;
+        char *t = NULL;
 
         free_data();
 
+        r = readlink_malloc("/etc/localtime", &t);
+        if (r < 0) {
+                if (r == -EINVAL)
+                        log_warning("/etc/localtime should be a symbolic link to a timezone data file in " ZONEINFO_PATH);
+                else
+                        log_warning("Failed to get target of %s: %s", "/etc/localtime", strerror(-r));
+        } else {
+                /* we only support the trivial relative link of (/etc/)..$ABSOLUTE */
+                int rel_link_offset = startswith(t, "..") ? strlen("..") : 0;
+
+                if (!startswith(t + rel_link_offset, ZONEINFO_PATH))
+                        log_warning("/etc/localtime should be a symbolic link to a timezone data file in " ZONEINFO_PATH);
+                else {
+                        tz.zone = strdup(t + rel_link_offset + strlen(ZONEINFO_PATH));
+                        free(t);
+                        if (!tz.zone)
+                                return log_oom();
+
+                        goto have_timezone;
+                }
+        }
+
+        free(t);
+
         r = read_one_line_file("/etc/timezone", &tz.zone);
         if (r < 0) {
                 if (r != -ENOENT)
@@ -192,6 +222,7 @@ static int read_data(void) {
 #endif
         }
 
+have_timezone:
         if (isempty(tz.zone)) {
                 free(tz.zone);
                 tz.zone = NULL;
@@ -207,6 +238,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)
@@ -218,21 +250,21 @@ static int write_data_timezone(void) {
                 return r;
         }
 
-        p = strappend("/usr/share/zoneinfo/", tz.zone);
-        if (!p) {
-                log_error("Out of memory.");
-                return -ENOMEM;
-        }
+        p = strappend(ZONEINFO_PATH, tz.zone);
+        if (!p)
+                return log_oom();
 
-        r = symlink_or_copy_atomic(p, "/etc/localtime");
+        r = symlink(p, "/etc/localtime");
         free(p);
 
         if (r < 0)
-                return r;
+                return -errno;
 
-        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;
 }
@@ -341,7 +373,7 @@ static char** get_ntp_services(void) {
 
                         q = strv_append(r, l);
                         if (!q) {
-                                log_error("Out of memory.");
+                                log_oom();
                                 break;
                         }
 
@@ -379,16 +411,14 @@ static int read_ntp(DBusConnection *bus) {
                                 "org.freedesktop.systemd1.Manager",
                                 "GetUnitFileState");
                 if (!m) {
-                        log_error("Out of memory.");
-                        r = -ENOMEM;
+                        r = log_oom();
                         goto finish;
                 }
 
                 if (!dbus_message_append_args(m,
                                               DBUS_TYPE_STRING, i,
                                               DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
+                        r = log_oom();
                         goto finish;
                 }
 
@@ -705,7 +735,11 @@ static DBusHandlerResult timedate_message_handler(
                                 hwclock_set_time(tm);
                         }
 
-                        log_info("Changed timezone to '%s'.", tz.zone);
+                        log_struct(LOG_INFO,
+                                   "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_TIMEZONE_CHANGE),
+                                   "TIMEZONE=%s", tz.zone,
+                                   "MESSAGE=Changed timezone to '%s'.", tz.zone,
+                                   NULL);
 
                         changed = bus_properties_changed_new(
                                         "/org/freedesktop/timedate1",
@@ -846,7 +880,11 @@ static DBusHandlerResult timedate_message_handler(
 
                         hwclock_set_time(tm);
 
-                        log_info("Changed local time to %s", ctime(&ts.tv_sec));
+                        log_struct(LOG_INFO,
+                                   "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_TIME_CHANGE),
+                                   "REALTIME=%llu", (unsigned long long) timespec_load(&ts),
+                                   "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec),
+                                   NULL);
                 }
         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) {
                 dbus_bool_t ntp;
@@ -943,8 +981,7 @@ static int connect_bus(DBusConnection **_bus) {
 
         if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) ||
             !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
-                log_error("Out of memory.");
-                r = -ENOMEM;
+                r = log_oom();
                 goto fail;
         }