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