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