1 /*
2 * msyslog - either send a message to the terminal or print it on
3 * the standard output.
4 *
5 * Converted to use varargs, much better ... jks
6 */
7
8 #include "config.h"
9
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
14
15 #include "ntp.h"
16 #include "ntp_debug.h"
17 #include "ntp_stdlib.h"
18 #include "ntp_syslog.h"
19 #include "lib_strbuf.h"
20
21 /* start out with syslog and stderr, otherwise startup errors lost */
22 bool syslogit = true; /* log messages to syslog */
23 bool termlogit = true; /* duplicate to stdout/err */
24 bool termlogit_pid = true;
25 bool msyslog_include_timestamp = true;
26
27 static FILE * syslog_file;
28 static char * syslog_fname;
29 static char * syslog_abs_fname;
30
31 int debug;
32
33 /* libntp default ntp_syslogmask is all bits lit */
34 #define INIT_NTP_SYSLOGMASK ~(uint32_t)0
35 uint32_t ntp_syslogmask = INIT_NTP_SYSLOGMASK;
36
37 extern char * progname;
38
39 /* Declare the local functions */
40 #define TIMESTAMP_LEN 128
41 static void humanlogtime(char buf[TIMESTAMP_LEN]);
42 static void addto_syslog (int, const char *);
43
44
45 /* We don't want to clutter up the log with the year and day of the week,
46 etc.; just the minimal date and time. */
47 static void
humanlogtime(char buf[TIMESTAMP_LEN])48 humanlogtime(char buf[TIMESTAMP_LEN])
49 {
50 time_t cursec;
51 struct tm tmbuf, *tm;
52
53 cursec = time(NULL);
54 tm = localtime_r(&cursec, &tmbuf);
55 if (!tm) {
56 strlcpy(buf, "-- --- --:--:--", TIMESTAMP_LEN);
57 return;
58 }
59
60 #ifdef ENABLE_CLASSIC_MODE
61 const char * const months[12] = {
62 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
63 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
64 };
65
66 snprintf(buf, TIMESTAMP_LEN, "%2d %s %02d:%02d:%02d",
67 tm->tm_mday, months[tm->tm_mon],
68 tm->tm_hour, tm->tm_min, tm->tm_sec);
69 #else
70 /* ISO 8601 is a better format, sort order equals time order */
71 snprintf(buf, TIMESTAMP_LEN, "%04d-%02d-%02dT%02d:%02d:%02d",
72 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
73 tm->tm_hour, tm->tm_min, tm->tm_sec);
74 #endif /* ENABLE_CLASSIC_MODE */
75 }
76
77
78 /*
79 * addto_syslog()
80 * This routine adds the contents of a buffer to the syslog or an
81 * application-specific logfile.
82 */
83 static void
addto_syslog(int level,const char * msg)84 addto_syslog(
85 int level,
86 const char * msg
87 )
88 {
89 static char * prevcall_progname;
90 static char * prog;
91 const char nl[] = "\n";
92 const char empty[] = "";
93 FILE * term_file;
94 bool log_to_term;
95 bool log_to_file;
96 int pid;
97 const char * nl_or_empty;
98 const char * human_time;
99 char tbuf[TIMESTAMP_LEN];
100
101 /* setup program basename static var prog if needed */
102 if (progname != prevcall_progname) {
103 prevcall_progname = progname;
104 prog = strrchr(progname, DIR_SEP);
105 if (prog != NULL) {
106 prog++;
107 } else {
108 prog = progname;
109 }
110 }
111
112 log_to_term = termlogit;
113 log_to_file = false;
114 if (syslogit)
115 syslog(level, "%s", msg);
116 else
117 if (syslog_file != NULL)
118 log_to_file = true;
119 #if defined(DEBUG) && DEBUG
120 if (debug > 0) /* SPECIAL DEBUG */
121 log_to_term = true;
122 #endif
123 if (!(log_to_file || log_to_term))
124 return;
125
126 /* syslog() adds the timestamp, name, and pid */
127 if (msyslog_include_timestamp) {
128 humanlogtime(tbuf);
129 human_time = tbuf;
130 } else /* suppress gcc pot. uninit. warning */
131 human_time = NULL;
132 if (termlogit_pid || log_to_file)
133 pid = getpid();
134 else /* suppress gcc pot. uninit. warning */
135 pid = -1;
136
137 /* syslog() adds trailing \n if not present */
138 if ('\n' != msg[strlen(msg) - 1]) {
139 nl_or_empty = nl;
140 } else {
141 nl_or_empty = empty;
142 }
143
144 if (log_to_term) {
145 term_file = (level <= LOG_ERR)
146 ? stderr
147 : stdout;
148 if (msyslog_include_timestamp)
149 fprintf(term_file, "%s ", human_time);
150 if (termlogit_pid)
151 fprintf(term_file, "%s[%d]: ", prog, pid);
152 fprintf(term_file, "%s%s", msg, nl_or_empty);
153 fflush(term_file);
154 }
155
156 if (log_to_file) {
157 /*
158 * Thread-safe write, the de-facto way. It's not
159 * actually guaranteed by standards that a write of
160 * PIPE_BUF chars or less is atomic anywhere but on a
161 * pipe. In ancient times this was 512 and happened to
162 * be equal to the usual size of a hardware disk sector,
163 * which was what really bounded atomicity. The actual
164 * answer under POSIX is SSIZE_MAX, which is far larger
165 * than we want or need to allocate here.
166 */
167 char buf[PIPE_BUF];
168 buf[0] = '\0';
169 if (msyslog_include_timestamp)
170 snprintf(buf, sizeof(buf), "%s ", human_time);
171 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
172 "%s[%d]: %s%s", prog, pid, msg, nl_or_empty);
173 IGNORE(write(fileno(syslog_file), buf, strlen(buf)));
174 }
175 }
176
177
178 void
msyslog(int level,const char * fmt,...)179 msyslog(
180 int level,
181 const char * fmt,
182 ...
183 )
184 {
185 char buf[1024];
186 va_list ap;
187
188 va_start(ap, fmt);
189 vsnprintf(buf, sizeof(buf), fmt, ap);
190 va_end(ap);
191 addto_syslog(level, buf);
192 }
193
194
195 /*
196 * Initialize the logging
197 *
198 * Called once per process, including forked children.
199 */
200 void
init_logging(const char * name,uint32_t def_syslogmask,int is_daemon)201 init_logging(
202 const char * name,
203 uint32_t def_syslogmask,
204 int is_daemon
205 )
206 {
207 static bool was_daemon;
208 const char * cp;
209 const char * pname;
210
211 /*
212 * ntpd defaults to only logging sync-category events, when
213 * NLOG() is used to conditionalize. Other libntp clients
214 * leave it alone so that all NLOG() conditionals will fire.
215 * This presumes all bits lit in ntp_syslogmask can't be
216 * configured via logconfig and all lit is thereby a sentinel
217 * that ntp_syslogmask is still at its default from libntp,
218 * keeping in mind this function is called in forked children
219 * where it has already been called in the parent earlier.
220 * Forked children pass 0 for def_syslogmask.
221 */
222 if (INIT_NTP_SYSLOGMASK == ntp_syslogmask &&
223 0 != def_syslogmask)
224 ntp_syslogmask = def_syslogmask; /* set more via logconfig */
225
226 /*
227 * Logging. This may actually work on the gizmo board. Find a name
228 * to log with by using the basename
229 */
230 cp = strrchr(name, DIR_SEP);
231 if (NULL == cp) {
232 pname = name;
233 } else {
234 pname = 1 + cp; /* skip DIR_SEP */
235 }
236 progname = estrdup(pname);
237
238 if (is_daemon)
239 was_daemon = true;
240 # ifndef LOG_DAEMON
241 openlog(progname, LOG_PID);
242 # else /* LOG_DAEMON */
243
244 # ifndef LOG_NTP
245 # define LOG_NTP LOG_DAEMON
246 # endif
247 openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon)
248 ? LOG_NTP
249 : 0);
250 # ifdef DEBUG
251 if (debug) /* SPECIAL DEBUG */
252 setlogmask(LOG_UPTO(LOG_DEBUG));
253 else
254 # endif /* DEBUG */
255 setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
256 # endif /* LOG_DAEMON */
257 }
258
259
260 /*
261 * change_logfile()
262 *
263 * Used to change from syslog to a logfile, or from one logfile to
264 * another, and to reopen logfiles after forking. On systems where
265 * ntpd forks, deals with converting relative logfile paths to
266 * absolute (root-based) because we reopen logfiles after the current
267 * directory has changed.
268 */
269 int
change_logfile(const char * fname,bool leave_crumbs)270 change_logfile(
271 const char * fname,
272 bool leave_crumbs
273 )
274 {
275 FILE * new_file;
276 const char * log_fname;
277 char * abs_fname;
278 char curdir[512];
279 size_t cd_octets;
280 size_t octets;
281
282 //REQUIRE(fname != NULL);
283 log_fname = fname;
284
285 /*
286 * In a forked child of a parent which is logging to a file
287 * instead of syslog, syslog_file will be NULL and both
288 * syslog_fname and syslog_abs_fname will be non-NULL.
289 * If we are given the same filename previously opened
290 * and it's still open, there's nothing to do here.
291 */
292 if (syslog_file != NULL && syslog_fname != NULL &&
293 0 == strcmp(syslog_fname, log_fname)) {
294 return 0;
295 }
296
297 if (0 == strcmp(log_fname, "stderr")) {
298 new_file = stderr;
299 abs_fname = estrdup(log_fname);
300 } else if (0 == strcmp(log_fname, "stdout")) {
301 new_file = stdout;
302 abs_fname = estrdup(log_fname);
303 } else {
304 if (syslog_fname != NULL &&
305 0 == strcmp(log_fname, syslog_fname)) {
306 log_fname = syslog_abs_fname;
307 }
308 if (log_fname != syslog_abs_fname &&
309 DIR_SEP != log_fname[0] &&
310 0 != strcmp(log_fname, "stderr") &&
311 0 != strcmp(log_fname, "stdout") &&
312 NULL != getcwd(curdir, sizeof(curdir))) {
313 cd_octets = strlen(curdir);
314 /* trim any trailing '/' */
315 if (cd_octets > 1 &&
316 DIR_SEP == curdir[cd_octets - 1])
317 cd_octets--;
318 octets = cd_octets;
319 octets += 1; /* separator '/' */
320 octets += strlen(log_fname);
321 octets += 1; /* NUL terminator */
322 abs_fname = emalloc(octets);
323 snprintf(abs_fname, octets, "%.*s%c%s",
324 (int)cd_octets, curdir, DIR_SEP,
325 log_fname);
326 } else
327 abs_fname = estrdup(log_fname);
328 DPRINT(1, ("attempting to open log %s\n", abs_fname));
329 new_file = fopen(abs_fname, "a");
330 }
331
332 if (NULL == new_file) {
333 free(abs_fname);
334 return -1;
335 }
336
337 /* leave a pointer in the old log */
338 if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname))
339 msyslog(LOG_NOTICE, "LOG: switching logging to file %s",
340 abs_fname);
341
342 if (syslog_file != NULL &&
343 syslog_file != stderr && syslog_file != stdout &&
344 fileno(syslog_file) != fileno(new_file)) {
345 fclose(syslog_file);
346 }
347 syslog_file = new_file;
348 if (log_fname == syslog_abs_fname) {
349 free(abs_fname);
350 } else {
351 if (syslog_abs_fname != NULL &&
352 syslog_abs_fname != syslog_fname) {
353 free(syslog_abs_fname);
354 }
355 if (syslog_fname != NULL) {
356 free(syslog_fname);
357 }
358 syslog_fname = estrdup(log_fname);
359 syslog_abs_fname = abs_fname;
360 }
361 syslogit = false;
362
363 return 0;
364 }
365
366
367 /*
368 * setup_logfile()
369 *
370 * Redirect logging to a file if requested with -l/--logfile or via
371 * ntp.conf logfile directive.
372 *
373 * This routine is invoked three different times in the sequence of a
374 * typical daemon ntpd with DNS lookups to do. First it is invoked in
375 * the original ntpd process, then again in the daemon after closing
376 * all descriptors. In both of those cases, ntp.conf has not been
377 * processed, so only -l/--logfile will trigger logfile redirection in
378 * those invocations. Finally, if DNS names are resolved, the worker
379 * child invokes this routine after its fork and close of all
380 * descriptors. In this case, ntp.conf has been processed and any
381 * "logfile" directive needs to be honored in the child as well.
382 */
383 void
setup_logfile(const char * name)384 setup_logfile(
385 const char * name
386 )
387 {
388 if (NULL == syslog_fname && NULL != name) {
389 if (-1 == change_logfile(name, true))
390 msyslog(LOG_ERR, "LOG: Cannot open log file %s, %s",
391 name, strerror(errno));
392 return ;
393 }
394 if (NULL == syslog_fname) {
395 return;
396 }
397
398 if (-1 == change_logfile(syslog_fname, false))
399 msyslog(LOG_ERR, "LOG: Cannot reopen log file %s, %s",
400 syslog_fname, strerror(errno));
401 }
402
403 /*
404 * check_logfile()
405 *
406 * reopen current logfile in case the old file has been renamed by logrotate
407 * called on SIGHUP and hourly
408 */
409
410 void
check_logfile(void)411 check_logfile(void)
412 {
413 FILE * new_file;
414
415 if (NULL == syslog_file) {
416 return; /* no log file, no clutter */
417 }
418
419 new_file = fopen(syslog_fname, "a");
420 if (NULL == new_file) {
421 msyslog(LOG_ERR, "LOG: check_logfile: couldn't open %s %s",
422 syslog_fname, strerror(errno));
423 return;
424 }
425
426 /* This is a hack to avoid cluttering the log if we would reuse
427 * the same file all over again.
428 * change_logfile compares filenos. That doesn't work.
429 * Can't check for a new file using a length of 0 since
430 * newsyslog on FreeBSD puts a "logfile turned over" message there.
431 * This seems to work.
432 */
433 if (ftell(syslog_file) == ftell(new_file)) {
434 fclose(new_file);
435 return;
436 }
437
438 msyslog(LOG_INFO, "LOG: check_logfile: closing old file");
439 fclose(syslog_file);
440 syslog_file = new_file;
441 msyslog(LOG_INFO, "LOG: check_logfile: using %s", syslog_fname);
442 }
443
444 /* Hack because there are 2 APIs to strerror_r() */
ntp_strerror_r(int errnum,char * buf,size_t buflen)445 void ntp_strerror_r(int errnum, char *buf, size_t buflen) {
446 #ifdef STRERROR_CHAR
447 char *answer = strerror_r(errnum, buf, buflen);
448 if (answer != buf) {
449 strlcpy(buf, answer, buflen);
450 }
451 #else
452 int answer = strerror_r(errnum, buf, buflen);
453 UNUSED_LOCAL(answer);
454 #endif
455 }
456
457