timedated: sync clock down to RTC where necessary
This commit is contained in:
parent
d3fc81bd6a
commit
2076cf8831
4
TODO
4
TODO
|
@ -74,6 +74,10 @@ Features:
|
||||||
|
|
||||||
* support wildcard expansion in EnvironmentFile= and friends
|
* 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
|
* avoid DefaultStandardOutput=syslog to have any effect on StandardInput=socket services
|
||||||
|
|
||||||
* fix alsa mixer restore to not print error when no config is stored
|
* fix alsa mixer restore to not print error when no config is stored
|
||||||
|
|
|
@ -1050,11 +1050,14 @@ int main(int argc, char *argv[]) {
|
||||||
if (label_init() < 0)
|
if (label_init() < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
if (hwclock_is_localtime()) {
|
if (hwclock_is_localtime() > 0) {
|
||||||
int min;
|
int min;
|
||||||
|
|
||||||
min = hwclock_apply_localtime_delta();
|
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 {
|
} else {
|
||||||
arg_running_as = MANAGER_USER;
|
arg_running_as = MANAGER_USER;
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
" </method>\n" \
|
" </method>\n" \
|
||||||
" <method name=\"SetLocalRTC\">\n" \
|
" <method name=\"SetLocalRTC\">\n" \
|
||||||
" <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\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" \
|
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
|
||||||
" </method>\n" \
|
" </method>\n" \
|
||||||
" </interface>\n" \
|
" </interface>\n" \
|
||||||
|
@ -151,7 +152,6 @@ static void verify_timezone(void) {
|
||||||
|
|
||||||
static int read_data(void) {
|
static int read_data(void) {
|
||||||
int r;
|
int r;
|
||||||
FILE *f;
|
|
||||||
|
|
||||||
free_data();
|
free_data();
|
||||||
|
|
||||||
|
@ -161,25 +161,7 @@ static int read_data(void) {
|
||||||
|
|
||||||
verify_timezone();
|
verify_timezone();
|
||||||
|
|
||||||
f = fopen("/etc/adjtime", "r");
|
local_rtc = hwclock_is_localtime() > 0;
|
||||||
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;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -333,12 +315,26 @@ static DBusHandlerResult timedate_message_handler(
|
||||||
free(zone);
|
free(zone);
|
||||||
zone = t;
|
zone = t;
|
||||||
|
|
||||||
|
/* 1. Write new configuration file */
|
||||||
r = write_data_timezone();
|
r = write_data_timezone();
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("Failed to set timezone: %s", strerror(-r));
|
log_error("Failed to set timezone: %s", strerror(-r));
|
||||||
return bus_send_error_reply(connection, message, NULL, 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);
|
log_info("Changed timezone to '%s'.", zone);
|
||||||
|
|
||||||
changed = bus_properties_changed_new(
|
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")) {
|
} else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
|
||||||
dbus_bool_t lrtc;
|
dbus_bool_t lrtc;
|
||||||
|
dbus_bool_t correct_system;
|
||||||
dbus_bool_t interactive;
|
dbus_bool_t interactive;
|
||||||
|
|
||||||
if (!dbus_message_get_args(
|
if (!dbus_message_get_args(
|
||||||
message,
|
message,
|
||||||
&error,
|
&error,
|
||||||
DBUS_TYPE_BOOLEAN, &lrtc,
|
DBUS_TYPE_BOOLEAN, &lrtc,
|
||||||
|
DBUS_TYPE_BOOLEAN, &correct_system,
|
||||||
DBUS_TYPE_BOOLEAN, &interactive,
|
DBUS_TYPE_BOOLEAN, &interactive,
|
||||||
DBUS_TYPE_INVALID))
|
DBUS_TYPE_INVALID))
|
||||||
return bus_send_error_reply(connection, message, &error, -EINVAL);
|
return bus_send_error_reply(connection, message, &error, -EINVAL);
|
||||||
|
|
||||||
if (lrtc != local_rtc) {
|
if (lrtc != local_rtc) {
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error);
|
r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_send_error_reply(connection, message, &error, r);
|
return bus_send_error_reply(connection, message, &error, r);
|
||||||
|
|
||||||
local_rtc = lrtc;
|
local_rtc = lrtc;
|
||||||
|
|
||||||
|
/* 1. Write new configuration file */
|
||||||
r = write_data_local_rtc();
|
r = write_data_local_rtc();
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
|
log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
|
||||||
return bus_send_error_reply(connection, message, NULL, 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));
|
log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc));
|
||||||
|
|
||||||
changed = bus_properties_changed_new(
|
changed = bus_properties_changed_new(
|
||||||
|
@ -403,6 +451,7 @@ static DBusHandlerResult timedate_message_handler(
|
||||||
|
|
||||||
if (!relative || utc != 0) {
|
if (!relative || utc != 0) {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
struct tm* tm;
|
||||||
|
|
||||||
r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error);
|
r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -413,11 +462,20 @@ static DBusHandlerResult timedate_message_handler(
|
||||||
else
|
else
|
||||||
timespec_store(&ts, utc);
|
timespec_store(&ts, utc);
|
||||||
|
|
||||||
|
/* Set system clock */
|
||||||
if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
|
if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
|
||||||
log_error("Failed to set local time: %m");
|
log_error("Failed to set local time: %m");
|
||||||
return bus_send_error_reply(connection, message, NULL, -errno);
|
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));
|
log_info("Changed local time to %s", ctime(&ts.tv_sec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
67
src/util.c
67
src/util.c
|
@ -4785,40 +4785,50 @@ finish:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hwclock_is_localtime(void) {
|
int hwclock_is_localtime(void) {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
char line[LINE_MAX];
|
|
||||||
bool local = false;
|
bool local = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The third line of adjtime is "UTC" or "LOCAL" or nothing.
|
* The third line of adjtime is "UTC" or "LOCAL" or nothing.
|
||||||
* # /etc/adjtime
|
* # /etc/adjtime
|
||||||
* 0.0 0 0.0
|
* 0.0 0 0
|
||||||
* 0
|
* 0
|
||||||
* UTC
|
* UTC
|
||||||
*/
|
*/
|
||||||
f = fopen("/etc/adjtime", "re");
|
f = fopen("/etc/adjtime", "re");
|
||||||
if (f) {
|
if (f) {
|
||||||
if (fgets(line, sizeof(line), f) &&
|
char line[LINE_MAX];
|
||||||
|
bool b;
|
||||||
|
|
||||||
|
b = fgets(line, sizeof(line), f) &&
|
||||||
fgets(line, sizeof(line), f) &&
|
fgets(line, sizeof(line), f) &&
|
||||||
fgets(line, sizeof(line), f) ) {
|
fgets(line, sizeof(line), f);
|
||||||
if (!strcmp(line, "LOCAL\n"))
|
|
||||||
local = true;
|
|
||||||
}
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
|
||||||
|
if (!b)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
|
||||||
|
truncate_nl(line);
|
||||||
|
local = streq(line, "LOCAL");
|
||||||
|
|
||||||
|
} else if (errno != -ENOENT)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hwclock_apply_localtime_delta(void) {
|
int hwclock_apply_localtime_delta(void) {
|
||||||
const struct timeval *tv_null = NULL;
|
const struct timeval *tv_null = NULL;
|
||||||
struct timeval tv;
|
struct timespec ts;
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
int minuteswest;
|
int minuteswest;
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
|
|
||||||
gettimeofday(&tv, NULL);
|
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
|
||||||
tm = localtime(&tv.tv_sec);
|
assert_se(tm = localtime(&ts.tv_sec));
|
||||||
minuteswest = tm->tm_gmtoff / 60;
|
minuteswest = tm->tm_gmtoff / 60;
|
||||||
|
|
||||||
tz.tz_minuteswest = -minuteswest;
|
tz.tz_minuteswest = -minuteswest;
|
||||||
|
@ -4831,20 +4841,43 @@ int hwclock_apply_localtime_delta(void) {
|
||||||
*/
|
*/
|
||||||
if (settimeofday(tv_null, &tz) < 0)
|
if (settimeofday(tv_null, &tz) < 0)
|
||||||
return -errno;
|
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 hwclock_get_time(struct tm *tm) {
|
||||||
int fd;
|
int fd;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
assert(tm);
|
||||||
|
|
||||||
fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
|
fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
/* This leaves the timezone fields of struct tm
|
||||||
|
* uninitialized! */
|
||||||
if (ioctl(fd, RTC_RD_TIME, tm) < 0)
|
if (ioctl(fd, RTC_RD_TIME, tm) < 0)
|
||||||
err = -errno;
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -4853,12 +4886,16 @@ int hwclock_set_time(const struct tm *tm) {
|
||||||
int fd;
|
int fd;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
assert(tm);
|
||||||
|
|
||||||
fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
|
fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (ioctl(fd, RTC_SET_TIME, tm) < 0)
|
if (ioctl(fd, RTC_SET_TIME, tm) < 0)
|
||||||
err = -errno;
|
err = -errno;
|
||||||
close(fd);
|
|
||||||
|
close_nointr_nofail(fd);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,12 +431,11 @@ int fchmod_umask(int fd, mode_t mode);
|
||||||
|
|
||||||
int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
|
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_apply_localtime_delta(void);
|
||||||
|
int hwclock_reset_localtime_delta(void);
|
||||||
int hwclock_get_time(struct tm *tm);
|
int hwclock_get_time(struct tm *tm);
|
||||||
|
|
||||||
int hwclock_set_time(const struct tm *tm);
|
int hwclock_set_time(const struct tm *tm);
|
||||||
|
|
||||||
#define NULSTR_FOREACH(i, l) \
|
#define NULSTR_FOREACH(i, l) \
|
||||||
|
|
Loading…
Reference in a new issue