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