#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
+#define INTERFACE \
" <interface name=\"org.freedesktop.timedate1\">\n" \
" <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n" \
" </method>\n" \
" <method name=\"SetLocalRTC\">\n" \
" <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
- " </interface>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ INTERFACE \
BUS_PROPERTIES_INTERFACE \
BUS_INTROSPECTABLE_INTERFACE \
BUS_PEER_INTERFACE \
BUS_GENERIC_INTERFACES_LIST \
"org.freedesktop.locale1\0"
+const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
+
static char *zone = NULL;
static bool local_rtc = false;
static int read_data(void) {
int r;
- FILE *f;
free_data();
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;
}
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(
} else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
dbus_bool_t lrtc;
+ dbus_bool_t fix_system;
dbus_bool_t interactive;
if (!dbus_message_get_args(
message,
&error,
DBUS_TYPE_BOOLEAN, &lrtc,
+ DBUS_TYPE_BOOLEAN, &fix_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);
}
- log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc));
+ /* 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 (fix_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_error("RTC configured to %s time.", local_rtc ? "local" : "UTC");
changed = bus_properties_changed_new(
"/org/freedesktop/timedate1",
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)
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));
}
bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
if (!bus) {
- log_error("Failed to get system D-Bus connection: %s", error.message);
+ log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
r = -ECONNREFUSED;
goto fail;
}
goto fail;
}
- if (dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
- log_error("Failed to register name on bus: %s", error.message);
+ r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
+ if (dbus_error_is_set(&error)) {
+ log_error("Failed to register name on bus: %s", bus_error_message(&error));
+ r = -EEXIST;
+ goto fail;
+ }
+
+ if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ log_error("Failed to acquire name.");
r = -EEXIST;
goto fail;
}
log_parse_environment();
log_open();
+ if (argc == 2 && streq(argv[1], "--introspect")) {
+ fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node>\n", stdout);
+ fputs(timedate_interface, stdout);
+ fputs("</node>\n", stdout);
+ return 0;
+ }
+
if (argc != 1) {
log_error("This program takes no arguments.");
r = -EINVAL;