]> git.ipfire.org Git - thirdparty/chrony.git/blame - main.c
avoid some static analysis errors
[thirdparty/chrony.git] / main.c
CommitLineData
88840341 1/*
88840341
RC
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
6672f045 5 * Copyright (C) Richard P. Curnow 1997-2003
f7e08d0c 6 * Copyright (C) John G. Hasler 2009
3916c336 7 * Copyright (C) Miroslav Lichvar 2012-2020
88840341
RC
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of version 2 of the GNU General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This program 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
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
8e23110a 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
88840341
RC
21 *
22 **********************************************************************
23
24 =======================================================================
25
26 The main program
27 */
28
da2c8d90
ML
29#include "config.h"
30
88840341
RC
31#include "sysincl.h"
32
33#include "main.h"
34#include "sched.h"
017d6f8f 35#include "leapdb.h"
88840341
RC
36#include "local.h"
37#include "sys.h"
38#include "ntp_io.h"
577aed48 39#include "ntp_signd.h"
88840341
RC
40#include "ntp_sources.h"
41#include "ntp_core.h"
a420ed57 42#include "nts_ke_server.h"
6043632f 43#include "nts_ntp_server.h"
2de24cfd 44#include "socket.h"
88840341
RC
45#include "sources.h"
46#include "sourcestats.h"
47#include "reference.h"
48#include "logging.h"
49#include "conf.h"
50#include "cmdmon.h"
51#include "keys.h"
88840341 52#include "manual.h"
88840341 53#include "rtc.h"
ac30bb06 54#include "refclock.h"
88840341 55#include "clientlog.h"
fbd20c42 56#include "nameserv.h"
610f2340 57#include "privops.h"
7f45eb79 58#include "smooth.h"
c386d117 59#include "tempcomp.h"
ceef8ad2 60#include "util.h"
88840341
RC
61
62/* ================================================== */
63
64/* Set when the initialisation chain has been completed. Prevents finalisation
65 * chain being run if a fatal error happened early. */
66
67static int initialised = 0;
68
93b66ac1 69static int exit_status = 0;
88840341
RC
70
71static int reload = 0;
72
e176587e 73static REF_Mode ref_mode = REF_ModeNormal;
7fda9c67 74
88840341
RC
75/* ================================================== */
76
bafb434f
ML
77static void
78do_platform_checks(void)
79{
d5894c07
ML
80 struct timespec ts;
81
bafb434f
ML
82 /* Require at least 32-bit integers, two's complement representation and
83 the usual implementation of conversion of unsigned integers */
84 assert(sizeof (int) >= 4);
85 assert(-1 == ~0);
fe502128 86 assert((int32_t)4294967295U == (int32_t)-1);
d5894c07
ML
87
88 /* Require time_t and tv_nsec in timespec to be signed */
89 ts.tv_sec = -1;
90 ts.tv_nsec = -1;
91 assert(ts.tv_sec < 0 && ts.tv_nsec < 0);
bafb434f
ML
92}
93
94/* ================================================== */
95
88840341
RC
96static void
97delete_pidfile(void)
98{
99 const char *pidfile = CNF_GetPidFile();
778fce40 100
60049f15 101 if (!pidfile)
778fce40
ML
102 return;
103
e18903a6
ML
104 if (!UTI_RemoveFile(NULL, pidfile, NULL))
105 ;
88840341
RC
106}
107
108/* ================================================== */
109
1e7e7d32 110void
88840341
RC
111MAI_CleanupAndExit(void)
112{
93b66ac1 113 if (!initialised) exit(exit_status);
88840341 114
3cef7f97 115 LCL_CancelOffsetCorrection();
60049f15 116 SRC_DumpSources();
88840341 117
f6ed7844
ML
118 /* Don't update clock when removing sources */
119 REF_SetMode(REF_ModeIgnore);
120
7f45eb79 121 SMT_Finalise();
c386d117 122 TMC_Finalise();
88840341 123 MNL_Finalise();
88840341 124 CLG_Finalise();
a420ed57 125 NKS_Finalise();
6043632f 126 NNS_Finalise();
577aed48 127 NSD_Finalise();
88840341 128 NSR_Finalise();
8854c00d 129 SST_Finalise();
88840341 130 NCR_Finalise();
4e54770f 131 NIO_Finalise();
8854c00d 132 CAM_Finalise();
66e097e3 133
1c901b82 134 KEY_Finalise();
ac30bb06 135 RCL_Finalise();
3a222336 136 SRC_Finalise();
f6ed7844 137 REF_Finalise();
017d6f8f 138 LDB_Finalise();
be42b4ee 139 RTC_Finalise();
88840341 140 SYS_Finalise();
66e097e3
ML
141
142 SCK_Finalise();
88840341
RC
143 SCH_Finalise();
144 LCL_Finalise();
334ac061 145 PRV_Finalise();
88840341
RC
146
147 delete_pidfile();
148
f6ed7844 149 CNF_Finalise();
f6ed7844 150 HSH_Finalise();
b712c100 151 LOG_Finalise();
f6ed7844 152
a0a95602
ML
153 UTI_ResetGetRandomFunctions();
154
93b66ac1 155 exit(exit_status);
88840341
RC
156}
157
158/* ================================================== */
159
160static void
161signal_cleanup(int x)
162{
9416a24f 163 SCH_QuitProgram();
88840341
RC
164}
165
166/* ================================================== */
167
35134848
ML
168static void
169quit_timeout(void *arg)
170{
de598c23
ML
171 LOG(LOGS_INFO, "Timeout reached");
172
35134848
ML
173 /* Return with non-zero status if the clock is not synchronised */
174 exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
175 SCH_QuitProgram();
176}
177
178/* ================================================== */
179
88840341 180static void
6ee357d2 181ntp_source_resolving_end(void)
88840341 182{
6ee357d2 183 NSR_SetSourceResolvingEndHandler(NULL);
88840341 184
88840341
RC
185 if (reload) {
186 /* Note, we want reload to come well after the initialisation from
187 the real time clock - this gives us a fighting chance that the
188 system-clock scale for the reloaded samples still has a
189 semblence of validity about it. */
190 SRC_ReloadSources();
191 }
88840341 192
a06a5f1b 193 SRC_RemoveDumpFiles();
88840341 194 RTC_StartMeasurements();
ac30bb06 195 RCL_StartRefclocks();
779e40ed
ML
196 NSR_StartSources();
197 NSR_AutoStartSources();
70feea48
ML
198
199 /* Special modes can end only when sources update their reachability.
144fcdde 200 Give up immediately if there are no active sources. */
7fa22d4c 201 if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
70feea48
ML
202 REF_SetUnsynchronised();
203 }
88840341
RC
204}
205
206/* ================================================== */
207
6ee357d2
ML
208static void
209post_init_ntp_hook(void *anything)
210{
211 if (ref_mode == REF_ModeInitStepSlew) {
212 /* Remove the initstepslew sources and set normal mode */
213 NSR_RemoveAllSources();
214 ref_mode = REF_ModeNormal;
215 REF_SetMode(ref_mode);
216 }
217
218 /* Close the pipe to the foreground process so it can exit */
219 LOG_CloseParentFd();
220
221 CNF_AddSources();
222 CNF_AddBroadcasts();
223
224 NSR_SetSourceResolvingEndHandler(ntp_source_resolving_end);
225 NSR_ResolveSources();
226}
227
228/* ================================================== */
229
7fda9c67
ML
230static void
231reference_mode_end(int result)
232{
233 switch (ref_mode) {
234 case REF_ModeNormal:
70feea48
ML
235 case REF_ModeUpdateOnce:
236 case REF_ModePrintOnce:
93b66ac1
ML
237 exit_status = !result;
238 SCH_QuitProgram();
7fda9c67
ML
239 break;
240 case REF_ModeInitStepSlew:
ead3ca14
ML
241 /* Switch to the normal mode, the delay is used to prevent polling
242 interval shorter than the burst interval if some configured servers
243 were used also for initstepslew */
244 SCH_AddTimeoutByDelay(2.0, post_init_ntp_hook, NULL);
7fda9c67
ML
245 break;
246 default:
247 assert(0);
248 }
249}
250
251/* ================================================== */
252
88840341
RC
253static void
254post_init_rtc_hook(void *anything)
255{
7fda9c67
ML
256 if (CNF_GetInitSources() > 0) {
257 CNF_AddInitSources();
779e40ed 258 NSR_StartSources();
7fda9c67
ML
259 assert(REF_GetMode() != REF_ModeNormal);
260 /* Wait for mode end notification */
261 } else {
262 (post_init_ntp_hook)(NULL);
263 }
88840341
RC
264}
265
266/* ================================================== */
88840341 267
7bd1c027
ML
268static void
269check_pidfile(void)
88840341
RC
270{
271 const char *pidfile = CNF_GetPidFile();
272 FILE *in;
273 int pid, count;
274
60049f15 275 if (!pidfile)
077dbd56
ML
276 return;
277
e18903a6 278 in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
7bd1c027
ML
279 if (!in)
280 return;
88840341
RC
281
282 count = fscanf(in, "%d", &pid);
283 fclose(in);
284
7bd1c027
ML
285 if (count != 1)
286 return;
88840341 287
7bd1c027
ML
288 if (getsid(pid) < 0)
289 return;
290
291 LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
292 pid, pidfile);
88840341
RC
293}
294
295/* ================================================== */
296
297static void
7bd1c027 298write_pidfile(void)
88840341
RC
299{
300 const char *pidfile = CNF_GetPidFile();
301 FILE *out;
302
60049f15 303 if (!pidfile)
778fce40
ML
304 return;
305
e18903a6
ML
306 out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
307 fprintf(out, "%d\n", (int)getpid());
308 fclose(out);
88840341
RC
309}
310
311/* ================================================== */
312
da862158
ML
313#define DEV_NULL "/dev/null"
314
fe4b661f
ML
315static void
316go_daemon(void)
317{
1d2a0856
ML
318 int pid, fd, pipefd[2];
319
320 /* Create pipe which will the daemon use to notify the grandparent
321 when it's initialised or send an error message */
322 if (pipe(pipefd)) {
539ef3f7 323 LOG_FATAL("pipe() failed : %s", strerror(errno));
1d2a0856 324 }
fe4b661f
ML
325
326 /* Does this preserve existing signal handlers? */
327 pid = fork();
328
329 if (pid < 0) {
539ef3f7 330 LOG_FATAL("fork() failed : %s", strerror(errno));
fe4b661f 331 } else if (pid > 0) {
1d2a0856
ML
332 /* In the 'grandparent' */
333 char message[1024];
334 int r;
335
0db30fd0
ML
336 /* Don't exit before the 'parent' */
337 waitpid(pid, NULL, 0);
338
1d2a0856
ML
339 close(pipefd[1]);
340 r = read(pipefd[0], message, sizeof (message));
341 if (r) {
342 if (r > 0) {
343 /* Print the error message from the child */
a7802e9a
ML
344 message[sizeof (message) - 1] = '\0';
345 fprintf(stderr, "%s\n", message);
1d2a0856
ML
346 }
347 exit(1);
348 } else
349 exit(0);
fe4b661f 350 } else {
1d2a0856 351 close(pipefd[0]);
fe4b661f
ML
352
353 setsid();
354
355 /* Do 2nd fork, as-per recommended practice for launching daemons. */
356 pid = fork();
357
358 if (pid < 0) {
539ef3f7 359 LOG_FATAL("fork() failed : %s", strerror(errno));
fe4b661f 360 } else if (pid > 0) {
0db30fd0
ML
361 /* In the 'parent' */
362 close(pipefd[1]);
363 exit(0);
fe4b661f
ML
364 } else {
365 /* In the child we want to leave running as the daemon */
366
2a305d8e
ML
367 /* Change current directory to / */
368 if (chdir("/") < 0) {
539ef3f7 369 LOG_FATAL("chdir() failed : %s", strerror(errno));
2a305d8e
ML
370 }
371
1d2a0856 372 /* Don't keep stdin/out/err from before. But don't close
e6a0476e 373 the parent pipe yet, or reusable file descriptors. */
fe4b661f 374 for (fd=0; fd<1024; fd++) {
e6a0476e 375 if (fd != pipefd[1] && !SCK_IsReusable(fd))
1d2a0856 376 close(fd);
fe4b661f 377 }
919b5b5a 378
1d2a0856 379 LOG_SetParentFd(pipefd[1]);
da862158
ML
380
381 /* Open /dev/null as new stdin/out/err */
382 errno = 0;
383 if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
384 open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
385 open(DEV_NULL, O_RDWR) != STDERR_FILENO)
386 LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
fe4b661f
ML
387 }
388 }
fe4b661f
ML
389}
390
391/* ================================================== */
392
ae0c3bbb
ML
393static void
394print_help(const char *progname)
395{
0e08ca7c
ML
396 printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n"
397 "Options:\n"
398 " -4\t\tUse IPv4 addresses only\n"
399 " -6\t\tUse IPv6 addresses only\n"
400 " -f FILE\tSpecify configuration file (%s)\n"
401 " -n\t\tDon't run as daemon\n"
402 " -d\t\tDon't run as daemon and log to stderr\n"
403#if DEBUG > 0
404 " -d -d\t\tEnable debug messages\n"
405#endif
406 " -l FILE\tLog to file\n"
407 " -L LEVEL\tSet logging threshold (0)\n"
408 " -p\t\tPrint configuration and exit\n"
409 " -q\t\tSet clock and exit\n"
410 " -Q\t\tLog offset and exit\n"
411 " -r\t\tReload dump files\n"
412 " -R\t\tAdapt configuration for restart\n"
413 " -s\t\tSet clock from RTC\n"
414 " -t SECONDS\tExit after elapsed time\n"
415 " -u USER\tSpecify user (%s)\n"
416 " -U\t\tDon't check for root\n"
417 " -F LEVEL\tSet system call filter level (0)\n"
418 " -P PRIORITY\tSet process priority (0)\n"
419 " -m\t\tLock memory\n"
420 " -x\t\tDon't control clock\n"
421 " -v, --version\tPrint version and exit\n"
422 " -h, --help\tPrint usage and exit\n",
423 progname, DEFAULT_CONF_FILE, DEFAULT_USER);
ae0c3bbb
ML
424}
425
426/* ================================================== */
427
428static void
429print_version(void)
430{
431 printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
432}
433
434/* ================================================== */
435
436static int
437parse_int_arg(const char *arg)
438{
439 int i;
440
441 if (sscanf(arg, "%d", &i) != 1)
442 LOG_FATAL("Invalid argument %s", arg);
443 return i;
444}
445
446/* ================================================== */
447
88840341
RC
448int main
449(int argc, char **argv)
450{
0f8def4c 451 const char *conf_file = DEFAULT_CONF_FILE;
1cda2db4 452 const char *progname = argv[0];
a1cbd4eb 453 char *user = NULL, *log_file = NULL;
6402350c 454 struct passwd *pw;
ae0c3bbb 455 int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
552d3b53 456 int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
434faeec 457 int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
70fa3a69 458 int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
02c38934 459 int user_check = 1, config_args = 0, print_config = 0;
35e662d8 460
bafb434f
ML
461 do_platform_checks();
462
88840341
RC
463 LOG_Initialise();
464
068cd3c3 465 /* Parse long command-line options */
ae0c3bbb
ML
466 for (optind = 1; optind < argc; optind++) {
467 if (!strcmp("--help", argv[optind])) {
468 print_help(progname);
1769b8ea 469 return 0;
ae0c3bbb
ML
470 } else if (!strcmp("--version", argv[optind])) {
471 print_version();
1769b8ea 472 return 0;
ae0c3bbb
ML
473 }
474 }
475
476 optind = 1;
477
478 /* Parse short command-line options */
02c38934 479 while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) {
ae0c3bbb
ML
480 switch (opt) {
481 case '4':
482 case '6':
483 address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
484 break;
485 case 'd':
486 debug++;
487 nofork = 1;
488 system_log = 0;
489 break;
490 case 'f':
491 conf_file = optarg;
492 break;
493 case 'F':
494 scfilter_level = parse_int_arg(optarg);
495 break;
496 case 'l':
497 log_file = optarg;
498 break;
70fa3a69
ML
499 case 'L':
500 log_severity = parse_int_arg(optarg);
501 break;
ae0c3bbb
ML
502 case 'm':
503 lock_memory = 1;
504 break;
505 case 'n':
506 nofork = 1;
507 break;
1b82604f
ML
508 case 'p':
509 print_config = 1;
02c38934 510 user_check = 0;
1b82604f
ML
511 nofork = 1;
512 system_log = 0;
517b1ae2 513 log_severity = LOGS_WARN;
1b82604f 514 break;
ae0c3bbb
ML
515 case 'P':
516 sched_priority = parse_int_arg(optarg);
517 break;
518 case 'q':
4f1fc1ee
BC
519 ref_mode = REF_ModeUpdateOnce;
520 nofork = 1;
521 client_only = 0;
522 system_log = 0;
523 break;
ae0c3bbb 524 case 'Q':
4f1fc1ee 525 ref_mode = REF_ModePrintOnce;
ae0c3bbb 526 nofork = 1;
778fce40 527 client_only = 1;
02c38934 528 user_check = 0;
778fce40 529 clock_control = 0;
ae0c3bbb
ML
530 system_log = 0;
531 break;
532 case 'r':
533 reload = 1;
534 break;
535 case 'R':
536 restarted = 1;
537 break;
538 case 's':
539 do_init_rtc = 1;
540 break;
541 case 't':
542 timeout = parse_int_arg(optarg);
543 break;
544 case 'u':
545 user = optarg;
546 break;
02c38934
ML
547 case 'U':
548 user_check = 0;
549 break;
ae0c3bbb
ML
550 case 'v':
551 print_version();
552 return 0;
553 case 'x':
554 clock_control = 0;
555 break;
556 default:
557 print_help(progname);
558 return opt != 'h';
88840341
RC
559 }
560 }
561
02c38934 562 if (user_check && getuid() != 0)
3a5566c6 563 LOG_FATAL("Not superuser");
88840341 564
e6a0476e
LV
565 /* Initialise reusable file descriptors before fork */
566 SCK_PreInitialise();
567
88840341 568 /* Turn into a daemon */
fe4b661f
ML
569 if (!nofork) {
570 go_daemon();
571 }
572
a1cbd4eb
ML
573 if (log_file) {
574 LOG_OpenFileLog(log_file);
575 } else if (system_log) {
fe4b661f 576 LOG_OpenSystemLog();
88840341
RC
577 }
578
70fa3a69 579 LOG_SetMinSeverity(debug >= 2 ? LOGS_DEBUG : log_severity);
4bbc5520 580
f282856c 581 LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
cb905507 582
72d0b3c9
ML
583 DNS_SetAddressFamily(address_family);
584
778fce40 585 CNF_Initialise(restarted, client_only);
1b82604f
ML
586 if (print_config)
587 CNF_EnablePrint();
3edd3fe5
ML
588
589 /* Parse the config file or the remaining command line arguments */
ae0c3bbb 590 config_args = argc - optind;
3edd3fe5
ML
591 if (!config_args) {
592 CNF_ReadFile(conf_file);
593 } else {
ae0c3bbb
ML
594 for (; optind < argc; optind++)
595 CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
3edd3fe5 596 }
1e35b268 597
1b82604f
ML
598 if (print_config)
599 return 0;
600
7bd1c027
ML
601 /* Check whether another chronyd may already be running */
602 check_pidfile();
88840341 603
c5d8af02
ML
604 if (!user)
605 user = CNF_GetUser();
606
607 pw = getpwnam(user);
608 if (!pw)
609 LOG_FATAL("Could not get user/group ID of %s", user);
610
611 /* Create directories for sockets, log files, and dump files */
612 CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
613
26e08abe
ML
614 /* Write our pidfile to prevent other instances from running */
615 write_pidfile();
616
334ac061 617 PRV_Initialise();
88840341
RC
618 LCL_Initialise();
619 SCH_Initialise();
66e097e3
ML
620 SCK_Initialise(address_family);
621
622 /* Start helper processes if needed */
623 NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level);
624
c4434609 625 SYS_Initialise(clock_control);
c6e06420 626 RTC_Initialise(do_init_rtc);
3a222336 627 SRC_Initialise();
ac30bb06 628 RCL_Initialise();
1c901b82 629 KEY_Initialise();
be42b4ee 630
8854c00d 631 /* Open privileged ports before dropping root */
27e20a56
ML
632 CAM_Initialise();
633 NIO_Initialise();
8854c00d
ML
634 NCR_Initialise();
635 CNF_SetupAccessRestrictions();
636
e3234465
ML
637 /* Command-line switch must have priority */
638 if (!sched_priority) {
639 sched_priority = CNF_GetSchedPriority();
640 }
641 if (sched_priority) {
642 SYS_SetScheduler(sched_priority);
96759116
ML
643 }
644
e3234465
ML
645 if (lock_memory || CNF_GetLockMemory()) {
646 SYS_LockMemory();
96759116
ML
647 }
648
778fce40 649 /* Drop root privileges if the specified user has a non-zero UID */
883b0dde 650 if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
a96d2880 651 SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
be42b4ee 652
883b0dde
ML
653 /* Warn if missing read access or having write access to keys */
654 CNF_CheckReadOnlyAccess();
655 }
656
4f878ba1
ML
657 if (!geteuid())
658 LOG(LOGS_WARN, "Running with root privileges");
659
017d6f8f 660 LDB_Initialise();
88840341
RC
661 REF_Initialise();
662 SST_Initialise();
88840341 663 NSR_Initialise();
577aed48 664 NSD_Initialise();
6043632f 665 NNS_Initialise();
66e097e3 666 NKS_Initialise();
88840341 667 CLG_Initialise();
88840341 668 MNL_Initialise();
c386d117 669 TMC_Initialise();
7f45eb79 670 SMT_Initialise();
88840341
RC
671
672 /* From now on, it is safe to do finalisation on exit */
673 initialised = 1;
674
879d9362 675 UTI_SetQuitSignalsHandler(signal_cleanup, 1);
ea2858b3 676
8854c00d 677 CAM_OpenUnixSocket();
46951b85 678
434faeec 679 if (scfilter_level)
e6848b1e 680 SYS_EnableSystemCallFilter(scfilter_level, SYS_MAIN_PROCESS);
434faeec 681
70feea48 682 if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
7fda9c67
ML
683 ref_mode = REF_ModeInitStepSlew;
684 }
685
686 REF_SetModeEndHandler(reference_mode_end);
687 REF_SetMode(ref_mode);
688
552d3b53 689 if (timeout >= 0)
35134848
ML
690 SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
691
88840341
RC
692 if (do_init_rtc) {
693 RTC_TimeInit(post_init_rtc_hook, NULL);
694 } else {
695 post_init_rtc_hook(NULL);
696 }
697
88840341
RC
698 /* The program normally runs under control of the main loop in
699 the scheduler. */
700 SCH_MainLoop();
701
f282856c 702 LOG(LOGS_INFO, "chronyd exiting");
0fc9b555 703
88840341
RC
704 MAI_CleanupAndExit();
705
706 return 0;
707}
708
709/* ================================================== */