summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.c7
-rw-r--r--src/timedated.c98
-rw-r--r--src/util.c71
-rw-r--r--src/util.h5
4 files changed, 139 insertions, 42 deletions
diff --git a/src/main.c b/src/main.c
index 11379f6bb4..045203383d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
diff --git a/src/timedated.c b/src/timedated.c
index 4749648df9..ad7b881daf 100644
--- a/src/timedated.c
+++ b/src/timedated.c
@@ -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));
}
diff --git a/src/util.c b/src/util.c
index dfb153bbb6..2047ebd7bd 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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;
}
diff --git a/src/util.h b/src/util.h
index bd98b654fd..3863a08b63 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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) \