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