1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "sd-messages.h"
35 #include "conf-files.h"
36 #include "path-util.h"
37 #include "fileio-label.h"
40 #include "event-util.h"
42 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
43 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
45 typedef struct Context {
50 Hashmap *polkit_registry;
53 static void context_reset(Context *c) {
60 c->can_ntp = c->use_ntp = -1;
63 static void context_free(Context *c, sd_bus *bus) {
67 bus_verify_polkit_async_registry_free(bus, c->polkit_registry);
70 static bool valid_timezone(const char *name) {
79 if (*name == '/' || *name == 0)
82 for (p = name; *p; p++) {
83 if (!(*p >= '0' && *p <= '9') &&
84 !(*p >= 'a' && *p <= 'z') &&
85 !(*p >= 'A' && *p <= 'Z') &&
86 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
102 t = strappend("/usr/share/zoneinfo/", name);
112 if (!S_ISREG(st.st_mode))
118 static int context_read_data(Context *c) {
119 _cleanup_free_ char *t = NULL;
126 r = readlink_malloc("/etc/localtime", &t);
129 log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
131 log_warning("Failed to get target of /etc/localtime: %s", strerror(-r));
135 e = path_startswith(t, "/usr/share/zoneinfo/");
137 e = path_startswith(t, "../usr/share/zoneinfo/");
140 log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
151 if (isempty(c->zone)) {
156 c->local_rtc = hwclock_is_localtime() > 0;
161 static int context_write_data_timezone(Context *c) {
162 _cleanup_free_ char *p = NULL;
167 if (isempty(c->zone)) {
168 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
174 p = strappend("../usr/share/zoneinfo/", c->zone);
178 r = symlink_atomic(p, "/etc/localtime");
185 static int context_write_data_local_rtc(Context *c) {
187 _cleanup_free_ char *s = NULL, *w = NULL;
191 r = read_full_file("/etc/adjtime", &s, NULL);
199 w = strdup(NULL_ADJTIME_LOCAL);
210 p = strchr(p+1, '\n');
222 w = new(char, a + (c->local_rtc ? 5 : 3) + b + 1);
226 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
228 if (streq(w, NULL_ADJTIME_UTC)) {
229 if (unlink("/etc/adjtime") < 0)
238 return write_string_file_atomic_label("/etc/adjtime", w);
241 static char** get_ntp_services(void) {
242 _cleanup_strv_free_ char **r = NULL, **files = NULL;
246 k = conf_files_list(&files, ".list", NULL,
247 "/etc/systemd/ntp-units.d",
248 "/run/systemd/ntp-units.d",
249 "/usr/local/lib/systemd/ntp-units.d",
250 "/usr/lib/systemd/ntp-units.d",
251 #ifdef HAVE_SPLIT_USR
252 "/lib/systemd/ntp-units.d",
258 STRV_FOREACH(i, files) {
259 _cleanup_fclose_ FILE *f;
266 char line[PATH_MAX], *l;
268 if (!fgets(line, sizeof(line), f)) {
271 log_error("Failed to read NTP units file: %m");
277 if (l[0] == 0 || l[0] == '#')
280 if (strv_extend(&r, l) < 0) {
288 r = NULL; /* avoid cleanup */
293 static int context_read_ntp(Context *c, sd_bus *bus) {
294 _cleanup_strv_free_ char **l;
301 l = get_ntp_services();
303 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
304 sd_bus_message *reply = NULL;
307 r = sd_bus_call_method(
309 "org.freedesktop.systemd1",
310 "/org/freedesktop/systemd1",
311 "org.freedesktop.systemd1.Manager",
319 /* This implementation does not exist, try next one */
320 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND))
326 r = sd_bus_message_read(reply, "s", &s);
332 streq(s, "enabled") ||
333 streq(s, "enabled-runtime");
338 /* NTP is not installed. */
345 static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
346 _cleanup_strv_free_ char **l = NULL;
354 l = get_ntp_services();
358 r = sd_bus_call_method(
360 "org.freedesktop.systemd1",
361 "/org/freedesktop/systemd1",
362 "org.freedesktop.systemd1.Manager",
366 "ss", *i, "replace");
368 r = sd_bus_call_method(
370 "org.freedesktop.systemd1",
371 "/org/freedesktop/systemd1",
372 "org.freedesktop.systemd1.Manager",
376 "ss", *i, "replace");
379 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
380 sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
381 sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) {
382 /* This implementation does not exist, try next one */
383 sd_bus_error_free(error);
393 sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
397 static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) {
398 _cleanup_strv_free_ char **l = NULL;
406 l = get_ntp_services();
409 r = sd_bus_call_method(
411 "org.freedesktop.systemd1",
412 "/org/freedesktop/systemd1",
413 "org.freedesktop.systemd1.Manager",
417 "asbb", 1, *i, false, true);
419 r = sd_bus_call_method(
421 "org.freedesktop.systemd1",
422 "/org/freedesktop/systemd1",
423 "org.freedesktop.systemd1.Manager",
427 "asb", 1, *i, false);
430 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) {
431 /* This implementation does not exist, try next one */
432 sd_bus_error_free(error);
439 r = sd_bus_call_method(
441 "org.freedesktop.systemd1",
442 "/org/freedesktop/systemd1",
443 "org.freedesktop.systemd1.Manager",
454 sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
458 static int property_get_rtc_time(
461 const char *interface,
462 const char *property,
463 sd_bus_message *reply,
472 r = hwclock_get_time(&tm);
474 log_warning("/dev/rtc is busy, is somebody keeping it open continously? That's not a good idea... Returning a bogus RTC timestamp.");
477 sd_bus_error_set_errnof(error, -r, "Failed to read RTC: %s", strerror(-r));
480 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
482 r = sd_bus_message_append(reply, "t", t);
489 static int property_get_time(
492 const char *interface,
493 const char *property,
494 sd_bus_message *reply,
500 r = sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
507 static int property_get_ntp_sync(
510 const char *interface,
511 const char *property,
512 sd_bus_message *reply,
518 r = sd_bus_message_append(reply, "b", ntp_synced());
525 static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata) {
526 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
527 Context *c = userdata;
529 unsigned interactive;
537 r = sd_bus_message_read(m, "sb", &z, &interactive);
539 return sd_bus_reply_method_errno(bus, m, r, NULL);
541 if (!valid_timezone(z))
542 return sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
544 if (streq_ptr(z, c->zone))
545 return sd_bus_reply_method_return(bus, m, NULL);
547 r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-timezone", interactive, &error, method_set_timezone, c);
549 return sd_bus_reply_method_errno(bus, m, r, &error);
551 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
560 /* 1. Write new configuration file */
561 r = context_write_data_timezone(c);
563 log_error("Failed to set timezone: %s", strerror(-r));
564 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set timezone: %s", strerror(-r));
567 /* 2. Tell the kernel our timezone */
568 hwclock_set_timezone(NULL);
574 /* 3. Sync RTC from system clock, with the new delta */
575 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
576 assert_se(tm = localtime(&ts.tv_sec));
577 hwclock_set_time(tm);
581 MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
582 "TIMEZONE=%s", c->zone,
583 "MESSAGE=Changed timezone to '%s'.", c->zone,
586 sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
588 return sd_bus_reply_method_return(bus, m, NULL);
591 static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata) {
592 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
593 unsigned lrtc, fix_system, interactive;
594 Context *c = userdata;
602 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
604 return sd_bus_reply_method_errno(bus, m, r, NULL);
606 if (lrtc == c->local_rtc)
607 return sd_bus_reply_method_return(bus, m, NULL);
609 r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-local-rtc", interactive, &error, method_set_local_rtc, c);
611 return sd_bus_reply_method_errno(bus, m, r, &error);
617 /* 1. Write new configuration file */
618 r = context_write_data_local_rtc(c);
620 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
621 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set RTC to local/UTC: %s", strerror(-r));
624 /* 2. Tell the kernel our timezone */
625 hwclock_set_timezone(NULL);
627 /* 3. Synchronize clocks */
628 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
633 /* Sync system clock from RTC; first,
634 * initialize the timezone fields of
637 tm = *localtime(&ts.tv_sec);
639 tm = *gmtime(&ts.tv_sec);
641 /* Override the main fields of
642 * struct tm, but not the timezone
644 if (hwclock_get_time(&tm) >= 0) {
646 /* And set the system clock
649 ts.tv_sec = mktime(&tm);
651 ts.tv_sec = timegm(&tm);
653 clock_settime(CLOCK_REALTIME, &ts);
659 /* Sync RTC from system clock */
661 tm = localtime(&ts.tv_sec);
663 tm = gmtime(&ts.tv_sec);
665 hwclock_set_time(tm);
668 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
670 sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
672 return sd_bus_reply_method_return(bus, m, NULL);
675 static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata) {
676 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
677 unsigned relative, interactive;
678 Context *c = userdata;
688 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
690 return sd_bus_reply_method_errno(bus, m, r, NULL);
692 if (!relative && utc <= 0)
693 return sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
695 if (relative && utc == 0)
696 return sd_bus_reply_method_return(bus, m, NULL);
701 n = now(CLOCK_REALTIME);
704 if ((utc > 0 && x < n) ||
706 return sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
708 timespec_store(&ts, x);
710 timespec_store(&ts, (usec_t) utc);
712 r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-time", interactive, &error, method_set_time, c);
714 return sd_bus_reply_method_errno(bus, m, r, &error);
718 /* Set system clock */
719 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
720 log_error("Failed to set local time: %m");
721 return sd_bus_reply_method_errnof(bus, m, errno, "Failed to set local time: %m");
724 /* Sync down to RTC */
726 tm = localtime(&ts.tv_sec);
728 tm = gmtime(&ts.tv_sec);
730 hwclock_set_time(tm);
733 MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
734 "REALTIME=%llu", (unsigned long long) timespec_load(&ts),
735 "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec),
738 return sd_bus_reply_method_return(bus, m, NULL);
741 static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata) {
742 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
743 unsigned ntp, interactive;
744 Context *c = userdata;
747 r = sd_bus_message_read(m, "bb", &ntp, &interactive);
749 return sd_bus_reply_method_errno(bus, m, r, NULL);
751 if (ntp == c->use_ntp)
752 return sd_bus_reply_method_return(bus, m, NULL);
754 r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-ntp", interactive, &error, method_set_ntp, c);
756 return sd_bus_reply_method_errno(bus, m, r, &error);
762 r = context_enable_ntp(c, bus, &error);
764 return sd_bus_reply_method_errno(bus, m, r, &error);
766 r = context_start_ntp(c, bus, &error);
768 return sd_bus_reply_method_errno(bus, m, r, &error);
770 log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
772 sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
774 return sd_bus_reply_method_return(bus, m, NULL);
777 static const sd_bus_vtable timedate_vtable[] = {
778 SD_BUS_VTABLE_START(0),
779 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
780 SD_BUS_PROPERTY("LocalRTC", "b", NULL, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
781 SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_tristate, offsetof(Context, can_ntp), 0),
782 SD_BUS_PROPERTY("NTP", "b", bus_property_get_tristate, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
783 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
784 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
785 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
786 SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, 0),
787 SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, 0),
788 SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, 0),
789 SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, 0),
793 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
794 _cleanup_bus_unref_ sd_bus *bus = NULL;
801 r = sd_bus_open_system(&bus);
803 log_error("Failed to get system bus connection: %s", strerror(-r));
807 r = sd_bus_add_object_vtable(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
809 log_error("Failed to register object: %s", strerror(-r));
813 r = sd_bus_request_name(bus, "org.freedesktop.timedate1", SD_BUS_NAME_DO_NOT_QUEUE);
815 log_error("Failed to register name: %s", strerror(-r));
819 if (r != SD_BUS_NAME_PRIMARY_OWNER) {
820 log_error("Failed to acquire name.");
824 r = sd_bus_attach_event(bus, event, 0);
826 log_error("Failed to attach bus to event loop: %s", strerror(-r));
836 int main(int argc, char *argv[]) {
844 _cleanup_event_unref_ sd_event *event = NULL;
845 _cleanup_bus_unref_ sd_bus *bus = NULL;
848 log_set_target(LOG_TARGET_AUTO);
849 log_parse_environment();
855 log_error("This program takes no arguments.");
860 r = sd_event_new(&event);
862 log_error("Failed to allocate event loop: %s", strerror(-r));
866 r = connect_bus(&context, event, &bus);
870 r = context_read_data(&context);
872 log_error("Failed to read timezone data: %s", strerror(-r));
876 r = context_read_ntp(&context, bus);
878 log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
882 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC);
884 log_error("Failed to run event loop: %s", strerror(-r));
891 context_free(&context, bus);
893 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;