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