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