]>
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 |
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" | |
47 | #include "acquire.h" | |
48 | #include "manual.h" | |
88840341 | 49 | #include "rtc.h" |
ac30bb06 | 50 | #include "refclock.h" |
88840341 RC |
51 | #include "clientlog.h" |
52 | #include "broadcast.h" | |
fbd20c42 | 53 | #include "nameserv.h" |
c386d117 | 54 | #include "tempcomp.h" |
88840341 RC |
55 | |
56 | /* ================================================== */ | |
57 | ||
58 | /* Set when the initialisation chain has been completed. Prevents finalisation | |
59 | * chain being run if a fatal error happened early. */ | |
60 | ||
61 | static int initialised = 0; | |
62 | ||
63 | /* ================================================== */ | |
64 | ||
65 | static int reload = 0; | |
66 | ||
67 | /* ================================================== */ | |
68 | ||
69 | static void | |
70 | delete_pidfile(void) | |
71 | { | |
72 | const char *pidfile = CNF_GetPidFile(); | |
73 | /* Don't care if this fails, there's not a lot we can do */ | |
74 | unlink(pidfile); | |
75 | } | |
76 | ||
77 | /* ================================================== */ | |
78 | ||
1e7e7d32 | 79 | void |
88840341 RC |
80 | MAI_CleanupAndExit(void) |
81 | { | |
82 | if (!initialised) exit(0); | |
83 | ||
84 | if (CNF_GetDumpOnExit()) { | |
85 | SRC_DumpSources(); | |
86 | } | |
87 | ||
c386d117 | 88 | TMC_Finalise(); |
88840341 RC |
89 | MNL_Finalise(); |
90 | ACQ_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 | ||
111 | exit(0); | |
112 | } | |
113 | ||
114 | /* ================================================== */ | |
115 | ||
116 | static void | |
117 | signal_cleanup(int x) | |
118 | { | |
9416a24f ML |
119 | if (!initialised) exit(0); |
120 | SCH_QuitProgram(); | |
88840341 RC |
121 | } |
122 | ||
123 | /* ================================================== */ | |
124 | ||
125 | static void | |
126 | post_acquire_hook(void *anything) | |
127 | { | |
1d2a0856 ML |
128 | /* Close the pipe to the foreground process so it can exit */ |
129 | LOG_CloseParentFd(); | |
88840341 RC |
130 | |
131 | CNF_AddSources(); | |
132 | CNF_AddBroadcasts(); | |
133 | if (reload) { | |
134 | /* Note, we want reload to come well after the initialisation from | |
135 | the real time clock - this gives us a fighting chance that the | |
136 | system-clock scale for the reloaded samples still has a | |
137 | semblence of validity about it. */ | |
138 | SRC_ReloadSources(); | |
139 | } | |
140 | CNF_SetupAccessRestrictions(); | |
141 | ||
142 | RTC_StartMeasurements(); | |
ac30bb06 | 143 | RCL_StartRefclocks(); |
88840341 RC |
144 | } |
145 | ||
146 | /* ================================================== */ | |
147 | ||
148 | static void | |
149 | post_init_rtc_hook(void *anything) | |
150 | { | |
151 | CNF_ProcessInitStepSlew(post_acquire_hook, NULL); | |
152 | } | |
153 | ||
154 | /* ================================================== */ | |
155 | /* Return 1 if the process exists on the system. */ | |
156 | ||
157 | static int | |
158 | does_process_exist(int pid) | |
159 | { | |
160 | int status; | |
161 | status = getsid(pid); | |
162 | if (status >= 0) { | |
163 | return 1; | |
164 | } else { | |
165 | return 0; | |
166 | } | |
167 | } | |
168 | ||
169 | /* ================================================== */ | |
170 | ||
171 | static int | |
172 | maybe_another_chronyd_running(int *other_pid) | |
173 | { | |
174 | const char *pidfile = CNF_GetPidFile(); | |
175 | FILE *in; | |
176 | int pid, count; | |
177 | ||
178 | *other_pid = 0; | |
179 | ||
180 | in = fopen(pidfile, "r"); | |
181 | if (!in) return 0; | |
182 | ||
183 | count = fscanf(in, "%d", &pid); | |
184 | fclose(in); | |
185 | ||
186 | if (count != 1) return 0; | |
187 | ||
188 | *other_pid = pid; | |
189 | return does_process_exist(pid); | |
190 | ||
191 | } | |
192 | ||
193 | /* ================================================== */ | |
194 | ||
195 | static void | |
196 | write_lockfile(void) | |
197 | { | |
198 | const char *pidfile = CNF_GetPidFile(); | |
199 | FILE *out; | |
200 | ||
201 | out = fopen(pidfile, "w"); | |
202 | if (!out) { | |
203 | LOG(LOGS_ERR, LOGF_Main, "could not open lockfile %s for writing", pidfile); | |
204 | } else { | |
205 | fprintf(out, "%d\n", getpid()); | |
206 | fclose(out); | |
207 | } | |
208 | } | |
209 | ||
210 | /* ================================================== */ | |
211 | ||
fe4b661f ML |
212 | static void |
213 | go_daemon(void) | |
214 | { | |
215 | #ifdef WINNT | |
216 | ||
217 | ||
218 | #else | |
219 | ||
1d2a0856 ML |
220 | int pid, fd, pipefd[2]; |
221 | ||
222 | /* Create pipe which will the daemon use to notify the grandparent | |
223 | when it's initialised or send an error message */ | |
224 | if (pipe(pipefd)) { | |
225 | LOG(LOGS_ERR, LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno)); | |
226 | } | |
fe4b661f ML |
227 | |
228 | /* Does this preserve existing signal handlers? */ | |
229 | pid = fork(); | |
230 | ||
231 | if (pid < 0) { | |
232 | LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); | |
233 | } else if (pid > 0) { | |
1d2a0856 ML |
234 | /* In the 'grandparent' */ |
235 | char message[1024]; | |
236 | int r; | |
237 | ||
238 | close(pipefd[1]); | |
239 | r = read(pipefd[0], message, sizeof (message)); | |
240 | if (r) { | |
241 | if (r > 0) { | |
242 | /* Print the error message from the child */ | |
243 | fprintf(stderr, "%.1024s\n", message); | |
244 | } | |
245 | exit(1); | |
246 | } else | |
247 | exit(0); | |
fe4b661f | 248 | } else { |
1d2a0856 | 249 | close(pipefd[0]); |
fe4b661f ML |
250 | |
251 | setsid(); | |
252 | ||
253 | /* Do 2nd fork, as-per recommended practice for launching daemons. */ | |
254 | pid = fork(); | |
255 | ||
256 | if (pid < 0) { | |
257 | LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); | |
258 | } else if (pid > 0) { | |
259 | exit(0); /* In the 'parent' */ | |
260 | } else { | |
261 | /* In the child we want to leave running as the daemon */ | |
262 | ||
2a305d8e ML |
263 | /* Change current directory to / */ |
264 | if (chdir("/") < 0) { | |
265 | LOG(LOGS_ERR, LOGF_Logging, "Could not chdir to / : %s", strerror(errno)); | |
266 | } | |
267 | ||
1d2a0856 ML |
268 | /* Don't keep stdin/out/err from before. But don't close |
269 | the parent pipe yet. */ | |
fe4b661f | 270 | for (fd=0; fd<1024; fd++) { |
1d2a0856 ML |
271 | if (fd != pipefd[1]) |
272 | close(fd); | |
fe4b661f | 273 | } |
919b5b5a | 274 | |
1d2a0856 | 275 | LOG_SetParentFd(pipefd[1]); |
fe4b661f ML |
276 | } |
277 | } | |
278 | ||
279 | #endif | |
280 | } | |
281 | ||
282 | /* ================================================== */ | |
283 | ||
88840341 RC |
284 | int main |
285 | (int argc, char **argv) | |
286 | { | |
0f8def4c | 287 | const char *conf_file = DEFAULT_CONF_FILE; |
be42b4ee | 288 | char *user = NULL; |
72d0b3c9 | 289 | int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; |
f2eb6b16 | 290 | int do_init_rtc = 0, restarted = 0; |
88840341 | 291 | int other_pid; |
e3234465 | 292 | int lock_memory = 0, sched_priority = 0; |
35e662d8 | 293 | |
88840341 RC |
294 | LOG_Initialise(); |
295 | ||
296 | /* Parse command line options */ | |
297 | while (++argv, (--argc)>0) { | |
298 | ||
299 | if (!strcmp("-f", *argv)) { | |
300 | ++argv, --argc; | |
301 | conf_file = *argv; | |
35e662d8 JH |
302 | } else if (!strcmp("-P", *argv)) { |
303 | ++argv, --argc; | |
e3234465 ML |
304 | if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) { |
305 | LOG_FATAL(LOGF_Main, "Bad scheduler priority"); | |
35e662d8 | 306 | } |
35e662d8 | 307 | } else if (!strcmp("-m", *argv)) { |
e3234465 | 308 | lock_memory = 1; |
88840341 RC |
309 | } else if (!strcmp("-r", *argv)) { |
310 | reload = 1; | |
f2eb6b16 ML |
311 | } else if (!strcmp("-R", *argv)) { |
312 | restarted = 1; | |
be42b4ee ML |
313 | } else if (!strcmp("-u", *argv)) { |
314 | ++argv, --argc; | |
e3234465 ML |
315 | if (argc == 0) { |
316 | LOG_FATAL(LOGF_Main, "Missing user name"); | |
317 | } else { | |
318 | user = *argv; | |
319 | } | |
88840341 RC |
320 | } else if (!strcmp("-s", *argv)) { |
321 | do_init_rtc = 1; | |
322 | } else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) { | |
323 | /* This write to the terminal is OK, it comes before we turn into a daemon */ | |
4ba3dd66 | 324 | printf("chronyd (chrony) version %s\n", CHRONY_VERSION); |
88840341 | 325 | exit(0); |
fe4b661f ML |
326 | } else if (!strcmp("-n", *argv)) { |
327 | nofork = 1; | |
88840341 RC |
328 | } else if (!strcmp("-d", *argv)) { |
329 | debug = 1; | |
fe4b661f | 330 | nofork = 1; |
fbd20c42 | 331 | } else if (!strcmp("-4", *argv)) { |
72d0b3c9 | 332 | address_family = IPADDR_INET4; |
fbd20c42 | 333 | } else if (!strcmp("-6", *argv)) { |
72d0b3c9 | 334 | address_family = IPADDR_INET6; |
88840341 | 335 | } else { |
dbb550e6 | 336 | LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv); |
88840341 RC |
337 | } |
338 | } | |
339 | ||
88840341 RC |
340 | if (getuid() != 0) { |
341 | /* This write to the terminal is OK, it comes before we turn into a daemon */ | |
342 | fprintf(stderr,"Not superuser\n"); | |
343 | exit(1); | |
344 | } | |
345 | ||
88840341 | 346 | /* Turn into a daemon */ |
fe4b661f ML |
347 | if (!nofork) { |
348 | go_daemon(); | |
349 | } | |
350 | ||
88840341 | 351 | if (!debug) { |
fe4b661f | 352 | LOG_OpenSystemLog(); |
88840341 RC |
353 | } |
354 | ||
4ba3dd66 | 355 | LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting", CHRONY_VERSION); |
cb905507 | 356 | |
72d0b3c9 ML |
357 | DNS_SetAddressFamily(address_family); |
358 | ||
f2eb6b16 | 359 | CNF_SetRestarted(restarted); |
1e35b268 ML |
360 | CNF_ReadFile(conf_file); |
361 | ||
88840341 RC |
362 | /* Check whether another chronyd may already be running. Do this after |
363 | * forking, so that message logging goes to the right place (i.e. syslog), in | |
364 | * case this chronyd is being run from a boot script. */ | |
365 | if (maybe_another_chronyd_running(&other_pid)) { | |
366 | LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)", | |
367 | other_pid, CNF_GetPidFile()); | |
88840341 RC |
368 | } |
369 | ||
370 | /* Write our lockfile to prevent other chronyds running. This has *GOT* to | |
371 | * be done *AFTER* the daemon-creation fork() */ | |
372 | write_lockfile(); | |
88840341 | 373 | |
88840341 RC |
374 | if (do_init_rtc) { |
375 | RTC_TimePreInit(); | |
376 | } | |
377 | ||
378 | LCL_Initialise(); | |
379 | SCH_Initialise(); | |
380 | SYS_Initialise(); | |
72d0b3c9 ML |
381 | NIO_Initialise(address_family); |
382 | CAM_Initialise(address_family); | |
be42b4ee | 383 | RTC_Initialise(); |
3a222336 | 384 | SRC_Initialise(); |
ac30bb06 | 385 | RCL_Initialise(); |
1c901b82 | 386 | KEY_Initialise(); |
be42b4ee | 387 | |
e3234465 ML |
388 | /* Command-line switch must have priority */ |
389 | if (!sched_priority) { | |
390 | sched_priority = CNF_GetSchedPriority(); | |
391 | } | |
392 | if (sched_priority) { | |
393 | SYS_SetScheduler(sched_priority); | |
96759116 ML |
394 | } |
395 | ||
e3234465 ML |
396 | if (lock_memory || CNF_GetLockMemory()) { |
397 | SYS_LockMemory(); | |
96759116 ML |
398 | } |
399 | ||
edda0c60 ML |
400 | if (!user) { |
401 | user = CNF_GetUser(); | |
402 | } | |
e3234465 | 403 | if (user) { |
be42b4ee | 404 | SYS_DropRoot(user); |
e3234465 | 405 | } |
be42b4ee | 406 | |
103a520a ML |
407 | LOG_CreateLogFileDir(); |
408 | ||
88840341 RC |
409 | REF_Initialise(); |
410 | SST_Initialise(); | |
88840341 RC |
411 | BRD_Initialise(); |
412 | NCR_Initialise(); | |
413 | NSR_Initialise(); | |
88840341 | 414 | CLG_Initialise(); |
88840341 RC |
415 | ACQ_Initialise(); |
416 | MNL_Initialise(); | |
c386d117 | 417 | TMC_Initialise(); |
88840341 RC |
418 | |
419 | /* From now on, it is safe to do finalisation on exit */ | |
420 | initialised = 1; | |
421 | ||
422 | if (do_init_rtc) { | |
423 | RTC_TimeInit(post_init_rtc_hook, NULL); | |
424 | } else { | |
425 | post_init_rtc_hook(NULL); | |
426 | } | |
427 | ||
428 | signal(SIGINT, signal_cleanup); | |
429 | signal(SIGTERM, signal_cleanup); | |
430 | #if !defined(WINNT) | |
431 | signal(SIGQUIT, signal_cleanup); | |
432 | signal(SIGHUP, signal_cleanup); | |
433 | #endif /* WINNT */ | |
434 | ||
435 | /* The program normally runs under control of the main loop in | |
436 | the scheduler. */ | |
437 | SCH_MainLoop(); | |
438 | ||
0fc9b555 ML |
439 | LOG(LOGS_INFO, LOGF_Main, "chronyd exiting"); |
440 | ||
88840341 RC |
441 | MAI_CleanupAndExit(); |
442 | ||
443 | return 0; | |
444 | } | |
445 | ||
446 | /* ================================================== */ |