]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
treewide: use log_*_errno whenever %m is in the format string
[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
fedfcdee 47SD_BUS_ERROR_MAPPING(timedated) = {
7358dc02
ZJS
48 {"org.freedesktop.timedate1.NoNTPSupport", ENOTSUP},
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
92c4ef2d
SL
72 r = readlink_malloc("/etc/localtime", &t);
73 if (r < 0) {
74 if (r == -EINVAL)
07a062a7 75 log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
92c4ef2d 76 else
da927ba9 77 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
92c4ef2d 78 } else {
424a19f8 79 const char *e;
92c4ef2d 80
424a19f8
LP
81 e = path_startswith(t, "/usr/share/zoneinfo/");
82 if (!e)
83 e = path_startswith(t, "../usr/share/zoneinfo/");
84
85 if (!e)
07a062a7 86 log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
92c4ef2d 87 else {
40ca29a1
LP
88 c->zone = strdup(e);
89 if (!c->zone)
92c4ef2d
SL
90 return log_oom();
91
92 goto have_timezone;
93 }
94 }
95
92c4ef2d 96have_timezone:
40ca29a1
LP
97 if (isempty(c->zone)) {
98 free(c->zone);
99 c->zone = NULL;
a724d2ed 100 }
f401e48c 101
24efb112 102 c->local_rtc = clock_is_localtime() > 0;
f401e48c
LP
103
104 return 0;
105}
106
40ca29a1 107static int context_write_data_timezone(Context *c) {
424a19f8 108 _cleanup_free_ char *p = NULL;
40ca29a1
LP
109 int r = 0;
110
111 assert(c);
e19a21a8 112
40ca29a1 113 if (isempty(c->zone)) {
424a19f8 114 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
f401e48c
LP
115 r = -errno;
116
f401e48c
LP
117 return r;
118 }
119
40ca29a1 120 p = strappend("../usr/share/zoneinfo/", c->zone);
0d0f0c50
SL
121 if (!p)
122 return log_oom();
f401e48c 123
424a19f8 124 r = symlink_atomic(p, "/etc/localtime");
f401e48c 125 if (r < 0)
424a19f8 126 return r;
f401e48c 127
f401e48c
LP
128 return 0;
129}
130
40ca29a1 131static int context_write_data_local_rtc(Context *c) {
f401e48c 132 int r;
7fd1b19b 133 _cleanup_free_ char *s = NULL, *w = NULL;
f401e48c 134
40ca29a1
LP
135 assert(c);
136
f401e48c
LP
137 r = read_full_file("/etc/adjtime", &s, NULL);
138 if (r < 0) {
139 if (r != -ENOENT)
140 return r;
141
40ca29a1 142 if (!c->local_rtc)
f401e48c
LP
143 return 0;
144
145 w = strdup(NULL_ADJTIME_LOCAL);
146 if (!w)
147 return -ENOMEM;
148 } else {
149 char *p, *e;
150 size_t a, b;
151
152 p = strchr(s, '\n');
d257f05a 153 if (!p)
f401e48c 154 return -EIO;
f401e48c
LP
155
156 p = strchr(p+1, '\n');
d257f05a 157 if (!p)
f401e48c 158 return -EIO;
f401e48c
LP
159
160 p++;
161 e = strchr(p, '\n');
d257f05a 162 if (!e)
f401e48c 163 return -EIO;
f401e48c
LP
164
165 a = p - s;
166 b = strlen(e);
167
40ca29a1 168 w = new(char, a + (c->local_rtc ? 5 : 3) + b + 1);
d257f05a 169 if (!w)
f401e48c 170 return -ENOMEM;
f401e48c 171
40ca29a1 172 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
f401e48c
LP
173
174 if (streq(w, NULL_ADJTIME_UTC)) {
d257f05a 175 if (unlink("/etc/adjtime") < 0)
f401e48c
LP
176 if (errno != ENOENT)
177 return -errno;
f401e48c
LP
178
179 return 0;
180 }
181 }
40ca29a1 182
cc56fafe 183 mac_selinux_init("/etc");
d257f05a 184 return write_string_file_atomic_label("/etc/adjtime", w);
f401e48c
LP
185}
186
40ca29a1 187static int context_read_ntp(Context *c, sd_bus *bus) {
b72ddf0f
KS
188 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
189 sd_bus_message *reply = NULL;
190 const char *s;
c5f0532f
LP
191 int r;
192
40ca29a1 193 assert(c);
c5f0532f
LP
194 assert(bus);
195
b72ddf0f
KS
196 r = sd_bus_call_method(
197 bus,
198 "org.freedesktop.systemd1",
199 "/org/freedesktop/systemd1",
200 "org.freedesktop.systemd1.Manager",
201 "GetUnitFileState",
202 &error,
203 &reply,
204 "s",
205 "systemd-timesyncd.service");
2aa4c315 206
b72ddf0f
KS
207 if (r < 0) {
208 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
209 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") ||
210 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit"))
211 return 0;
2aa4c315 212
b72ddf0f
KS
213 return r;
214 }
c5f0532f 215
b72ddf0f
KS
216 r = sd_bus_message_read(reply, "s", &s);
217 if (r < 0)
218 return r;
40ca29a1 219
b72ddf0f
KS
220 c->can_ntp = true;
221 c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime");
c5f0532f 222
40ca29a1 223 return 0;
c5f0532f
LP
224}
225
40ca29a1 226static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
c5f0532f
LP
227 int r;
228
40ca29a1 229 assert(c);
c5f0532f
LP
230 assert(bus);
231 assert(error);
232
b72ddf0f
KS
233 if (c->use_ntp)
234 r = sd_bus_call_method(
235 bus,
236 "org.freedesktop.systemd1",
237 "/org/freedesktop/systemd1",
238 "org.freedesktop.systemd1.Manager",
239 "StartUnit",
240 error,
241 NULL,
242 "ss",
243 "systemd-timesyncd.service",
244 "replace");
245 else
246 r = sd_bus_call_method(
247 bus,
248 "org.freedesktop.systemd1",
249 "/org/freedesktop/systemd1",
250 "org.freedesktop.systemd1.Manager",
251 "StopUnit",
252 error,
253 NULL,
254 "ss",
255 "systemd-timesyncd.service",
256 "replace");
c5f0532f 257
b72ddf0f
KS
258 if (r < 0) {
259 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
260 sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
7358dc02
ZJS
261 sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit"))
262 return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
ac7019f3 263
b72ddf0f 264 return r;
c5f0532f
LP
265 }
266
b72ddf0f 267 return 0;
c5f0532f
LP
268}
269
40ca29a1 270static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) {
c5f0532f 271 int r;
c5f0532f 272
40ca29a1 273 assert(c);
c5f0532f
LP
274 assert(bus);
275 assert(error);
276
b72ddf0f 277 if (c->use_ntp)
40ca29a1
LP
278 r = sd_bus_call_method(
279 bus,
ac7019f3
LP
280 "org.freedesktop.systemd1",
281 "/org/freedesktop/systemd1",
282 "org.freedesktop.systemd1.Manager",
b72ddf0f 283 "EnableUnitFiles",
40ca29a1
LP
284 error,
285 NULL,
b72ddf0f
KS
286 "asbb", 1,
287 "systemd-timesyncd.service",
288 false, true);
289 else
290 r = sd_bus_call_method(
291 bus,
292 "org.freedesktop.systemd1",
293 "/org/freedesktop/systemd1",
294 "org.freedesktop.systemd1.Manager",
295 "DisableUnitFiles",
296 error,
297 NULL,
298 "asb", 1,
299 "systemd-timesyncd.service",
300 false);
ac7019f3 301
b72ddf0f 302 if (r < 0) {
7358dc02
ZJS
303 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND))
304 return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
b72ddf0f
KS
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
f3885791 398 r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-timezone", interactive, &c->polkit_registry, error);
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) {
da927ba9 414 log_error_errno(r, "Failed to set time zone: %m");
07a062a7 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 431 log_struct(LOG_INFO,
e2cc6eca 432 LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
40ca29a1 433 "TIMEZONE=%s", c->zone,
e2cc6eca 434 LOG_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
f3885791 459 r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-local-rtc", interactive, &c->polkit_registry, error);
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) {
da927ba9 470 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
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
f3885791 564 r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-time", interactive, &c->polkit_registry, error);
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) {
56f64d95 572 log_error_errno(errno, "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 583 log_struct(LOG_INFO,
e2cc6eca 584 LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
de0671ee 585 "REALTIME="USEC_FMT, timespec_load(&ts),
e2cc6eca 586 LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
40ca29a1 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
f3885791 604 r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-ntp", interactive, &c->polkit_registry, error);
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);
f647962d
MS
652 if (r < 0)
653 return log_error_errno(r, "Failed to get system bus connection: %m");
f401e48c 654
19befb2d 655 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
f647962d
MS
656 if (r < 0)
657 return log_error_errno(r, "Failed to register object: %m");
f401e48c 658
5bb658a1 659 r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
f647962d
MS
660 if (r < 0)
661 return log_error_errno(r, "Failed to register name: %m");
add10b5a 662
40ca29a1 663 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
664 if (r < 0)
665 return log_error_errno(r, "Failed to attach bus to event loop: %m");
f401e48c 666
40ca29a1
LP
667 *_bus = bus;
668 bus = NULL;
f401e48c 669
40ca29a1 670 return 0;
f401e48c
LP
671}
672
673int main(int argc, char *argv[]) {
82d115d9 674 Context context = {};
40ca29a1 675 _cleanup_event_unref_ sd_event *event = NULL;
24996861 676 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
f401e48c 677 int r;
f401e48c
LP
678
679 log_set_target(LOG_TARGET_AUTO);
680 log_parse_environment();
681 log_open();
682
4c12626c
LP
683 umask(0022);
684
f401e48c
LP
685 if (argc != 1) {
686 log_error("This program takes no arguments.");
687 r = -EINVAL;
688 goto finish;
689 }
690
afc6adb5 691 r = sd_event_default(&event);
f401e48c 692 if (r < 0) {
da927ba9 693 log_error_errno(r, "Failed to allocate event loop: %m");
f401e48c
LP
694 goto finish;
695 }
696
cde93897
LP
697 sd_event_set_watchdog(event, true);
698
40ca29a1 699 r = connect_bus(&context, event, &bus);
f401e48c
LP
700 if (r < 0)
701 goto finish;
702
40ca29a1 703 r = context_read_data(&context);
c5f0532f 704 if (r < 0) {
da927ba9 705 log_error_errno(r, "Failed to read time zone data: %m");
c5f0532f
LP
706 goto finish;
707 }
708
40ca29a1
LP
709 r = context_read_ntp(&context, bus);
710 if (r < 0) {
da927ba9 711 log_error_errno(r, "Failed to determine whether NTP is enabled: %m");
40ca29a1
LP
712 goto finish;
713 }
ad740100 714
37224a5f 715 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
40ca29a1 716 if (r < 0) {
da927ba9 717 log_error_errno(r, "Failed to run event loop: %m");
40ca29a1 718 goto finish;
ad740100 719 }
f401e48c 720
f401e48c 721finish:
36e34057 722 context_free(&context);
f401e48c
LP
723
724 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
725}