]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
Merge pull request #18701 from bugaevc/mdns-unicast
[thirdparty/systemd.git] / src / timedate / timedated.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
f401e48c 2
f401e48c 3#include <errno.h>
ca78ad1d
ZJS
4#include <sys/stat.h>
5#include <sys/types.h>
f401e48c
LP
6#include <unistd.h>
7
40ca29a1 8#include "sd-bus.h"
f4f15635
LP
9#include "sd-event.h"
10#include "sd-messages.h"
40ca29a1 11
b5efdb8a 12#include "alloc-util.h"
96aad8d1 13#include "bus-common-errors.h"
f4f15635 14#include "bus-error.h"
40af3d02 15#include "bus-get-properties.h"
9b71e4ab 16#include "bus-locator.h"
ac9f55ed 17#include "bus-log-control-api.h"
807542be 18#include "bus-map-properties.h"
269e4d2d 19#include "bus-polkit.h"
f4f15635 20#include "clock-util.h"
afaae43b 21#include "conf-files.h"
f4f15635 22#include "def.h"
afaae43b 23#include "fd-util.h"
f4f15635 24#include "fileio-label.h"
e4de7287 25#include "fileio.h"
f4f15635 26#include "fs-util.h"
5d280742
YW
27#include "hashmap.h"
28#include "list.h"
1f47bc33 29#include "main-func.h"
0a970718 30#include "memory-util.h"
36dd5ffd 31#include "missing_capability.h"
f4f15635 32#include "path-util.h"
d7b8eec7 33#include "selinux-util.h"
fc021a5b 34#include "service-util.h"
754f0269 35#include "signal-util.h"
5d280742 36#include "string-util.h"
f4f15635 37#include "strv.h"
5d280742
YW
38#include "unit-def.h"
39#include "unit-name.h"
ee104e11 40#include "user-util.h"
f401e48c
LP
41
42#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
43#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
44
afaae43b
ZJS
45#define UNIT_LIST_DIRS (const char* const*) CONF_PATHS_STRV("systemd/ntp-units.d")
46
5d280742
YW
47typedef struct UnitStatusInfo {
48 char *name;
49 char *load_state;
50 char *unit_file_state;
51 char *active_state;
cf3872bd 52 char *path;
5d280742
YW
53
54 LIST_FIELDS(struct UnitStatusInfo, units);
55} UnitStatusInfo;
56
40ca29a1 57typedef struct Context {
d200735e
MS
58 char *zone;
59 bool local_rtc;
40ca29a1 60 Hashmap *polkit_registry;
2770af85 61 sd_bus_message *cache;
5d280742 62
3af0a96c 63 sd_bus_slot *slot_job_removed;
3af0a96c 64
5d280742 65 LIST_HEAD(UnitStatusInfo, units);
40ca29a1 66} Context;
f401e48c 67
ad7fb943
ZJS
68#define log_unit_full(unit, level, error, ...) \
69 ({ \
70 const UnitStatusInfo *_u = (unit); \
71 log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, \
72 "UNIT=", _u->name, NULL, NULL, ##__VA_ARGS__); \
73 })
74
75#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__)
76#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, 0, ##__VA_ARGS__)
77#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, 0, ##__VA_ARGS__)
78#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, 0, ##__VA_ARGS__)
79#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, 0, ##__VA_ARGS__)
80
81#define log_unit_debug_errno(unit, error, ...) log_unit_full(unit, LOG_DEBUG, error, ##__VA_ARGS__)
82#define log_unit_info_errno(unit, error, ...) log_unit_full(unit, LOG_INFO, error, ##__VA_ARGS__)
83#define log_unit_notice_errno(unit, error, ...) log_unit_full(unit, LOG_NOTICE, error, ##__VA_ARGS__)
84#define log_unit_warning_errno(unit, error, ...) log_unit_full(unit, LOG_WARNING, error, ##__VA_ARGS__)
85#define log_unit_error_errno(unit, error, ...) log_unit_full(unit, LOG_ERR, error, ##__VA_ARGS__)
86
5d280742
YW
87static void unit_status_info_clear(UnitStatusInfo *p) {
88 assert(p);
89
90 p->load_state = mfree(p->load_state);
91 p->unit_file_state = mfree(p->unit_file_state);
92 p->active_state = mfree(p->active_state);
93}
94
95static void unit_status_info_free(UnitStatusInfo *p) {
96 assert(p);
97
98 unit_status_info_clear(p);
99 free(p->name);
cf3872bd 100 free(p->path);
5d280742
YW
101 free(p);
102}
103
1f47bc33 104static void context_clear(Context *c) {
5d280742
YW
105 UnitStatusInfo *p;
106
40ca29a1 107 assert(c);
f401e48c 108
82d115d9 109 free(c->zone);
36e34057 110 bus_verify_polkit_async_registry_free(c->polkit_registry);
2770af85 111 sd_bus_message_unref(c->cache);
5d280742 112
3af0a96c 113 sd_bus_slot_unref(c->slot_job_removed);
3af0a96c 114
5d280742
YW
115 while ((p = c->units)) {
116 LIST_REMOVE(units, c->units, p);
117 unit_status_info_free(p);
118 }
119}
120
ad7fb943 121static int context_add_ntp_service(Context *c, const char *s, const char *source) {
5d280742
YW
122 UnitStatusInfo *u;
123
124 if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
125 return -EINVAL;
126
127 /* Do not add this if it is already listed */
128 LIST_FOREACH(units, u, c->units)
129 if (streq(u->name, s))
130 return 0;
131
132 u = new0(UnitStatusInfo, 1);
133 if (!u)
134 return -ENOMEM;
135
136 u->name = strdup(s);
137 if (!u->name) {
138 free(u);
139 return -ENOMEM;
140 }
141
142 LIST_APPEND(units, c->units, u);
ad7fb943 143 log_unit_debug(u, "added from %s.", source);
5d280742
YW
144
145 return 0;
146}
147
afaae43b 148static int context_parse_ntp_services_from_environment(Context *c) {
5d280742
YW
149 const char *env, *p;
150 int r;
151
152 assert(c);
153
154 env = getenv("SYSTEMD_TIMEDATED_NTP_SERVICES");
afaae43b 155 if (!env)
5d280742 156 return 0;
afaae43b 157
ad7fb943 158 log_debug("Using list of ntp services from environment variable $SYSTEMD_TIMEDATED_NTP_SERVICES=%s.", env);
5d280742
YW
159
160 for (p = env;;) {
161 _cleanup_free_ char *word = NULL;
162
163 r = extract_first_word(&p, &word, ":", 0);
164 if (r == 0)
165 break;
166 if (r == -ENOMEM)
167 return log_oom();
168 if (r < 0) {
169 log_error("Invalid syntax, ignoring: %s", env);
170 break;
171 }
172
ad7fb943 173 r = context_add_ntp_service(c, word, "$SYSTEMD_TIMEDATED_NTP_SERVICES");
5d280742
YW
174 if (r < 0)
175 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
176 }
177
afaae43b
ZJS
178 return 1;
179}
180
181static int context_parse_ntp_services_from_disk(Context *c) {
182 _cleanup_strv_free_ char **files = NULL;
183 char **f;
184 int r;
185
186 r = conf_files_list_strv(&files, ".list", NULL, CONF_FILES_FILTER_MASKED, UNIT_LIST_DIRS);
187 if (r < 0)
188 return log_error_errno(r, "Failed to enumerate .list files: %m");
189
190 STRV_FOREACH(f, files) {
191 _cleanup_fclose_ FILE *file = NULL;
192
193 log_debug("Reading file '%s'", *f);
194
195 r = fopen_unlocked(*f, "re", &file);
196 if (r < 0) {
197 log_error_errno(r, "Failed to open %s, ignoring: %m", *f);
198 continue;
199 }
200
201 for (;;) {
202 _cleanup_free_ char *line = NULL;
203 const char *word;
204
205 r = read_line(file, LINE_MAX, &line);
206 if (r < 0) {
207 log_error_errno(r, "Failed to read %s, ignoring: %m", *f);
208 continue;
209 }
210 if (r == 0)
211 break;
212
213 word = strstrip(line);
03a81441 214 if (isempty(word) || startswith(word, "#"))
afaae43b
ZJS
215 continue;
216
ad7fb943 217 r = context_add_ntp_service(c, word, *f);
afaae43b
ZJS
218 if (r < 0)
219 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
220 }
221 }
222
223 return 1;
224}
225
226static int context_parse_ntp_services(Context *c) {
227 int r;
228
229 r = context_parse_ntp_services_from_environment(c);
230 if (r != 0)
231 return r;
232
233 return context_parse_ntp_services_from_disk(c);
5d280742
YW
234}
235
236static int context_ntp_service_is_active(Context *c) {
237 UnitStatusInfo *info;
238 int count = 0;
239
240 assert(c);
241
242 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
243
244 LIST_FOREACH(units, info, c->units)
84a87726 245 count += !STRPTR_IN_SET(info->active_state, "inactive", "failed");
5d280742
YW
246
247 return count;
248}
249
5d280742
YW
250static int context_ntp_service_exists(Context *c) {
251 UnitStatusInfo *info;
252 int count = 0;
253
254 assert(c);
255
256 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
257
258 LIST_FOREACH(units, info, c->units)
259 count += streq_ptr(info->load_state, "loaded");
260
261 return count;
f401e48c
LP
262}
263
40ca29a1 264static int context_read_data(Context *c) {
424a19f8 265 _cleanup_free_ char *t = NULL;
40ca29a1
LP
266 int r;
267
268 assert(c);
f401e48c 269
5c904ba5
LP
270 r = get_timezone(&t);
271 if (r == -EINVAL)
272 log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
273 else if (r < 0)
274 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
92c4ef2d 275
f9ecfd3b 276 free_and_replace(c->zone, t);
f401e48c 277
6369641d 278 c->local_rtc = clock_is_localtime(NULL) > 0;
f401e48c
LP
279
280 return 0;
281}
282
40ca29a1 283static int context_write_data_timezone(Context *c) {
424a19f8 284 _cleanup_free_ char *p = NULL;
9193af0f 285 const char *source;
40ca29a1
LP
286
287 assert(c);
e19a21a8 288
9193af0f
LP
289 /* No timezone is very similar to UTC. Hence in either of these cases link the UTC file in. Except if
290 * it isn't installed, in which case we remove the symlink altogether. Since glibc defaults to an
291 * internal version of UTC in that case behaviour is mostly equivalent. We still prefer creating the
292 * symlink though, since things are more self explanatory then. */
293
294 if (isempty(c->zone) || streq(c->zone, "UTC")) {
295
296 if (access("/usr/share/zoneinfo/UTC", F_OK) < 0) {
297
298 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
299 return -errno;
f401e48c 300
9193af0f
LP
301 return 0;
302 }
303
304 source = "../usr/share/zoneinfo/UTC";
305 } else {
306 p = path_join("../usr/share/zoneinfo", c->zone);
307 if (!p)
308 return -ENOMEM;
309
310 source = p;
311 }
f401e48c 312
9193af0f 313 return symlink_atomic(source, "/etc/localtime");
f401e48c
LP
314}
315
40ca29a1 316static int context_write_data_local_rtc(Context *c) {
7fd1b19b 317 _cleanup_free_ char *s = NULL, *w = NULL;
d9cb4bba 318 int r;
f401e48c 319
40ca29a1
LP
320 assert(c);
321
f401e48c
LP
322 r = read_full_file("/etc/adjtime", &s, NULL);
323 if (r < 0) {
324 if (r != -ENOENT)
325 return r;
326
40ca29a1 327 if (!c->local_rtc)
f401e48c
LP
328 return 0;
329
330 w = strdup(NULL_ADJTIME_LOCAL);
331 if (!w)
332 return -ENOMEM;
333 } else {
c9410dd4 334 char *p;
8f462d87 335 const char *e = "\n"; /* default if there is less than 3 lines */
c9410dd4 336 const char *prepend = "";
f401e48c
LP
337 size_t a, b;
338
c9410dd4 339 p = strchrnul(s, '\n');
cb971cc0 340 if (*p == '\0')
c9410dd4
MP
341 /* only one line, no \n terminator */
342 prepend = "\n0\n";
cb971cc0 343 else if (p[1] == '\0') {
c9410dd4
MP
344 /* only one line, with \n terminator */
345 ++p;
346 prepend = "0\n";
347 } else {
348 p = strchr(p+1, '\n');
349 if (!p) {
350 /* only two lines, no \n terminator */
351 prepend = "\n";
352 p = s + strlen(s);
353 } else {
354 char *end;
355 /* third line might have a \n terminator or not */
356 p++;
357 end = strchr(p, '\n');
358 /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */
359 if (end)
360 e = end;
361 }
362 }
f401e48c
LP
363
364 a = p - s;
365 b = strlen(e);
366
c9410dd4 367 w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1);
d257f05a 368 if (!w)
f401e48c 369 return -ENOMEM;
f401e48c 370
c9410dd4 371 *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
f401e48c
LP
372
373 if (streq(w, NULL_ADJTIME_UTC)) {
d257f05a 374 if (unlink("/etc/adjtime") < 0)
f401e48c
LP
375 if (errno != ENOENT)
376 return -errno;
f401e48c
LP
377
378 return 0;
379 }
380 }
40ca29a1 381
a9ba0e32
CG
382 r = mac_selinux_init();
383 if (r < 0)
384 return r;
385
d257f05a 386 return write_string_file_atomic_label("/etc/adjtime", w);
f401e48c
LP
387}
388
5d280742
YW
389static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) {
390 static const struct bus_properties_map map[] = {
391 { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
392 { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
393 { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
394 {}
395 };
5d280742 396 UnitStatusInfo *u;
c5f0532f
LP
397 int r;
398
40ca29a1 399 assert(c);
c5f0532f
LP
400 assert(bus);
401
2770af85
YW
402 /* Suppress calling context_update_ntp_status() multiple times within single DBus transaction. */
403 if (m) {
404 if (m == c->cache)
405 return 0;
2aa4c315 406
2770af85
YW
407 sd_bus_message_unref(c->cache);
408 c->cache = sd_bus_message_ref(m);
409 }
2aa4c315 410
5d280742
YW
411 LIST_FOREACH(units, u, c->units) {
412 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5d280742 413 _cleanup_free_ char *path = NULL;
c5f0532f 414
5d280742 415 unit_status_info_clear(u);
40ca29a1 416
5d280742
YW
417 path = unit_dbus_path_from_name(u->name);
418 if (!path)
419 return -ENOMEM;
420
421 r = bus_map_all_properties(
422 bus,
423 "org.freedesktop.systemd1",
424 path,
425 map,
426 BUS_MAP_STRDUP,
427 &error,
428 NULL,
429 u);
430 if (r < 0)
ad7fb943 431 return log_unit_error_errno(u, r, "Failed to get properties: %s", bus_error_message(&error, r));
5d280742 432 }
c5f0532f 433
40ca29a1 434 return 0;
c5f0532f
LP
435}
436
3af0a96c 437static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
3af0a96c 438 Context *c = userdata;
cf3872bd
YW
439 UnitStatusInfo *u;
440 const char *path;
441 unsigned n = 0;
3af0a96c
YW
442 int r;
443
444 assert(c);
445 assert(m);
446
447 r = sd_bus_message_read(m, "uoss", NULL, &path, NULL, NULL);
448 if (r < 0) {
449 bus_log_parse_error(r);
450 return 0;
451 }
452
cf3872bd
YW
453 LIST_FOREACH(units, u, c->units)
454 if (streq_ptr(path, u->path))
455 u->path = mfree(u->path);
456 else
457 n += !!u->path;
3af0a96c 458
cf3872bd 459 if (n == 0) {
cf3872bd 460 c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
49942d6b 461
ad7fb943
ZJS
462 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
463 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP",
464 NULL);
cf3872bd 465 }
3af0a96c
YW
466
467 return 0;
468}
469
cf3872bd 470static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
3af0a96c 471 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3af0a96c 472 const char *path;
c5f0532f
LP
473 int r;
474
5d280742 475 assert(u);
c5f0532f
LP
476 assert(bus);
477 assert(error);
478
43fe4f76 479 r = bus_call_method(
81b84399 480 bus,
43fe4f76 481 bus_systemd_mgr,
5d280742 482 start ? "StartUnit" : "StopUnit",
81b84399 483 error,
3af0a96c 484 &reply,
81b84399 485 "ss",
5d280742 486 u->name,
81b84399 487 "replace");
ad7fb943
ZJS
488 log_unit_full(u, r < 0 ? LOG_WARNING : LOG_DEBUG, r,
489 "%s unit: %m", start ? "Starting" : "Stopping");
5d280742 490 if (r < 0)
b72ddf0f 491 return r;
c5f0532f 492
3af0a96c
YW
493 r = sd_bus_message_read(reply, "o", &path);
494 if (r < 0)
495 return bus_log_parse_error(r);
496
cf3872bd 497 r = free_and_strdup(&u->path, path);
3af0a96c
YW
498 if (r < 0)
499 return log_oom();
500
b72ddf0f 501 return 0;
c5f0532f
LP
502}
503
5d280742 504static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool enable) {
c5f0532f 505 int r;
c5f0532f 506
5d280742 507 assert(u);
c5f0532f
LP
508 assert(bus);
509 assert(error);
510
5d280742
YW
511 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
512
ad7fb943
ZJS
513 if (streq(u->unit_file_state, "enabled") == enable) {
514 log_unit_debug(u, "already %sd.", enable_disable(enable));
5d280742 515 return 0;
ad7fb943
ZJS
516 }
517
518 log_unit_info(u, "%s unit.", enable ? "Enabling" : "Disabling");
5d280742
YW
519
520 if (enable)
43fe4f76 521 r = bus_call_method(
40ca29a1 522 bus,
43fe4f76 523 bus_systemd_mgr,
b72ddf0f 524 "EnableUnitFiles",
40ca29a1
LP
525 error,
526 NULL,
b72ddf0f 527 "asbb", 1,
5d280742 528 u->name,
b72ddf0f
KS
529 false, true);
530 else
43fe4f76 531 r = bus_call_method(
b72ddf0f 532 bus,
43fe4f76 533 bus_systemd_mgr,
b72ddf0f
KS
534 "DisableUnitFiles",
535 error,
536 NULL,
537 "asb", 1,
5d280742 538 u->name,
b72ddf0f 539 false);
5d280742 540 if (r < 0)
b72ddf0f 541 return r;
c5f0532f 542
43fe4f76 543 r = bus_call_method(bus, bus_systemd_mgr, "Reload", error, NULL, NULL);
3af0a96c
YW
544 if (r < 0)
545 return r;
546
b72ddf0f 547 return 0;
40ca29a1 548}
c5f0532f 549
6cc379b5
YW
550static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_time, "t", now(CLOCK_REALTIME));
551static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_ntp_sync, "b", ntp_synced());
552
6fc60278
LP
553static int property_get_rtc_time(
554 sd_bus *bus,
555 const char *path,
556 const char *interface,
557 const char *property,
558 sd_bus_message *reply,
ebcf1f97
LP
559 void *userdata,
560 sd_bus_error *error) {
6fc60278 561
d9cb4bba
ZJS
562 struct tm tm = {};
563 usec_t t = 0;
6fc60278
LP
564 int r;
565
60989612 566 r = clock_get_hwclock(&tm);
d9cb4bba 567 if (r == -EBUSY)
07a062a7 568 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
d9cb4bba 569 else if (r == -ENOENT)
07a062a7 570 log_debug("/dev/rtc not found.");
d9cb4bba 571 else if (r < 0)
10a87006 572 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
ebcf1f97 573 else
2f6a5907 574 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
6fc60278 575
ebcf1f97 576 return sd_bus_message_append(reply, "t", t);
6fc60278
LP
577}
578
5d280742
YW
579static int property_get_can_ntp(
580 sd_bus *bus,
581 const char *path,
582 const char *interface,
583 const char *property,
584 sd_bus_message *reply,
585 void *userdata,
586 sd_bus_error *error) {
587
588 Context *c = userdata;
589 int r;
590
591 assert(c);
592 assert(bus);
593 assert(property);
594 assert(reply);
595 assert(error);
596
b4356b57
YW
597 if (c->slot_job_removed)
598 /* When the previous request is not finished, then assume NTP is enabled. */
599 return sd_bus_message_append(reply, "b", true);
600
5d280742
YW
601 r = context_update_ntp_status(c, bus, reply);
602 if (r < 0)
603 return r;
604
605 return sd_bus_message_append(reply, "b", context_ntp_service_exists(c) > 0);
606}
607
608static int property_get_ntp(
609 sd_bus *bus,
610 const char *path,
611 const char *interface,
612 const char *property,
613 sd_bus_message *reply,
614 void *userdata,
615 sd_bus_error *error) {
616
617 Context *c = userdata;
618 int r;
619
620 assert(c);
621 assert(bus);
622 assert(property);
623 assert(reply);
624 assert(error);
625
b4356b57
YW
626 if (c->slot_job_removed)
627 /* When the previous request is not finished, then assume NTP is active. */
628 return sd_bus_message_append(reply, "b", true);
629
5d280742
YW
630 r = context_update_ntp_status(c, bus, reply);
631 if (r < 0)
632 return r;
633
634 return sd_bus_message_append(reply, "b", context_ntp_service_is_active(c) > 0);
635}
636
19070062 637static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
40ca29a1 638 Context *c = userdata;
2c3def62 639 int interactive, r;
40ca29a1 640 const char *z;
c5f0532f 641
7e9cf16c
LP
642 assert(m);
643 assert(c);
644
40ca29a1
LP
645 r = sd_bus_message_read(m, "sb", &z, &interactive);
646 if (r < 0)
ebcf1f97 647 return r;
c5f0532f 648
089fb865 649 if (!timezone_is_valid(z, LOG_DEBUG))
5322db06 650 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid or not installed time zone '%s'", z);
ac7019f3 651
539a68e0 652 if (streq_ptr(z, c->zone))
df2d202e 653 return sd_bus_reply_method_return(m, NULL);
c5f0532f 654
c529695e
LP
655 r = bus_verify_polkit_async(
656 m,
657 CAP_SYS_TIME,
658 "org.freedesktop.timedate1.set-timezone",
403ed0e5 659 NULL,
c529695e
LP
660 interactive,
661 UID_INVALID,
662 &c->polkit_registry,
663 error);
40ca29a1 664 if (r < 0)
ebcf1f97 665 return r;
40ca29a1 666 if (r == 0)
6fc60278 667 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
6ffe5e37 668
539a68e0
YW
669 r = free_and_strdup(&c->zone, z);
670 if (r < 0)
671 return r;
672
40ca29a1
LP
673 /* 1. Write new configuration file */
674 r = context_write_data_timezone(c);
675 if (r < 0) {
da927ba9 676 log_error_errno(r, "Failed to set time zone: %m");
10a87006 677 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
40ca29a1 678 }
6ffe5e37 679
8a50b96f
LP
680 /* 2. Make glibc notice the new timezone */
681 tzset();
682
683 /* 3. Tell the kernel our timezone */
2a7ff45f
LP
684 r = clock_set_timezone(NULL);
685 if (r < 0)
686 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
6ffe5e37 687
40ca29a1
LP
688 if (c->local_rtc) {
689 struct timespec ts;
e0f691e1 690 struct tm tm;
c5f0532f 691
8a50b96f 692 /* 4. Sync RTC from system clock, with the new delta */
40ca29a1 693 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
e0f691e1 694 assert_se(localtime_r(&ts.tv_sec, &tm));
2a7ff45f 695
e0f691e1 696 r = clock_set_hwclock(&tm);
2a7ff45f
LP
697 if (r < 0)
698 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 699 }
c5f0532f 700
40ca29a1 701 log_struct(LOG_INFO,
2b044526 702 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
40ca29a1 703 "TIMEZONE=%s", c->zone,
8a50b96f
LP
704 "TIMEZONE_SHORTNAME=%s", tzname[daylight],
705 "DAYLIGHT=%i", daylight,
a1230ff9 706 LOG_MESSAGE("Changed time zone to '%s' (%s).", c->zone, tzname[daylight]));
c5f0532f 707
ad7fb943
ZJS
708 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
709 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone",
710 NULL);
c5f0532f 711
df2d202e 712 return sd_bus_reply_method_return(m, NULL);
c5f0532f
LP
713}
714
19070062 715static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102d8f81 716 int lrtc, fix_system, interactive;
40ca29a1
LP
717 Context *c = userdata;
718 struct timespec ts;
f401e48c
LP
719 int r;
720
40ca29a1
LP
721 assert(m);
722 assert(c);
f401e48c 723
40ca29a1
LP
724 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
725 if (r < 0)
ebcf1f97 726 return r;
f401e48c 727
2be6c475 728 if (lrtc == c->local_rtc && !fix_system)
df2d202e 729 return sd_bus_reply_method_return(m, NULL);
f401e48c 730
c529695e
LP
731 r = bus_verify_polkit_async(
732 m,
733 CAP_SYS_TIME,
734 "org.freedesktop.timedate1.set-local-rtc",
403ed0e5 735 NULL,
c529695e
LP
736 interactive,
737 UID_INVALID,
738 &c->polkit_registry,
739 error);
40ca29a1 740 if (r < 0)
ebcf1f97 741 return r;
40ca29a1
LP
742 if (r == 0)
743 return 1;
f401e48c 744
2be6c475
YW
745 if (lrtc != c->local_rtc) {
746 c->local_rtc = lrtc;
f401e48c 747
2be6c475
YW
748 /* 1. Write new configuration file */
749 r = context_write_data_local_rtc(c);
750 if (r < 0) {
751 log_error_errno(r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
752 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
753 }
40ca29a1 754 }
f401e48c 755
40ca29a1 756 /* 2. Tell the kernel our timezone */
2a7ff45f
LP
757 r = clock_set_timezone(NULL);
758 if (r < 0)
759 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
f401e48c 760
40ca29a1
LP
761 /* 3. Synchronize clocks */
762 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
f401e48c 763
40ca29a1
LP
764 if (fix_system) {
765 struct tm tm;
f401e48c 766
2a7ff45f 767 /* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
56b0ef2f 768 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
72edcff5 769
2a7ff45f
LP
770 /* Override the main fields of struct tm, but not the timezone fields */
771 r = clock_get_hwclock(&tm);
772 if (r < 0)
773 log_debug_errno(r, "Failed to get hardware clock, ignoring: %m");
774 else {
775 /* And set the system clock with this */
df49792e 776 ts.tv_sec = mktime_or_timegm(&tm, !c->local_rtc);
2076cf88 777
2a7ff45f
LP
778 if (clock_settime(CLOCK_REALTIME, &ts) < 0)
779 log_debug_errno(errno, "Failed to update system clock, ignoring: %m");
f401e48c
LP
780 }
781
40ca29a1 782 } else {
e46acb79 783 struct tm tm;
f401e48c 784
40ca29a1 785 /* Sync RTC from system clock */
56b0ef2f 786 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
2076cf88 787
e46acb79 788 r = clock_set_hwclock(&tm);
2a7ff45f
LP
789 if (r < 0)
790 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 791 }
2076cf88 792
40ca29a1 793 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
2076cf88 794
ad7fb943
ZJS
795 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
796 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC",
797 NULL);
2076cf88 798
df2d202e 799 return sd_bus_reply_method_return(m, NULL);
40ca29a1 800}
2076cf88 801
19070062 802static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
5d280742 803 sd_bus *bus = sd_bus_message_get_bus(m);
dfc5c4f2 804 char buf[FORMAT_TIMESTAMP_MAX];
5d280742 805 int relative, interactive, r;
40ca29a1
LP
806 Context *c = userdata;
807 int64_t utc;
808 struct timespec ts;
2479df30 809 usec_t start;
e46acb79 810 struct tm tm;
2076cf88 811
40ca29a1
LP
812 assert(m);
813 assert(c);
2076cf88 814
b4356b57
YW
815 if (c->slot_job_removed)
816 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Previous request is not finished, refusing.");
817
5d280742
YW
818 r = context_update_ntp_status(c, bus, m);
819 if (r < 0)
820 return sd_bus_error_set_errnof(error, r, "Failed to update context: %m");
821
822 if (context_ntp_service_is_active(c) > 0)
e9e5ea88 823 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
82d115d9 824
6829cec4
SL
825 /* this only gets used if dbus does not provide a timestamp */
826 start = now(CLOCK_MONOTONIC);
827
40ca29a1
LP
828 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
829 if (r < 0)
ebcf1f97 830 return r;
2076cf88 831
40ca29a1 832 if (!relative && utc <= 0)
e9e5ea88 833 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
2076cf88 834
40ca29a1 835 if (relative && utc == 0)
df2d202e 836 return sd_bus_reply_method_return(m, NULL);
2076cf88 837
40ca29a1
LP
838 if (relative) {
839 usec_t n, x;
f401e48c 840
40ca29a1
LP
841 n = now(CLOCK_REALTIME);
842 x = n + utc;
f401e48c 843
40ca29a1
LP
844 if ((utc > 0 && x < n) ||
845 (utc < 0 && x > n))
e9e5ea88 846 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
f401e48c 847
40ca29a1
LP
848 timespec_store(&ts, x);
849 } else
850 timespec_store(&ts, (usec_t) utc);
2076cf88 851
c529695e
LP
852 r = bus_verify_polkit_async(
853 m,
854 CAP_SYS_TIME,
855 "org.freedesktop.timedate1.set-time",
403ed0e5 856 NULL,
c529695e
LP
857 interactive,
858 UID_INVALID,
859 &c->polkit_registry,
860 error);
40ca29a1 861 if (r < 0)
ebcf1f97 862 return r;
40ca29a1
LP
863 if (r == 0)
864 return 1;
865
2479df30
SL
866 /* adjust ts for time spent in program */
867 r = sd_bus_message_get_monotonic_usec(m, &start);
6829cec4 868 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
2479df30
SL
869 if (r < 0 && r != -ENODATA)
870 return r;
6829cec4
SL
871
872 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
2479df30 873
40ca29a1
LP
874 /* Set system clock */
875 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
56f64d95 876 log_error_errno(errno, "Failed to set local time: %m");
ebcf1f97 877 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
40ca29a1 878 }
2076cf88 879
40ca29a1 880 /* Sync down to RTC */
56b0ef2f 881 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
2a7ff45f 882
e46acb79 883 r = clock_set_hwclock(&tm);
2a7ff45f
LP
884 if (r < 0)
885 log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
f401e48c 886
40ca29a1 887 log_struct(LOG_INFO,
2b044526 888 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
de0671ee 889 "REALTIME="USEC_FMT, timespec_load(&ts),
dfc5c4f2 890 LOG_MESSAGE("Changed local time to %s", strnull(format_timestamp(buf, sizeof(buf), timespec_load(&ts)))));
f401e48c 891
df2d202e 892 return sd_bus_reply_method_return(m, NULL);
40ca29a1 893}
f401e48c 894
19070062 895static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
cf3872bd 896 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
5d280742 897 sd_bus *bus = sd_bus_message_get_bus(m);
40ca29a1 898 Context *c = userdata;
5d280742 899 UnitStatusInfo *u;
ad7fb943 900 const UnitStatusInfo *selected = NULL;
5d280742 901 int enable, interactive, q, r;
f401e48c 902
19070062 903 assert(m);
5d280742 904 assert(bus);
19070062
LP
905 assert(c);
906
5d280742 907 r = sd_bus_message_read(m, "bb", &enable, &interactive);
40ca29a1 908 if (r < 0)
ebcf1f97 909 return r;
f401e48c 910
5d280742
YW
911 r = context_update_ntp_status(c, bus, m);
912 if (r < 0)
913 return r;
914
915 if (context_ntp_service_exists(c) <= 0)
916 return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
f401e48c 917
c529695e
LP
918 r = bus_verify_polkit_async(
919 m,
920 CAP_SYS_TIME,
921 "org.freedesktop.timedate1.set-ntp",
403ed0e5 922 NULL,
c529695e
LP
923 interactive,
924 UID_INVALID,
925 &c->polkit_registry,
926 error);
40ca29a1 927 if (r < 0)
ebcf1f97 928 return r;
40ca29a1
LP
929 if (r == 0)
930 return 1;
f401e48c 931
cf3872bd
YW
932 /* This method may be called frequently. Forget the previous job if it has not completed yet. */
933 LIST_FOREACH(units, u, c->units)
934 u->path = mfree(u->path);
935
936 if (!c->slot_job_removed) {
43fe4f76 937 r = bus_match_signal_async(
cf3872bd
YW
938 bus,
939 &slot,
43fe4f76 940 bus_systemd_mgr,
cf3872bd
YW
941 "JobRemoved",
942 match_job_removed, NULL, c);
943 if (r < 0)
944 return r;
945 }
946
0957790b 947 if (enable)
5d280742 948 LIST_FOREACH(units, u, c->units) {
0957790b 949 bool enable_this_one = !selected;
5d280742 950
5d280742
YW
951 if (!streq(u->load_state, "loaded"))
952 continue;
953
0957790b 954 r = unit_enable_or_disable(u, bus, error, enable_this_one);
5d280742 955 if (r < 0)
0957790b
ZJS
956 /* If enablement failed, don't start this unit. */
957 enable_this_one = false;
5d280742 958
0957790b
ZJS
959 r = unit_start_or_stop(u, bus, error, enable_this_one);
960 if (r < 0)
961 log_unit_warning_errno(u, r, "Failed to %s %sd NTP unit, ignoring: %m",
962 enable_this_one ? "start" : "stop",
963 enable_disable(enable_this_one));
964 if (enable_this_one)
965 selected = u;
5d280742 966 }
3af0a96c 967 else
5d280742 968 LIST_FOREACH(units, u, c->units) {
0957790b 969 if (!streq(u->load_state, "loaded"))
5d280742
YW
970 continue;
971
0957790b
ZJS
972 q = unit_enable_or_disable(u, bus, error, false);
973 if (q < 0)
974 r = q;
975
976 q = unit_start_or_stop(u, bus, error, false);
977 if (q < 0)
978 r = q;
5d280742 979 }
40ca29a1 980
40ca29a1 981 if (r < 0)
ebcf1f97 982 return r;
ad7fb943
ZJS
983 if (enable && !selected)
984 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No NTP service found to enable.");
f401e48c 985
cf3872bd
YW
986 if (slot)
987 c->slot_job_removed = TAKE_PTR(slot);
988
ad7fb943
ZJS
989 if (selected)
990 log_info("Set NTP to enabled (%s).", selected->name);
991 else
992 log_info("Set NTP to disabled.");
f401e48c 993
df2d202e 994 return sd_bus_reply_method_return(m, NULL);
f401e48c
LP
995}
996
2cf0b2fe
NT
997static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error *error) {
998 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
999 _cleanup_strv_free_ char **zones = NULL;
1000 int r;
1001
1002 assert(m);
1003
1004 r = get_timezones(&zones);
1005 if (r < 0)
1006 return sd_bus_error_set_errnof(error, r, "Failed to read list of time zones: %m");
1007
1008 r = sd_bus_message_new_method_return(m, &reply);
1009 if (r < 0)
1010 return r;
1011
1012 r = sd_bus_message_append_strv(reply, zones);
1013 if (r < 0)
1014 return r;
1015
1016 return sd_bus_send(NULL, reply, NULL);
1017}
1018
40ca29a1
LP
1019static const sd_bus_vtable timedate_vtable[] = {
1020 SD_BUS_VTABLE_START(0),
599c99ee 1021
40ca29a1 1022 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
82d115d9 1023 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
5d280742
YW
1024 SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0),
1025 SD_BUS_PROPERTY("NTP", "b", property_get_ntp, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
03cc26dd
LP
1026 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
1027 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
6fc60278 1028 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
599c99ee
ZJS
1029
1030 SD_BUS_METHOD_WITH_NAMES("SetTime",
1031 "xbb",
1032 SD_BUS_PARAM(usec_utc)
1033 SD_BUS_PARAM(relative)
1034 SD_BUS_PARAM(interactive),
1035 NULL,,
1036 method_set_time,
1037 SD_BUS_VTABLE_UNPRIVILEGED),
1038 SD_BUS_METHOD_WITH_NAMES("SetTimezone",
1039 "sb",
1040 SD_BUS_PARAM(timezone)
1041 SD_BUS_PARAM(interactive),
1042 NULL,,
1043 method_set_timezone,
1044 SD_BUS_VTABLE_UNPRIVILEGED),
1045 SD_BUS_METHOD_WITH_NAMES("SetLocalRTC",
1046 "bbb",
1047 SD_BUS_PARAM(local_rtc)
1048 SD_BUS_PARAM(fix_system)
1049 SD_BUS_PARAM(interactive),
1050 NULL,,
1051 method_set_local_rtc,
1052 SD_BUS_VTABLE_UNPRIVILEGED),
1053 SD_BUS_METHOD_WITH_NAMES("SetNTP",
1054 "bb",
1055 SD_BUS_PARAM(use_ntp)
1056 SD_BUS_PARAM(interactive),
1057 NULL,,
1058 method_set_ntp,
1059 SD_BUS_VTABLE_UNPRIVILEGED),
1060 SD_BUS_METHOD_WITH_NAMES("ListTimezones",
1061 NULL,,
1062 "as",
1063 SD_BUS_PARAM(timezones),
1064 method_list_timezones,
1065 SD_BUS_VTABLE_UNPRIVILEGED),
1066
40ca29a1
LP
1067 SD_BUS_VTABLE_END,
1068};
1069
c4b7d95c
ZJS
1070const BusObjectImplementation manager_object = {
1071 "/org/freedesktop/timedate1",
1072 "org.freedesktop.timedate1",
1073 .vtables = BUS_VTABLES(timedate_vtable),
1074};
1075
40ca29a1 1076static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 1077 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c
LP
1078 int r;
1079
40ca29a1
LP
1080 assert(c);
1081 assert(event);
f401e48c
LP
1082 assert(_bus);
1083
76b54375 1084 r = sd_bus_default_system(&bus);
f647962d
MS
1085 if (r < 0)
1086 return log_error_errno(r, "Failed to get system bus connection: %m");
f401e48c 1087
c4b7d95c 1088 r = bus_add_implementation(bus, &manager_object, c);
f647962d 1089 if (r < 0)
c4b7d95c 1090 return r;
f401e48c 1091
ac9f55ed
LP
1092 r = bus_log_control_api_register(bus);
1093 if (r < 0)
1094 return r;
1095
0c0b9306 1096 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
f647962d 1097 if (r < 0)
0c0b9306 1098 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1099
40ca29a1 1100 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1101 if (r < 0)
1102 return log_error_errno(r, "Failed to attach bus to event loop: %m");
f401e48c 1103
1cc6c93a 1104 *_bus = TAKE_PTR(bus);
f401e48c 1105
40ca29a1 1106 return 0;
f401e48c
LP
1107}
1108
1f47bc33
YW
1109static int run(int argc, char *argv[]) {
1110 _cleanup_(context_clear) Context context = {};
4afd3348
LP
1111 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1112 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c 1113 int r;
f401e48c 1114
d2acb93d 1115 log_setup();
f401e48c 1116
fc021a5b
ZJS
1117 r = service_parse_argv("systemd-timedated.service",
1118 "Manage the system clock and timezone and NTP enablement.",
c4b7d95c
ZJS
1119 BUS_IMPLEMENTATIONS(&manager_object,
1120 &log_control_object),
fc021a5b
ZJS
1121 argc, argv);
1122 if (r <= 0)
1123 return r;
4c12626c 1124
fc021a5b 1125 umask(0022);
f401e48c 1126
754f0269
YW
1127 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1128
afc6adb5 1129 r = sd_event_default(&event);
1f47bc33
YW
1130 if (r < 0)
1131 return log_error_errno(r, "Failed to allocate event loop: %m");
f401e48c 1132
754f0269
YW
1133 (void) sd_event_set_watchdog(event, true);
1134
1135 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1f47bc33
YW
1136 if (r < 0)
1137 return log_error_errno(r, "Failed to install SIGINT handler: %m");
754f0269
YW
1138
1139 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1f47bc33
YW
1140 if (r < 0)
1141 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1142
40ca29a1 1143 r = connect_bus(&context, event, &bus);
f401e48c 1144 if (r < 0)
1f47bc33 1145 return r;
f401e48c 1146
dc751688 1147 (void) sd_bus_negotiate_timestamp(bus, true);
2479df30 1148
40ca29a1 1149 r = context_read_data(&context);
1f47bc33
YW
1150 if (r < 0)
1151 return log_error_errno(r, "Failed to read time zone data: %m");
c5f0532f 1152
5d280742
YW
1153 r = context_parse_ntp_services(&context);
1154 if (r < 0)
1f47bc33 1155 return r;
ad740100 1156
37224a5f 1157 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
1f47bc33
YW
1158 if (r < 0)
1159 return log_error_errno(r, "Failed to run event loop: %m");
f401e48c 1160
1f47bc33 1161 return 0;
f401e48c 1162}
1f47bc33
YW
1163
1164DEFINE_MAIN_FUNCTION(run);