chiark / gitweb /
timedated: sync clock down to RTC where necessary
authorLennart Poettering <lennart@poettering.net>
Thu, 16 Jun 2011 19:52:11 +0000 (21:52 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 21 Jun 2011 17:29:45 +0000 (19:29 +0200)
TODO
src/main.c
src/timedated.c
src/util.c
src/util.h

diff --git a/TODO b/TODO
index 99b77076bf338d67ec7b0028070a48f82abfca12..aab4431de21dab0eb332fb6891ecb76e17fa243e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -74,6 +74,10 @@ Features:
 
 * support wildcard expansion in EnvironmentFile= and friends
 
+* add JoinControllers= to system.conf to mount certain cgroup
+  controllers together in order to guarantee atomic creation/addition
+  of cgroups
+
 * avoid DefaultStandardOutput=syslog to have any effect on StandardInput=socket services
 
 * fix alsa mixer restore to not print error when no config is stored
index 11379f6bb434f0abc33c5fca04611ac7522170e2..045203383de7c55960b07ba158bb33f66ea98472 100644 (file)
@@ -1050,11 +1050,14 @@ int main(int argc, char *argv[]) {
                 if (label_init() < 0)
                         goto finish;
 
-                if (hwclock_is_localtime()) {
+                if (hwclock_is_localtime() > 0) {
                         int min;
 
                         min = hwclock_apply_localtime_delta();
-                        log_info("Hwclock configured in localtime, applying delta of %i minutes to system time", min);
+                        if (min < 0)
+                                log_error("Failed to apply local time delta: %s", strerror(-min));
+                        else
+                                log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min);
                 }
         } else {
                 arg_running_as = MANAGER_USER;
index 4749648df9d2e7b5f6cc703476c1e560d22b6813..ad7b881dafbaf4cd2966eafeaa532ad315ca4ba6 100644 (file)
@@ -50,6 +50,7 @@
         "  </method>\n"                                                 \
         "  <method name=\"SetLocalRTC\">\n"                             \
         "   <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n"    \
+        "   <arg name=\"correct_system\" type=\"b\" direction=\"in\"/>\n" \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
         " </interface>\n"                                               \
@@ -151,7 +152,6 @@ static void verify_timezone(void) {
 
 static int read_data(void) {
         int r;
-        FILE *f;
 
         free_data();
 
@@ -161,25 +161,7 @@ static int read_data(void) {
 
         verify_timezone();
 
-        f = fopen("/etc/adjtime", "r");
-        if (f) {
-                char line[LINE_MAX];
-                bool b;
-
-                b = fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f);
-
-                fclose(f);
-
-                if (!b)
-                        return -EIO;
-
-                truncate_nl(line);
-                local_rtc = streq(line, "LOCAL");
-
-        } else if (errno != ENOENT)
-                return -errno;
+        local_rtc = hwclock_is_localtime() > 0;
 
         return 0;
 }
@@ -333,12 +315,26 @@ static DBusHandlerResult timedate_message_handler(
                         free(zone);
                         zone = t;
 
+                        /* 1. Write new configuration file */
                         r = write_data_timezone();
                         if (r < 0) {
                                 log_error("Failed to set timezone: %s", strerror(-r));
                                 return bus_send_error_reply(connection, message, NULL, r);
                         }
 
+                        if (local_rtc) {
+                                struct timespec ts;
+                                struct tm *tm;
+
+                                /* 2. Teach kernel new timezone */
+                                hwclock_apply_localtime_delta();
+
+                                /* 3. Sync RTC from system clock, with the new delta */
+                                assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+                                assert_se(tm = localtime(&ts.tv_sec));
+                                hwclock_set_time(tm);
+                        }
+
                         log_info("Changed timezone to '%s'.", zone);
 
                         changed = bus_properties_changed_new(
@@ -351,29 +347,81 @@ static DBusHandlerResult timedate_message_handler(
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
                 dbus_bool_t lrtc;
+                dbus_bool_t correct_system;
                 dbus_bool_t interactive;
 
                 if (!dbus_message_get_args(
                                     message,
                                     &error,
                                     DBUS_TYPE_BOOLEAN, &lrtc,
+                                    DBUS_TYPE_BOOLEAN, &correct_system,
                                     DBUS_TYPE_BOOLEAN, &interactive,
                                     DBUS_TYPE_INVALID))
                         return bus_send_error_reply(connection, message, &error, -EINVAL);
 
                 if (lrtc != local_rtc) {
+                        struct timespec ts;
+
                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error);
                         if (r < 0)
                                 return bus_send_error_reply(connection, message, &error, r);
 
                         local_rtc = lrtc;
 
+                        /* 1. Write new configuration file */
                         r = write_data_local_rtc();
                         if (r < 0) {
                                 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
                                 return bus_send_error_reply(connection, message, NULL, r);
                         }
 
+                        /* 2. Teach kernel new timezone */
+                        if (local_rtc)
+                                hwclock_apply_localtime_delta();
+                        else
+                                hwclock_reset_localtime_delta();
+
+                        /* 3. Synchronize clocks */
+                        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+                        if (correct_system) {
+                                struct tm tm;
+
+                                /* Sync system clock from RTC; first,
+                                 * initialize the timezone fields of
+                                 * struct tm. */
+                                if (local_rtc)
+                                        tm = *localtime(&ts.tv_sec);
+                                else
+                                        tm = *gmtime(&ts.tv_sec);
+
+                                /* Override the main fields of
+                                 * struct tm, but not the timezone
+                                 * fields */
+                                if (hwclock_get_time(&tm) >= 0) {
+
+                                        /* And set the system clock
+                                         * with this */
+                                        if (local_rtc)
+                                                ts.tv_sec = mktime(&tm);
+                                        else
+                                                ts.tv_sec = timegm(&tm);
+
+                                        clock_settime(CLOCK_REALTIME, &ts);
+                                }
+
+                        } else {
+                                struct tm *tm;
+
+                                /* Sync RTC from system clock */
+                                if (local_rtc)
+                                        tm = localtime(&ts.tv_sec);
+                                else
+                                        tm = gmtime(&ts.tv_sec);
+
+                                hwclock_set_time(tm);
+                        }
+
                         log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc));
 
                         changed = bus_properties_changed_new(
@@ -403,6 +451,7 @@ static DBusHandlerResult timedate_message_handler(
 
                 if (!relative || utc != 0) {
                         struct timespec ts;
+                        struct tm* tm;
 
                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error);
                         if (r < 0)
@@ -413,11 +462,20 @@ static DBusHandlerResult timedate_message_handler(
                         else
                                 timespec_store(&ts, utc);
 
+                        /* Set system clock */
                         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
                                 log_error("Failed to set local time: %m");
                                 return bus_send_error_reply(connection, message, NULL, -errno);
                         }
 
+                        /* Sync down to RTC */
+                        if (local_rtc)
+                                tm = localtime(&ts.tv_sec);
+                        else
+                                tm = gmtime(&ts.tv_sec);
+
+                        hwclock_set_time(tm);
+
                         log_info("Changed local time to %s", ctime(&ts.tv_sec));
                 }
 
index dfb153bbb6285258e5b0f6400a745a42ff816152..2047ebd7bd841bd8b045c86258327dab81f4fcf4 100644 (file)
@@ -4785,40 +4785,50 @@ finish:
         return r;
 }
 
-bool hwclock_is_localtime(void) {
+int hwclock_is_localtime(void) {
         FILE *f;
-        char line[LINE_MAX];
         bool local = false;
 
         /*
          * The third line of adjtime is "UTC" or "LOCAL" or nothing.
          *   # /etc/adjtime
-         *   0.0 0 0.0
+         *   0.0 0 0
          *   0
          *   UTC
          */
         f = fopen("/etc/adjtime", "re");
         if (f) {
-                if (fgets(line, sizeof(line), f) &&
-                    fgets(line, sizeof(line), f) &&
-                    fgets(line, sizeof(line), f) ) {
-                            if (!strcmp(line, "LOCAL\n"))
-                                 local = true;
-                }
+                char line[LINE_MAX];
+                bool b;
+
+                b = fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f);
+
                 fclose(f);
-        }
+
+                if (!b)
+                        return -EIO;
+
+
+                truncate_nl(line);
+                local = streq(line, "LOCAL");
+
+        } else if (errno != -ENOENT)
+                return -errno;
+
         return local;
 }
 
 int hwclock_apply_localtime_delta(void) {
         const struct timeval *tv_null = NULL;
-        struct timeval tv;
+        struct timespec ts;
         struct tm *tm;
         int minuteswest;
         struct timezone tz;
 
-        gettimeofday(&tv, NULL);
-        tm = localtime(&tv.tv_sec);
+        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+        assert_se(tm = localtime(&ts.tv_sec));
         minuteswest = tm->tm_gmtoff / 60;
 
         tz.tz_minuteswest = -minuteswest;
@@ -4831,20 +4841,43 @@ int hwclock_apply_localtime_delta(void) {
          */
         if (settimeofday(tv_null, &tz) < 0)
                 return -errno;
-        else
-                return minuteswest;
+
+        return minuteswest;
+}
+
+int hwclock_reset_localtime_delta(void) {
+        const struct timeval *tv_null = NULL;
+        struct timezone tz;
+
+        tz.tz_minuteswest = 0;
+        tz.tz_dsttime = 0; /* DST_NONE*/
+
+        if (settimeofday(tv_null, &tz) < 0)
+                return -errno;
+
+        return 0;
 }
 
 int hwclock_get_time(struct tm *tm) {
         int fd;
         int err = 0;
 
+        assert(tm);
+
         fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
+
+        /* This leaves the timezone fields of struct tm
+         * uninitialized! */
         if (ioctl(fd, RTC_RD_TIME, tm) < 0)
                 err = -errno;
-        close(fd);
+
+        /* We don't now daylight saving, so we reset this in order not
+         * to confused mktime(). */
+        tm->tm_isdst = -1;
+
+        close_nointr_nofail(fd);
 
         return err;
 }
@@ -4853,12 +4886,16 @@ int hwclock_set_time(const struct tm *tm) {
         int fd;
         int err = 0;
 
+        assert(tm);
+
         fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
+
         if (ioctl(fd, RTC_SET_TIME, tm) < 0)
                 err = -errno;
-        close(fd);
+
+        close_nointr_nofail(fd);
 
         return err;
 }
index bd98b654fdfcec8b9d0491b1207b99c948c3eddf..3863a08b6346c5829aedbb1576b368a96de4d720 100644 (file)
@@ -431,12 +431,11 @@ int fchmod_umask(int fd, mode_t mode);
 
 int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
 
-bool hwclock_is_localtime(void);
+int hwclock_is_localtime(void);
 
 int hwclock_apply_localtime_delta(void);
-
+int hwclock_reset_localtime_delta(void);
 int hwclock_get_time(struct tm *tm);
-
 int hwclock_set_time(const struct tm *tm);
 
 #define NULSTR_FOREACH(i, l)                                    \