]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
hostnamectl: allow trailing dot on fqdn
[thirdparty/systemd.git] / src / timedate / timedated.c
CommitLineData
f401e48c
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
f401e48c
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
f401e48c 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
f401e48c
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
f401e48c
LP
22#include <errno.h>
23#include <string.h>
24#include <unistd.h>
25
40ca29a1
LP
26#include "sd-messages.h"
27#include "sd-event.h"
28#include "sd-bus.h"
29
f401e48c
LP
30#include "util.h"
31#include "strv.h"
ad740100 32#include "def.h"
24efb112 33#include "clock-util.h"
424a19f8 34#include "path-util.h"
a5c32cff 35#include "fileio-label.h"
40ca29a1 36#include "bus-util.h"
5f86c1f4 37#include "bus-error.h"
96aad8d1 38#include "bus-common-errors.h"
40ca29a1 39#include "event-util.h"
d7b8eec7 40#include "selinux-util.h"
f401e48c
LP
41
42#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
43#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
44
5f86c1f4 45static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = {
15411c0c 46 SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", EOPNOTSUPP),
5f86c1f4 47 SD_BUS_ERROR_MAP_END
7358dc02
ZJS
48};
49
40ca29a1 50typedef struct Context {
d200735e
MS
51 char *zone;
52 bool local_rtc;
82d115d9
KS
53 bool can_ntp;
54 bool use_ntp;
40ca29a1
LP
55 Hashmap *polkit_registry;
56} Context;
f401e48c 57
36e34057 58static void context_free(Context *c) {
40ca29a1 59 assert(c);
f401e48c 60
82d115d9 61 free(c->zone);
36e34057 62 bus_verify_polkit_async_registry_free(c->polkit_registry);
f401e48c
LP
63}
64
40ca29a1 65static int context_read_data(Context *c) {
424a19f8 66 _cleanup_free_ char *t = NULL;
40ca29a1
LP
67 int r;
68
69 assert(c);
f401e48c 70
92c4ef2d
SL
71 r = readlink_malloc("/etc/localtime", &t);
72 if (r < 0) {
73 if (r == -EINVAL)
07a062a7 74 log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
92c4ef2d 75 else
da927ba9 76 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
92c4ef2d 77 } else {
424a19f8 78 const char *e;
92c4ef2d 79
424a19f8
LP
80 e = path_startswith(t, "/usr/share/zoneinfo/");
81 if (!e)
82 e = path_startswith(t, "../usr/share/zoneinfo/");
83
84 if (!e)
07a062a7 85 log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
92c4ef2d 86 else {
40ca29a1
LP
87 c->zone = strdup(e);
88 if (!c->zone)
92c4ef2d 89 return log_oom();
92c4ef2d
SL
90 }
91 }
92
40ca29a1
LP
93 if (isempty(c->zone)) {
94 free(c->zone);
95 c->zone = NULL;
a724d2ed 96 }
f401e48c 97
24efb112 98 c->local_rtc = clock_is_localtime() > 0;
f401e48c
LP
99
100 return 0;
101}
102
40ca29a1 103static int context_write_data_timezone(Context *c) {
424a19f8 104 _cleanup_free_ char *p = NULL;
40ca29a1
LP
105 int r = 0;
106
107 assert(c);
e19a21a8 108
40ca29a1 109 if (isempty(c->zone)) {
424a19f8 110 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
f401e48c
LP
111 r = -errno;
112
f401e48c
LP
113 return r;
114 }
115
40ca29a1 116 p = strappend("../usr/share/zoneinfo/", c->zone);
0d0f0c50
SL
117 if (!p)
118 return log_oom();
f401e48c 119
424a19f8 120 r = symlink_atomic(p, "/etc/localtime");
f401e48c 121 if (r < 0)
424a19f8 122 return r;
f401e48c 123
f401e48c
LP
124 return 0;
125}
126
40ca29a1 127static int context_write_data_local_rtc(Context *c) {
f401e48c 128 int r;
7fd1b19b 129 _cleanup_free_ char *s = NULL, *w = NULL;
f401e48c 130
40ca29a1
LP
131 assert(c);
132
f401e48c
LP
133 r = read_full_file("/etc/adjtime", &s, NULL);
134 if (r < 0) {
135 if (r != -ENOENT)
136 return r;
137
40ca29a1 138 if (!c->local_rtc)
f401e48c
LP
139 return 0;
140
141 w = strdup(NULL_ADJTIME_LOCAL);
142 if (!w)
143 return -ENOMEM;
144 } else {
145 char *p, *e;
146 size_t a, b;
147
148 p = strchr(s, '\n');
d257f05a 149 if (!p)
f401e48c 150 return -EIO;
f401e48c
LP
151
152 p = strchr(p+1, '\n');
d257f05a 153 if (!p)
f401e48c 154 return -EIO;
f401e48c
LP
155
156 p++;
157 e = strchr(p, '\n');
d257f05a 158 if (!e)
f401e48c 159 return -EIO;
f401e48c
LP
160
161 a = p - s;
162 b = strlen(e);
163
40ca29a1 164 w = new(char, a + (c->local_rtc ? 5 : 3) + b + 1);
d257f05a 165 if (!w)
f401e48c 166 return -ENOMEM;
f401e48c 167
40ca29a1 168 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
f401e48c
LP
169
170 if (streq(w, NULL_ADJTIME_UTC)) {
d257f05a 171 if (unlink("/etc/adjtime") < 0)
f401e48c
LP
172 if (errno != ENOENT)
173 return -errno;
f401e48c
LP
174
175 return 0;
176 }
177 }
40ca29a1 178
cc56fafe 179 mac_selinux_init("/etc");
d257f05a 180 return write_string_file_atomic_label("/etc/adjtime", w);
f401e48c
LP
181}
182
40ca29a1 183static int context_read_ntp(Context *c, sd_bus *bus) {
b72ddf0f 184 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
6b71bab0 185 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
b72ddf0f 186 const char *s;
c5f0532f
LP
187 int r;
188
40ca29a1 189 assert(c);
c5f0532f
LP
190 assert(bus);
191
b72ddf0f
KS
192 r = sd_bus_call_method(
193 bus,
194 "org.freedesktop.systemd1",
195 "/org/freedesktop/systemd1",
196 "org.freedesktop.systemd1.Manager",
197 "GetUnitFileState",
198 &error,
199 &reply,
200 "s",
201 "systemd-timesyncd.service");
2aa4c315 202
b72ddf0f
KS
203 if (r < 0) {
204 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
205 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") ||
206 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit"))
207 return 0;
2aa4c315 208
b72ddf0f
KS
209 return r;
210 }
c5f0532f 211
b72ddf0f
KS
212 r = sd_bus_message_read(reply, "s", &s);
213 if (r < 0)
214 return r;
40ca29a1 215
b72ddf0f
KS
216 c->can_ntp = true;
217 c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime");
c5f0532f 218
40ca29a1 219 return 0;
c5f0532f
LP
220}
221
81b84399 222static int context_start_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
c5f0532f
LP
223 int r;
224
225 assert(bus);
226 assert(error);
227
81b84399
ZJS
228 r = sd_bus_call_method(
229 bus,
230 "org.freedesktop.systemd1",
231 "/org/freedesktop/systemd1",
232 "org.freedesktop.systemd1.Manager",
233 enabled ? "StartUnit" : "StopUnit",
234 error,
235 NULL,
236 "ss",
237 "systemd-timesyncd.service",
238 "replace");
b72ddf0f
KS
239 if (r < 0) {
240 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
241 sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
7358dc02
ZJS
242 sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit"))
243 return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
ac7019f3 244
b72ddf0f 245 return r;
c5f0532f
LP
246 }
247
b72ddf0f 248 return 0;
c5f0532f
LP
249}
250
81b84399 251static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
c5f0532f 252 int r;
c5f0532f
LP
253
254 assert(bus);
255 assert(error);
256
81b84399 257 if (enabled)
40ca29a1
LP
258 r = sd_bus_call_method(
259 bus,
ac7019f3
LP
260 "org.freedesktop.systemd1",
261 "/org/freedesktop/systemd1",
262 "org.freedesktop.systemd1.Manager",
b72ddf0f 263 "EnableUnitFiles",
40ca29a1
LP
264 error,
265 NULL,
b72ddf0f
KS
266 "asbb", 1,
267 "systemd-timesyncd.service",
268 false, true);
269 else
270 r = sd_bus_call_method(
271 bus,
272 "org.freedesktop.systemd1",
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "DisableUnitFiles",
276 error,
277 NULL,
278 "asb", 1,
279 "systemd-timesyncd.service",
280 false);
ac7019f3 281
b72ddf0f 282 if (r < 0) {
7358dc02
ZJS
283 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND))
284 return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
b72ddf0f
KS
285
286 return r;
c5f0532f
LP
287 }
288
b72ddf0f
KS
289 r = sd_bus_call_method(
290 bus,
291 "org.freedesktop.systemd1",
292 "/org/freedesktop/systemd1",
293 "org.freedesktop.systemd1.Manager",
294 "Reload",
295 error,
296 NULL,
297 NULL);
298 if (r < 0)
299 return r;
300
301 return 0;
40ca29a1 302}
c5f0532f 303
6fc60278
LP
304static int property_get_rtc_time(
305 sd_bus *bus,
306 const char *path,
307 const char *interface,
308 const char *property,
309 sd_bus_message *reply,
ebcf1f97
LP
310 void *userdata,
311 sd_bus_error *error) {
6fc60278
LP
312
313 struct tm tm;
314 usec_t t;
315 int r;
316
317 zero(tm);
60989612 318 r = clock_get_hwclock(&tm);
88e262b6 319 if (r == -EBUSY) {
07a062a7 320 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
88e262b6 321 t = 0;
fe2b58a4 322 } else if (r == -ENOENT) {
07a062a7 323 log_debug("/dev/rtc not found.");
fe2b58a4 324 t = 0; /* no RTC found */
ebcf1f97 325 } else if (r < 0)
10a87006 326 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
ebcf1f97 327 else
2f6a5907 328 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
6fc60278 329
ebcf1f97 330 return sd_bus_message_append(reply, "t", t);
6fc60278
LP
331}
332
03cc26dd
LP
333static int property_get_time(
334 sd_bus *bus,
335 const char *path,
336 const char *interface,
337 const char *property,
338 sd_bus_message *reply,
ebcf1f97
LP
339 void *userdata,
340 sd_bus_error *error) {
03cc26dd 341
ebcf1f97 342 return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
03cc26dd
LP
343}
344
345static int property_get_ntp_sync(
346 sd_bus *bus,
347 const char *path,
348 const char *interface,
349 const char *property,
350 sd_bus_message *reply,
ebcf1f97
LP
351 void *userdata,
352 sd_bus_error *error) {
03cc26dd 353
ebcf1f97 354 return sd_bus_message_append(reply, "b", ntp_synced());
03cc26dd
LP
355}
356
19070062 357static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
40ca29a1
LP
358 Context *c = userdata;
359 const char *z;
102d8f81 360 int interactive;
40ca29a1
LP
361 char *t;
362 int r;
c5f0532f 363
7e9cf16c
LP
364 assert(m);
365 assert(c);
366
40ca29a1
LP
367 r = sd_bus_message_read(m, "sb", &z, &interactive);
368 if (r < 0)
ebcf1f97 369 return r;
c5f0532f 370
75683450 371 if (!timezone_is_valid(z))
ebcf1f97 372 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
ac7019f3 373
40ca29a1 374 if (streq_ptr(z, c->zone))
df2d202e 375 return sd_bus_reply_method_return(m, NULL);
c5f0532f 376
c529695e
LP
377 r = bus_verify_polkit_async(
378 m,
379 CAP_SYS_TIME,
380 "org.freedesktop.timedate1.set-timezone",
381 interactive,
382 UID_INVALID,
383 &c->polkit_registry,
384 error);
40ca29a1 385 if (r < 0)
ebcf1f97 386 return r;
40ca29a1 387 if (r == 0)
6fc60278 388 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
6ffe5e37 389
40ca29a1
LP
390 t = strdup(z);
391 if (!t)
ebcf1f97 392 return -ENOMEM;
6ffe5e37 393
40ca29a1
LP
394 free(c->zone);
395 c->zone = t;
6ffe5e37 396
40ca29a1
LP
397 /* 1. Write new configuration file */
398 r = context_write_data_timezone(c);
399 if (r < 0) {
da927ba9 400 log_error_errno(r, "Failed to set time zone: %m");
10a87006 401 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
40ca29a1 402 }
6ffe5e37 403
40ca29a1 404 /* 2. Tell the kernel our timezone */
24efb112 405 clock_set_timezone(NULL);
6ffe5e37 406
40ca29a1
LP
407 if (c->local_rtc) {
408 struct timespec ts;
409 struct tm *tm;
c5f0532f 410
40ca29a1
LP
411 /* 3. Sync RTC from system clock, with the new delta */
412 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
413 assert_se(tm = localtime(&ts.tv_sec));
60989612 414 clock_set_hwclock(tm);
40ca29a1 415 }
c5f0532f 416
40ca29a1 417 log_struct(LOG_INFO,
e2cc6eca 418 LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
40ca29a1 419 "TIMEZONE=%s", c->zone,
e2cc6eca 420 LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
40ca29a1 421 NULL);
c5f0532f 422
19070062 423 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
c5f0532f 424
df2d202e 425 return sd_bus_reply_method_return(m, NULL);
c5f0532f
LP
426}
427
19070062 428static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102d8f81 429 int lrtc, fix_system, interactive;
40ca29a1
LP
430 Context *c = userdata;
431 struct timespec ts;
f401e48c
LP
432 int r;
433
40ca29a1
LP
434 assert(m);
435 assert(c);
f401e48c 436
40ca29a1
LP
437 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
438 if (r < 0)
ebcf1f97 439 return r;
f401e48c 440
40ca29a1 441 if (lrtc == c->local_rtc)
df2d202e 442 return sd_bus_reply_method_return(m, NULL);
f401e48c 443
c529695e
LP
444 r = bus_verify_polkit_async(
445 m,
446 CAP_SYS_TIME,
447 "org.freedesktop.timedate1.set-local-rtc",
448 interactive,
449 UID_INVALID,
450 &c->polkit_registry,
451 error);
40ca29a1 452 if (r < 0)
ebcf1f97 453 return r;
40ca29a1
LP
454 if (r == 0)
455 return 1;
f401e48c 456
40ca29a1 457 c->local_rtc = lrtc;
f401e48c 458
40ca29a1
LP
459 /* 1. Write new configuration file */
460 r = context_write_data_local_rtc(c);
461 if (r < 0) {
da927ba9 462 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
10a87006 463 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m");
40ca29a1 464 }
f401e48c 465
40ca29a1 466 /* 2. Tell the kernel our timezone */
24efb112 467 clock_set_timezone(NULL);
f401e48c 468
40ca29a1
LP
469 /* 3. Synchronize clocks */
470 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
f401e48c 471
40ca29a1
LP
472 if (fix_system) {
473 struct tm tm;
f401e48c 474
40ca29a1
LP
475 /* Sync system clock from RTC; first,
476 * initialize the timezone fields of
477 * struct tm. */
478 if (c->local_rtc)
479 tm = *localtime(&ts.tv_sec);
480 else
481 tm = *gmtime(&ts.tv_sec);
72edcff5 482
40ca29a1
LP
483 /* Override the main fields of
484 * struct tm, but not the timezone
485 * fields */
60989612 486 if (clock_get_hwclock(&tm) >= 0) {
2076cf88 487
40ca29a1
LP
488 /* And set the system clock
489 * with this */
490 if (c->local_rtc)
491 ts.tv_sec = mktime(&tm);
492 else
493 ts.tv_sec = timegm(&tm);
2076cf88 494
40ca29a1 495 clock_settime(CLOCK_REALTIME, &ts);
f401e48c
LP
496 }
497
40ca29a1
LP
498 } else {
499 struct tm *tm;
f401e48c 500
40ca29a1
LP
501 /* Sync RTC from system clock */
502 if (c->local_rtc)
503 tm = localtime(&ts.tv_sec);
504 else
505 tm = gmtime(&ts.tv_sec);
2076cf88 506
60989612 507 clock_set_hwclock(tm);
40ca29a1 508 }
2076cf88 509
40ca29a1 510 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
2076cf88 511
19070062 512 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
2076cf88 513
df2d202e 514 return sd_bus_reply_method_return(m, NULL);
40ca29a1 515}
2076cf88 516
19070062 517static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102d8f81 518 int relative, interactive;
40ca29a1
LP
519 Context *c = userdata;
520 int64_t utc;
521 struct timespec ts;
2479df30 522 usec_t start;
40ca29a1
LP
523 struct tm* tm;
524 int r;
2076cf88 525
40ca29a1
LP
526 assert(m);
527 assert(c);
2076cf88 528
82d115d9
KS
529 if (c->use_ntp)
530 return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
531
6829cec4
SL
532 /* this only gets used if dbus does not provide a timestamp */
533 start = now(CLOCK_MONOTONIC);
534
40ca29a1
LP
535 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
536 if (r < 0)
ebcf1f97 537 return r;
2076cf88 538
40ca29a1 539 if (!relative && utc <= 0)
ebcf1f97 540 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
2076cf88 541
40ca29a1 542 if (relative && utc == 0)
df2d202e 543 return sd_bus_reply_method_return(m, NULL);
2076cf88 544
40ca29a1
LP
545 if (relative) {
546 usec_t n, x;
f401e48c 547
40ca29a1
LP
548 n = now(CLOCK_REALTIME);
549 x = n + utc;
f401e48c 550
40ca29a1
LP
551 if ((utc > 0 && x < n) ||
552 (utc < 0 && x > n))
ebcf1f97 553 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
f401e48c 554
40ca29a1
LP
555 timespec_store(&ts, x);
556 } else
557 timespec_store(&ts, (usec_t) utc);
2076cf88 558
c529695e
LP
559 r = bus_verify_polkit_async(
560 m,
561 CAP_SYS_TIME,
562 "org.freedesktop.timedate1.set-time",
563 interactive,
564 UID_INVALID,
565 &c->polkit_registry,
566 error);
40ca29a1 567 if (r < 0)
ebcf1f97 568 return r;
40ca29a1
LP
569 if (r == 0)
570 return 1;
571
2479df30
SL
572 /* adjust ts for time spent in program */
573 r = sd_bus_message_get_monotonic_usec(m, &start);
6829cec4 574 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
2479df30
SL
575 if (r < 0 && r != -ENODATA)
576 return r;
6829cec4
SL
577
578 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
2479df30 579
40ca29a1
LP
580 /* Set system clock */
581 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
56f64d95 582 log_error_errno(errno, "Failed to set local time: %m");
ebcf1f97 583 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
40ca29a1 584 }
2076cf88 585
40ca29a1
LP
586 /* Sync down to RTC */
587 if (c->local_rtc)
588 tm = localtime(&ts.tv_sec);
589 else
590 tm = gmtime(&ts.tv_sec);
60989612 591 clock_set_hwclock(tm);
f401e48c 592
40ca29a1 593 log_struct(LOG_INFO,
e2cc6eca 594 LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
de0671ee 595 "REALTIME="USEC_FMT, timespec_load(&ts),
e2cc6eca 596 LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
40ca29a1 597 NULL);
f401e48c 598
df2d202e 599 return sd_bus_reply_method_return(m, NULL);
40ca29a1 600}
f401e48c 601
19070062 602static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
81b84399 603 int enabled, interactive;
40ca29a1
LP
604 Context *c = userdata;
605 int r;
f401e48c 606
19070062
LP
607 assert(m);
608 assert(c);
609
81b84399 610 r = sd_bus_message_read(m, "bb", &enabled, &interactive);
40ca29a1 611 if (r < 0)
ebcf1f97 612 return r;
f401e48c 613
81b84399 614 if ((bool)enabled == c->use_ntp)
df2d202e 615 return sd_bus_reply_method_return(m, NULL);
f401e48c 616
c529695e
LP
617 r = bus_verify_polkit_async(
618 m,
619 CAP_SYS_TIME,
620 "org.freedesktop.timedate1.set-ntp",
621 interactive,
622 UID_INVALID,
623 &c->polkit_registry,
624 error);
40ca29a1 625 if (r < 0)
ebcf1f97 626 return r;
40ca29a1
LP
627 if (r == 0)
628 return 1;
f401e48c 629
19070062 630 r = context_enable_ntp(sd_bus_message_get_bus(m), error, enabled);
40ca29a1 631 if (r < 0)
ebcf1f97 632 return r;
40ca29a1 633
19070062 634 r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled);
40ca29a1 635 if (r < 0)
ebcf1f97 636 return r;
f401e48c 637
81b84399
ZJS
638 c->use_ntp = enabled;
639 log_info("Set NTP to %s", enabled ? "enabled" : "disabled");
f401e48c 640
19070062 641 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
f401e48c 642
df2d202e 643 return sd_bus_reply_method_return(m, NULL);
f401e48c
LP
644}
645
40ca29a1
LP
646static const sd_bus_vtable timedate_vtable[] = {
647 SD_BUS_VTABLE_START(0),
648 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
82d115d9
KS
649 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
650 SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0),
651 SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
03cc26dd
LP
652 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
653 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
6fc60278 654 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
adacb957
LP
655 SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
656 SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
657 SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
658 SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
40ca29a1
LP
659 SD_BUS_VTABLE_END,
660};
661
662static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
03976f7b 663 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
f401e48c
LP
664 int r;
665
40ca29a1
LP
666 assert(c);
667 assert(event);
f401e48c
LP
668 assert(_bus);
669
76b54375 670 r = sd_bus_default_system(&bus);
f647962d
MS
671 if (r < 0)
672 return log_error_errno(r, "Failed to get system bus connection: %m");
f401e48c 673
19befb2d 674 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
f647962d
MS
675 if (r < 0)
676 return log_error_errno(r, "Failed to register object: %m");
f401e48c 677
5bb658a1 678 r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
f647962d
MS
679 if (r < 0)
680 return log_error_errno(r, "Failed to register name: %m");
add10b5a 681
40ca29a1 682 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
683 if (r < 0)
684 return log_error_errno(r, "Failed to attach bus to event loop: %m");
f401e48c 685
40ca29a1
LP
686 *_bus = bus;
687 bus = NULL;
f401e48c 688
40ca29a1 689 return 0;
f401e48c
LP
690}
691
692int main(int argc, char *argv[]) {
82d115d9 693 Context context = {};
40ca29a1 694 _cleanup_event_unref_ sd_event *event = NULL;
03976f7b 695 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
f401e48c 696 int r;
f401e48c
LP
697
698 log_set_target(LOG_TARGET_AUTO);
699 log_parse_environment();
700 log_open();
701
4c12626c
LP
702 umask(0022);
703
f401e48c
LP
704 if (argc != 1) {
705 log_error("This program takes no arguments.");
706 r = -EINVAL;
707 goto finish;
708 }
709
afc6adb5 710 r = sd_event_default(&event);
f401e48c 711 if (r < 0) {
da927ba9 712 log_error_errno(r, "Failed to allocate event loop: %m");
f401e48c
LP
713 goto finish;
714 }
715
cde93897
LP
716 sd_event_set_watchdog(event, true);
717
40ca29a1 718 r = connect_bus(&context, event, &bus);
f401e48c
LP
719 if (r < 0)
720 goto finish;
721
dc751688 722 (void) sd_bus_negotiate_timestamp(bus, true);
2479df30 723
40ca29a1 724 r = context_read_data(&context);
c5f0532f 725 if (r < 0) {
da927ba9 726 log_error_errno(r, "Failed to read time zone data: %m");
c5f0532f
LP
727 goto finish;
728 }
729
40ca29a1
LP
730 r = context_read_ntp(&context, bus);
731 if (r < 0) {
da927ba9 732 log_error_errno(r, "Failed to determine whether NTP is enabled: %m");
40ca29a1
LP
733 goto finish;
734 }
ad740100 735
37224a5f 736 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
40ca29a1 737 if (r < 0) {
da927ba9 738 log_error_errno(r, "Failed to run event loop: %m");
40ca29a1 739 goto finish;
ad740100 740 }
f401e48c 741
f401e48c 742finish:
36e34057 743 context_free(&context);
f401e48c
LP
744
745 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
746}