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