]>
Commit | Line | Data |
---|---|---|
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 |
33967780 | 7 | * Copyright (C) Miroslav Lichvar 2012-2015 |
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" | |
35 | #include "local.h" | |
36 | #include "sys.h" | |
37 | #include "ntp_io.h" | |
38 | #include "ntp_sources.h" | |
39 | #include "ntp_core.h" | |
40 | #include "sources.h" | |
41 | #include "sourcestats.h" | |
42 | #include "reference.h" | |
43 | #include "logging.h" | |
44 | #include "conf.h" | |
45 | #include "cmdmon.h" | |
46 | #include "keys.h" | |
88840341 | 47 | #include "manual.h" |
88840341 | 48 | #include "rtc.h" |
ac30bb06 | 49 | #include "refclock.h" |
88840341 | 50 | #include "clientlog.h" |
fbd20c42 | 51 | #include "nameserv.h" |
610f2340 | 52 | #include "privops.h" |
7f45eb79 | 53 | #include "smooth.h" |
c386d117 | 54 | #include "tempcomp.h" |
ceef8ad2 | 55 | #include "util.h" |
88840341 RC |
56 | |
57 | /* ================================================== */ | |
58 | ||
59 | /* Set when the initialisation chain has been completed. Prevents finalisation | |
60 | * chain being run if a fatal error happened early. */ | |
61 | ||
62 | static int initialised = 0; | |
63 | ||
93b66ac1 | 64 | static int exit_status = 0; |
88840341 RC |
65 | |
66 | static int reload = 0; | |
67 | ||
e176587e | 68 | static REF_Mode ref_mode = REF_ModeNormal; |
7fda9c67 | 69 | |
88840341 RC |
70 | /* ================================================== */ |
71 | ||
bafb434f ML |
72 | static void |
73 | do_platform_checks(void) | |
74 | { | |
75 | /* Require at least 32-bit integers, two's complement representation and | |
76 | the usual implementation of conversion of unsigned integers */ | |
77 | assert(sizeof (int) >= 4); | |
78 | assert(-1 == ~0); | |
fe502128 | 79 | assert((int32_t)4294967295U == (int32_t)-1); |
bafb434f ML |
80 | } |
81 | ||
82 | /* ================================================== */ | |
83 | ||
88840341 RC |
84 | static void |
85 | delete_pidfile(void) | |
86 | { | |
87 | const char *pidfile = CNF_GetPidFile(); | |
88 | /* Don't care if this fails, there's not a lot we can do */ | |
89 | unlink(pidfile); | |
90 | } | |
91 | ||
92 | /* ================================================== */ | |
93 | ||
1e7e7d32 | 94 | void |
88840341 RC |
95 | MAI_CleanupAndExit(void) |
96 | { | |
93b66ac1 | 97 | if (!initialised) exit(exit_status); |
88840341 RC |
98 | |
99 | if (CNF_GetDumpOnExit()) { | |
100 | SRC_DumpSources(); | |
101 | } | |
102 | ||
f6ed7844 ML |
103 | /* Don't update clock when removing sources */ |
104 | REF_SetMode(REF_ModeIgnore); | |
105 | ||
7f45eb79 | 106 | SMT_Finalise(); |
c386d117 | 107 | TMC_Finalise(); |
88840341 | 108 | MNL_Finalise(); |
88840341 | 109 | CLG_Finalise(); |
88840341 | 110 | NSR_Finalise(); |
8854c00d | 111 | SST_Finalise(); |
88840341 | 112 | NCR_Finalise(); |
4e54770f | 113 | NIO_Finalise(); |
8854c00d | 114 | CAM_Finalise(); |
610f2340 | 115 | PRV_Finalise(); |
1c901b82 | 116 | KEY_Finalise(); |
ac30bb06 | 117 | RCL_Finalise(); |
3a222336 | 118 | SRC_Finalise(); |
f6ed7844 | 119 | REF_Finalise(); |
be42b4ee | 120 | RTC_Finalise(); |
88840341 RC |
121 | SYS_Finalise(); |
122 | SCH_Finalise(); | |
123 | LCL_Finalise(); | |
124 | ||
125 | delete_pidfile(); | |
126 | ||
f6ed7844 | 127 | CNF_Finalise(); |
88840341 RC |
128 | LOG_Finalise(); |
129 | ||
f6ed7844 ML |
130 | HSH_Finalise(); |
131 | ||
93b66ac1 | 132 | exit(exit_status); |
88840341 RC |
133 | } |
134 | ||
135 | /* ================================================== */ | |
136 | ||
137 | static void | |
138 | signal_cleanup(int x) | |
139 | { | |
9416a24f ML |
140 | if (!initialised) exit(0); |
141 | SCH_QuitProgram(); | |
88840341 RC |
142 | } |
143 | ||
144 | /* ================================================== */ | |
145 | ||
146 | static void | |
6ee357d2 | 147 | ntp_source_resolving_end(void) |
88840341 | 148 | { |
6ee357d2 | 149 | NSR_SetSourceResolvingEndHandler(NULL); |
88840341 | 150 | |
88840341 RC |
151 | if (reload) { |
152 | /* Note, we want reload to come well after the initialisation from | |
153 | the real time clock - this gives us a fighting chance that the | |
154 | system-clock scale for the reloaded samples still has a | |
155 | semblence of validity about it. */ | |
156 | SRC_ReloadSources(); | |
157 | } | |
88840341 RC |
158 | |
159 | RTC_StartMeasurements(); | |
ac30bb06 | 160 | RCL_StartRefclocks(); |
779e40ed ML |
161 | NSR_StartSources(); |
162 | NSR_AutoStartSources(); | |
70feea48 ML |
163 | |
164 | /* Special modes can end only when sources update their reachability. | |
7fa22d4c ML |
165 | Give up immediatelly if there are no active sources. */ |
166 | if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) { | |
70feea48 ML |
167 | REF_SetUnsynchronised(); |
168 | } | |
88840341 RC |
169 | } |
170 | ||
171 | /* ================================================== */ | |
172 | ||
6ee357d2 ML |
173 | static void |
174 | post_init_ntp_hook(void *anything) | |
175 | { | |
176 | if (ref_mode == REF_ModeInitStepSlew) { | |
177 | /* Remove the initstepslew sources and set normal mode */ | |
178 | NSR_RemoveAllSources(); | |
179 | ref_mode = REF_ModeNormal; | |
180 | REF_SetMode(ref_mode); | |
181 | } | |
182 | ||
183 | /* Close the pipe to the foreground process so it can exit */ | |
184 | LOG_CloseParentFd(); | |
185 | ||
186 | CNF_AddSources(); | |
187 | CNF_AddBroadcasts(); | |
188 | ||
189 | NSR_SetSourceResolvingEndHandler(ntp_source_resolving_end); | |
190 | NSR_ResolveSources(); | |
191 | } | |
192 | ||
193 | /* ================================================== */ | |
194 | ||
7fda9c67 ML |
195 | static void |
196 | reference_mode_end(int result) | |
197 | { | |
198 | switch (ref_mode) { | |
199 | case REF_ModeNormal: | |
70feea48 ML |
200 | case REF_ModeUpdateOnce: |
201 | case REF_ModePrintOnce: | |
93b66ac1 ML |
202 | exit_status = !result; |
203 | SCH_QuitProgram(); | |
7fda9c67 ML |
204 | break; |
205 | case REF_ModeInitStepSlew: | |
ead3ca14 ML |
206 | /* Switch to the normal mode, the delay is used to prevent polling |
207 | interval shorter than the burst interval if some configured servers | |
208 | were used also for initstepslew */ | |
209 | SCH_AddTimeoutByDelay(2.0, post_init_ntp_hook, NULL); | |
7fda9c67 ML |
210 | break; |
211 | default: | |
212 | assert(0); | |
213 | } | |
214 | } | |
215 | ||
216 | /* ================================================== */ | |
217 | ||
88840341 RC |
218 | static void |
219 | post_init_rtc_hook(void *anything) | |
220 | { | |
7fda9c67 ML |
221 | if (CNF_GetInitSources() > 0) { |
222 | CNF_AddInitSources(); | |
779e40ed | 223 | NSR_StartSources(); |
7fda9c67 ML |
224 | assert(REF_GetMode() != REF_ModeNormal); |
225 | /* Wait for mode end notification */ | |
226 | } else { | |
227 | (post_init_ntp_hook)(NULL); | |
228 | } | |
88840341 RC |
229 | } |
230 | ||
231 | /* ================================================== */ | |
232 | /* Return 1 if the process exists on the system. */ | |
233 | ||
234 | static int | |
235 | does_process_exist(int pid) | |
236 | { | |
237 | int status; | |
238 | status = getsid(pid); | |
239 | if (status >= 0) { | |
240 | return 1; | |
241 | } else { | |
242 | return 0; | |
243 | } | |
244 | } | |
245 | ||
246 | /* ================================================== */ | |
247 | ||
248 | static int | |
249 | maybe_another_chronyd_running(int *other_pid) | |
250 | { | |
251 | const char *pidfile = CNF_GetPidFile(); | |
252 | FILE *in; | |
253 | int pid, count; | |
254 | ||
255 | *other_pid = 0; | |
256 | ||
257 | in = fopen(pidfile, "r"); | |
258 | if (!in) return 0; | |
259 | ||
260 | count = fscanf(in, "%d", &pid); | |
261 | fclose(in); | |
262 | ||
263 | if (count != 1) return 0; | |
264 | ||
265 | *other_pid = pid; | |
266 | return does_process_exist(pid); | |
267 | ||
268 | } | |
269 | ||
270 | /* ================================================== */ | |
271 | ||
272 | static void | |
273 | write_lockfile(void) | |
274 | { | |
275 | const char *pidfile = CNF_GetPidFile(); | |
276 | FILE *out; | |
277 | ||
278 | out = fopen(pidfile, "w"); | |
279 | if (!out) { | |
fe35de69 | 280 | LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile); |
88840341 | 281 | } else { |
f444561a | 282 | fprintf(out, "%d\n", (int)getpid()); |
88840341 RC |
283 | fclose(out); |
284 | } | |
285 | } | |
286 | ||
287 | /* ================================================== */ | |
288 | ||
fe4b661f ML |
289 | static void |
290 | go_daemon(void) | |
291 | { | |
1d2a0856 ML |
292 | int pid, fd, pipefd[2]; |
293 | ||
294 | /* Create pipe which will the daemon use to notify the grandparent | |
295 | when it's initialised or send an error message */ | |
296 | if (pipe(pipefd)) { | |
fe35de69 | 297 | LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno)); |
1d2a0856 | 298 | } |
fe4b661f ML |
299 | |
300 | /* Does this preserve existing signal handlers? */ | |
301 | pid = fork(); | |
302 | ||
303 | if (pid < 0) { | |
fe35de69 | 304 | LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); |
fe4b661f | 305 | } else if (pid > 0) { |
1d2a0856 ML |
306 | /* In the 'grandparent' */ |
307 | char message[1024]; | |
308 | int r; | |
309 | ||
310 | close(pipefd[1]); | |
311 | r = read(pipefd[0], message, sizeof (message)); | |
312 | if (r) { | |
313 | if (r > 0) { | |
314 | /* Print the error message from the child */ | |
315 | fprintf(stderr, "%.1024s\n", message); | |
316 | } | |
317 | exit(1); | |
318 | } else | |
319 | exit(0); | |
fe4b661f | 320 | } else { |
1d2a0856 | 321 | close(pipefd[0]); |
fe4b661f ML |
322 | |
323 | setsid(); | |
324 | ||
325 | /* Do 2nd fork, as-per recommended practice for launching daemons. */ | |
326 | pid = fork(); | |
327 | ||
328 | if (pid < 0) { | |
fe35de69 | 329 | LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); |
fe4b661f ML |
330 | } else if (pid > 0) { |
331 | exit(0); /* In the 'parent' */ | |
332 | } else { | |
333 | /* In the child we want to leave running as the daemon */ | |
334 | ||
2a305d8e ML |
335 | /* Change current directory to / */ |
336 | if (chdir("/") < 0) { | |
fe35de69 | 337 | LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno)); |
2a305d8e ML |
338 | } |
339 | ||
1d2a0856 ML |
340 | /* Don't keep stdin/out/err from before. But don't close |
341 | the parent pipe yet. */ | |
fe4b661f | 342 | for (fd=0; fd<1024; fd++) { |
1d2a0856 ML |
343 | if (fd != pipefd[1]) |
344 | close(fd); | |
fe4b661f | 345 | } |
919b5b5a | 346 | |
1d2a0856 | 347 | LOG_SetParentFd(pipefd[1]); |
fe4b661f ML |
348 | } |
349 | } | |
fe4b661f ML |
350 | } |
351 | ||
352 | /* ================================================== */ | |
353 | ||
88840341 RC |
354 | int main |
355 | (int argc, char **argv) | |
356 | { | |
0f8def4c | 357 | const char *conf_file = DEFAULT_CONF_FILE; |
1cda2db4 | 358 | const char *progname = argv[0]; |
be42b4ee | 359 | char *user = NULL; |
6402350c | 360 | struct passwd *pw; |
72d0b3c9 | 361 | int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; |
f2eb6b16 | 362 | int do_init_rtc = 0, restarted = 0; |
88840341 | 363 | int other_pid; |
434faeec | 364 | int scfilter_level = 0, lock_memory = 0, sched_priority = 0; |
788e7fcd | 365 | int system_log = 1; |
3edd3fe5 | 366 | int config_args = 0; |
35e662d8 | 367 | |
bafb434f ML |
368 | do_platform_checks(); |
369 | ||
88840341 RC |
370 | LOG_Initialise(); |
371 | ||
372 | /* Parse command line options */ | |
373 | while (++argv, (--argc)>0) { | |
374 | ||
375 | if (!strcmp("-f", *argv)) { | |
376 | ++argv, --argc; | |
377 | conf_file = *argv; | |
35e662d8 JH |
378 | } else if (!strcmp("-P", *argv)) { |
379 | ++argv, --argc; | |
e3234465 ML |
380 | if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) { |
381 | LOG_FATAL(LOGF_Main, "Bad scheduler priority"); | |
35e662d8 | 382 | } |
35e662d8 | 383 | } else if (!strcmp("-m", *argv)) { |
e3234465 | 384 | lock_memory = 1; |
88840341 RC |
385 | } else if (!strcmp("-r", *argv)) { |
386 | reload = 1; | |
f2eb6b16 ML |
387 | } else if (!strcmp("-R", *argv)) { |
388 | restarted = 1; | |
be42b4ee ML |
389 | } else if (!strcmp("-u", *argv)) { |
390 | ++argv, --argc; | |
e3234465 ML |
391 | if (argc == 0) { |
392 | LOG_FATAL(LOGF_Main, "Missing user name"); | |
393 | } else { | |
394 | user = *argv; | |
395 | } | |
434faeec ML |
396 | } else if (!strcmp("-F", *argv)) { |
397 | ++argv, --argc; | |
398 | if (argc == 0 || sscanf(*argv, "%d", &scfilter_level) != 1) | |
399 | LOG_FATAL(LOGF_Main, "Bad syscall filter level"); | |
88840341 RC |
400 | } else if (!strcmp("-s", *argv)) { |
401 | do_init_rtc = 1; | |
402 | } else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) { | |
403 | /* This write to the terminal is OK, it comes before we turn into a daemon */ | |
f2710d5b | 404 | printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES); |
1769b8ea | 405 | return 0; |
fe4b661f ML |
406 | } else if (!strcmp("-n", *argv)) { |
407 | nofork = 1; | |
88840341 | 408 | } else if (!strcmp("-d", *argv)) { |
4bbc5520 | 409 | debug++; |
fe4b661f | 410 | nofork = 1; |
788e7fcd | 411 | system_log = 0; |
70feea48 ML |
412 | } else if (!strcmp("-q", *argv)) { |
413 | ref_mode = REF_ModeUpdateOnce; | |
414 | nofork = 1; | |
415 | system_log = 0; | |
416 | } else if (!strcmp("-Q", *argv)) { | |
417 | ref_mode = REF_ModePrintOnce; | |
418 | nofork = 1; | |
419 | system_log = 0; | |
fbd20c42 | 420 | } else if (!strcmp("-4", *argv)) { |
72d0b3c9 | 421 | address_family = IPADDR_INET4; |
fbd20c42 | 422 | } else if (!strcmp("-6", *argv)) { |
72d0b3c9 | 423 | address_family = IPADDR_INET6; |
1cda2db4 ML |
424 | } else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) { |
425 | printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n", | |
426 | progname); | |
1769b8ea | 427 | return 0; |
3edd3fe5 | 428 | } else if (*argv[0] == '-') { |
dbb550e6 | 429 | LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv); |
3edd3fe5 ML |
430 | } else { |
431 | /* Process remaining arguments and configuration lines */ | |
432 | config_args = argc; | |
433 | break; | |
88840341 RC |
434 | } |
435 | } | |
436 | ||
88840341 RC |
437 | if (getuid() != 0) { |
438 | /* This write to the terminal is OK, it comes before we turn into a daemon */ | |
439 | fprintf(stderr,"Not superuser\n"); | |
1769b8ea | 440 | return 1; |
88840341 RC |
441 | } |
442 | ||
88840341 | 443 | /* Turn into a daemon */ |
fe4b661f ML |
444 | if (!nofork) { |
445 | go_daemon(); | |
446 | } | |
447 | ||
788e7fcd | 448 | if (system_log) { |
fe4b661f | 449 | LOG_OpenSystemLog(); |
88840341 RC |
450 | } |
451 | ||
788e7fcd | 452 | LOG_SetDebugLevel(debug); |
4bbc5520 | 453 | |
f2710d5b ML |
454 | LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)", |
455 | CHRONY_VERSION, CHRONYD_FEATURES); | |
cb905507 | 456 | |
72d0b3c9 ML |
457 | DNS_SetAddressFamily(address_family); |
458 | ||
f6ed7844 | 459 | CNF_Initialise(restarted); |
3edd3fe5 ML |
460 | |
461 | /* Parse the config file or the remaining command line arguments */ | |
462 | if (!config_args) { | |
463 | CNF_ReadFile(conf_file); | |
464 | } else { | |
465 | do { | |
466 | CNF_ParseLine(NULL, config_args - argc + 1, *argv); | |
467 | } while (++argv, --argc); | |
468 | } | |
1e35b268 | 469 | |
88840341 RC |
470 | /* Check whether another chronyd may already be running. Do this after |
471 | * forking, so that message logging goes to the right place (i.e. syslog), in | |
472 | * case this chronyd is being run from a boot script. */ | |
473 | if (maybe_another_chronyd_running(&other_pid)) { | |
474 | LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)", | |
475 | other_pid, CNF_GetPidFile()); | |
88840341 RC |
476 | } |
477 | ||
478 | /* Write our lockfile to prevent other chronyds running. This has *GOT* to | |
479 | * be done *AFTER* the daemon-creation fork() */ | |
480 | write_lockfile(); | |
88840341 | 481 | |
88840341 RC |
482 | LCL_Initialise(); |
483 | SCH_Initialise(); | |
484 | SYS_Initialise(); | |
c6e06420 | 485 | RTC_Initialise(do_init_rtc); |
3a222336 | 486 | SRC_Initialise(); |
ac30bb06 | 487 | RCL_Initialise(); |
1c901b82 | 488 | KEY_Initialise(); |
610f2340 | 489 | PRV_Initialise(); |
be42b4ee | 490 | |
8854c00d ML |
491 | /* Open privileged ports before dropping root */ |
492 | CAM_Initialise(address_family); | |
493 | NIO_Initialise(address_family); | |
494 | NCR_Initialise(); | |
495 | CNF_SetupAccessRestrictions(); | |
496 | ||
e3234465 ML |
497 | /* Command-line switch must have priority */ |
498 | if (!sched_priority) { | |
499 | sched_priority = CNF_GetSchedPriority(); | |
500 | } | |
501 | if (sched_priority) { | |
502 | SYS_SetScheduler(sched_priority); | |
96759116 ML |
503 | } |
504 | ||
e3234465 ML |
505 | if (lock_memory || CNF_GetLockMemory()) { |
506 | SYS_LockMemory(); | |
96759116 ML |
507 | } |
508 | ||
edda0c60 ML |
509 | if (!user) { |
510 | user = CNF_GetUser(); | |
511 | } | |
6402350c | 512 | |
e7100e10 ML |
513 | if ((pw = getpwnam(user)) == NULL) |
514 | LOG_FATAL(LOGF_Main, "Could not get %s uid/gid", user); | |
6402350c | 515 | |
6d42dd86 ML |
516 | /* Create all directories before dropping root */ |
517 | CNF_CreateDirs(pw->pw_uid, pw->pw_gid); | |
518 | ||
e7100e10 ML |
519 | /* Drop root privileges if the user has non-zero uid or gid */ |
520 | if (pw->pw_uid || pw->pw_gid) | |
6402350c | 521 | SYS_DropRoot(pw->pw_uid, pw->pw_gid); |
be42b4ee | 522 | |
88840341 RC |
523 | REF_Initialise(); |
524 | SST_Initialise(); | |
88840341 | 525 | NSR_Initialise(); |
88840341 | 526 | CLG_Initialise(); |
88840341 | 527 | MNL_Initialise(); |
c386d117 | 528 | TMC_Initialise(); |
7f45eb79 | 529 | SMT_Initialise(); |
88840341 RC |
530 | |
531 | /* From now on, it is safe to do finalisation on exit */ | |
532 | initialised = 1; | |
533 | ||
ea2858b3 ML |
534 | UTI_SetQuitSignalsHandler(signal_cleanup); |
535 | ||
8854c00d | 536 | CAM_OpenUnixSocket(); |
46951b85 | 537 | |
434faeec ML |
538 | if (scfilter_level) |
539 | SYS_EnableSystemCallFilter(scfilter_level); | |
540 | ||
70feea48 | 541 | if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) { |
7fda9c67 ML |
542 | ref_mode = REF_ModeInitStepSlew; |
543 | } | |
544 | ||
545 | REF_SetModeEndHandler(reference_mode_end); | |
546 | REF_SetMode(ref_mode); | |
547 | ||
88840341 RC |
548 | if (do_init_rtc) { |
549 | RTC_TimeInit(post_init_rtc_hook, NULL); | |
550 | } else { | |
551 | post_init_rtc_hook(NULL); | |
552 | } | |
553 | ||
88840341 RC |
554 | /* The program normally runs under control of the main loop in |
555 | the scheduler. */ | |
556 | SCH_MainLoop(); | |
557 | ||
0fc9b555 ML |
558 | LOG(LOGS_INFO, LOGF_Main, "chronyd exiting"); |
559 | ||
88840341 RC |
560 | MAI_CleanupAndExit(); |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | /* ================================================== */ |