X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Ftimedated.c;h=4abcf1af73df3a88f0a48b3383cfabd15077ee62;hb=d937fbbd97760d4d1a59dc0fa6f3000e57a96998;hp=4749648df9d2e7b5f6cc703476c1e560d22b6813;hpb=f401e48c2db22ff9d1a05885b5599bebf19c2707;p=elogind.git diff --git a/src/timedated.c b/src/timedated.c index 4749648df..4abcf1af7 100644 --- a/src/timedated.c +++ b/src/timedated.c @@ -33,9 +33,7 @@ #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 \ - "\n" \ +#define INTERFACE \ " \n" \ " \n" \ " \n" \ @@ -50,9 +48,15 @@ " \n" \ " \n" \ " \n" \ + " \n" \ " \n" \ " \n" \ - " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + INTERFACE \ BUS_PROPERTIES_INTERFACE \ BUS_INTROSPECTABLE_INTERFACE \ BUS_PEER_INTERFACE \ @@ -62,6 +66,8 @@ BUS_GENERIC_INTERFACES_LIST \ "org.freedesktop.locale1\0" +const char timedate_interface[] _introspect_("timedate1") = INTERFACE; + static char *zone = NULL; static bool local_rtc = false; @@ -151,7 +157,6 @@ static void verify_timezone(void) { static int read_data(void) { int r; - FILE *f; free_data(); @@ -161,25 +166,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 +320,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(NULL); + + /* 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,30 +352,82 @@ static DBusHandlerResult timedate_message_handler( } 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(NULL); + 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", @@ -403,6 +456,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 +467,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)); } @@ -469,7 +532,7 @@ static int connect_bus(DBusConnection **_bus) { 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; } @@ -480,8 +543,15 @@ static int connect_bus(DBusConnection **_bus) { 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; } @@ -508,6 +578,14 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + if (argc == 2 && streq(argv[1], "--introspect")) { + fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "\n", stdout); + fputs(timedate_interface, stdout); + fputs("\n", stdout); + return 0; + } + if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL;