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