1 /* $NetBSD: msyslog.c,v 1.7 2020/05/29 20:15:38 christos Exp $ */
2
3 /*
4 * msyslog - either send a message to the terminal or print it on
5 * the standard output.
6 *
7 * Converted to use varargs, much better ... jks
8 */
9
10 #ifdef HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13
14 #include <sys/types.h>
15 #ifdef HAVE_UNISTD_H
16 # include <unistd.h>
17 #endif
18 #include <stdio.h>
19
20 #include "ntp_string.h"
21 #include "ntp.h"
22 #include "ntp_debug.h"
23 #include "ntp_syslog.h"
24
25 #ifdef SYS_WINNT
26 # include <stdarg.h>
27 # include "..\ports\winnt\libntp\messages.h"
28 #endif
29
30
31 int syslogit = TRUE;
32 int msyslog_term = FALSE; /* duplicate to stdout/err */
33 int msyslog_term_pid = TRUE;
34 int msyslog_include_timestamp = TRUE;
35 FILE * syslog_file;
36 char * syslog_fname;
37 char * syslog_abs_fname;
38
39 /* libntp default ntp_syslogmask is all bits lit */
40 #define INIT_NTP_SYSLOGMASK ~(u_int32)0
41 u_int32 ntp_syslogmask = INIT_NTP_SYSLOGMASK;
42
43 extern char const * progname;
44
45 /* Declare the local functions */
46 void addto_syslog (int, const char *);
47 #ifdef VSNPRINTF_PERCENT_M
48 #define format_errmsg(buf, len, fmt, error) (fmt)
49 #else
50 static const char *format_errmsg(char *, size_t, const char *, int)
51 NTP_FORMAT_ARG(3);
52
53 /* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */
54 static const char *
format_errmsg(char * nfmt,size_t lennfmt,const char * fmt,int errval)55 format_errmsg(
56 char * nfmt,
57 size_t lennfmt,
58 const char * fmt,
59 int errval
60 )
61 {
62 char errmsg[256];
63 char c;
64 char *n;
65 const char *f;
66 size_t len;
67
68 n = nfmt;
69 f = fmt;
70 while ((c = *f++) != '\0' && n < (nfmt + lennfmt - 1)) {
71 if (c != '%') {
72 *n++ = c;
73 continue;
74 }
75 if ((c = *f++) != 'm') {
76 *n++ = '%';
77 if ('\0' == c)
78 break;
79 *n++ = c;
80 continue;
81 }
82 errno_to_str(errval, errmsg, sizeof(errmsg));
83 len = strlen(errmsg);
84
85 /* Make sure we have enough space for the error message */
86 if ((n + len) < (nfmt + lennfmt - 1)) {
87 memcpy(n, errmsg, len);
88 n += len;
89 }
90 }
91 *n = '\0';
92 return nfmt;
93 }
94 #endif /* VSNPRINTF_PERCENT_M */
95
96
97 /*
98 * errno_to_str() - a thread-safe strerror() replacement.
99 * Hides the varied signatures of strerror_r().
100 * For Windows, we have:
101 * #define errno_to_str isc_strerror
102 */
103 #ifndef errno_to_str
104 void
errno_to_str(int err,char * buf,size_t bufsiz)105 errno_to_str(
106 int err,
107 char * buf,
108 size_t bufsiz
109 )
110 {
111 # if defined(STRERROR_R_CHAR_P) || !HAVE_DECL_STRERROR_R
112 char * pstatic;
113
114 buf[0] = '\0';
115 # ifdef STRERROR_R_CHAR_P
116 pstatic = strerror_r(err, buf, bufsiz);
117 # else
118 pstatic = strerror(err);
119 # endif
120 if (NULL == pstatic && '\0' == buf[0])
121 snprintf(buf, bufsiz, "%s(%d): errno %d",
122 # ifdef STRERROR_R_CHAR_P
123 "strerror_r",
124 # else
125 "strerror",
126 # endif
127 err, errno);
128 /* protect against believing an int return is a pointer */
129 else if (pstatic != buf && pstatic > (char *)bufsiz)
130 strlcpy(buf, pstatic, bufsiz);
131 # else
132 int rc;
133
134 rc = strerror_r(err, buf, bufsiz);
135 if (rc < 0)
136 snprintf(buf, bufsiz, "strerror_r(%d): errno %d",
137 err, errno);
138 # endif
139 }
140 #endif /* errno_to_str */
141
142
143 /*
144 * addto_syslog()
145 * This routine adds the contents of a buffer to the syslog or an
146 * application-specific logfile.
147 */
148 void
addto_syslog(int level,const char * msg)149 addto_syslog(
150 int level,
151 const char * msg
152 )
153 {
154 static char const * prevcall_progname;
155 static char const * prog;
156 const char nl[] = "\n";
157 const char empty[] = "";
158 FILE * term_file;
159 int log_to_term;
160 int log_to_file;
161 int pid;
162 const char * nl_or_empty;
163 const char * human_time;
164
165 /* setup program basename static var prog if needed */
166 if (progname != prevcall_progname) {
167 prevcall_progname = progname;
168 prog = strrchr(progname, DIR_SEP);
169 if (prog != NULL)
170 prog++;
171 else
172 prog = progname;
173 }
174
175 log_to_term = msyslog_term;
176 log_to_file = FALSE;
177 #if !defined(VMS) && !defined(SYS_VXWORKS)
178 if (syslogit)
179 syslog(level, "%s", msg);
180 else
181 #endif
182 if (syslog_file != NULL)
183 log_to_file = TRUE;
184 else
185 log_to_term = TRUE;
186 #if DEBUG
187 if (debug > 0)
188 log_to_term = TRUE;
189 #endif
190 if (!(log_to_file || log_to_term))
191 return;
192
193 /* syslog() adds the timestamp, name, and pid */
194 if (msyslog_include_timestamp)
195 human_time = humanlogtime();
196 else /* suppress gcc pot. uninit. warning */
197 human_time = NULL;
198 if (msyslog_term_pid || log_to_file)
199 pid = getpid();
200 else /* suppress gcc pot. uninit. warning */
201 pid = -1;
202
203 /* syslog() adds trailing \n if not present */
204 if ('\n' != msg[strlen(msg) - 1])
205 nl_or_empty = nl;
206 else
207 nl_or_empty = empty;
208
209 if (log_to_term) {
210 term_file = (level <= LOG_ERR)
211 ? stderr
212 : stdout;
213 if (msyslog_include_timestamp)
214 fprintf(term_file, "%s ", human_time);
215 if (msyslog_term_pid)
216 fprintf(term_file, "%s[%d]: ", prog, pid);
217 fprintf(term_file, "%s%s", msg, nl_or_empty);
218 fflush(term_file);
219 }
220
221 if (log_to_file) {
222 if (msyslog_include_timestamp)
223 fprintf(syslog_file, "%s ", human_time);
224 fprintf(syslog_file, "%s[%d]: %s%s", prog, pid, msg,
225 nl_or_empty);
226 fflush(syslog_file);
227 }
228 }
229
230
231 int
mvsnprintf(char * buf,size_t bufsiz,const char * fmt,va_list ap)232 mvsnprintf(
233 char * buf,
234 size_t bufsiz,
235 const char * fmt,
236 va_list ap
237 )
238 {
239 #ifndef VSNPRINTF_PERCENT_M
240 char fmtbuf[256];
241 #endif
242 int errval;
243
244 /*
245 * Save the error value as soon as possible
246 */
247 #ifdef SYS_WINNT
248 errval = GetLastError();
249 if (NO_ERROR == errval)
250 #endif /* SYS_WINNT */
251 errval = errno;
252
253 #ifdef VSNPRINTF_PERCENT_M
254 errno = errval;
255 #endif
256 return vsnprintf(buf, bufsiz,
257 format_errmsg(fmtbuf, sizeof(fmtbuf), fmt, errval), ap);
258 }
259
260
261 int
mvfprintf(FILE * fp,const char * fmt,va_list ap)262 mvfprintf(
263 FILE * fp,
264 const char * fmt,
265 va_list ap
266 )
267 {
268 #ifndef VSNPRINTF_PERCENT_M
269 char fmtbuf[256];
270 #endif
271 int errval;
272
273 /*
274 * Save the error value as soon as possible
275 */
276 #ifdef SYS_WINNT
277 errval = GetLastError();
278 if (NO_ERROR == errval)
279 #endif /* SYS_WINNT */
280 errval = errno;
281
282 #ifdef VSNPRINTF_PERCENT_M
283 errno = errval;
284 #endif
285 return vfprintf(fp,
286 format_errmsg(fmtbuf, sizeof(fmtbuf), fmt, errval), ap);
287 }
288
289
290 int
mfprintf(FILE * fp,const char * fmt,...)291 mfprintf(
292 FILE * fp,
293 const char * fmt,
294 ...
295 )
296 {
297 va_list ap;
298 int rc;
299
300 va_start(ap, fmt);
301 rc = mvfprintf(fp, fmt, ap);
302 va_end(ap);
303
304 return rc;
305 }
306
307
308 int
mprintf(const char * fmt,...)309 mprintf(
310 const char * fmt,
311 ...
312 )
313 {
314 va_list ap;
315 int rc;
316
317 va_start(ap, fmt);
318 rc = mvfprintf(stdout, fmt, ap);
319 va_end(ap);
320
321 return rc;
322 }
323
324
325 int
msnprintf(char * buf,size_t bufsiz,const char * fmt,...)326 msnprintf(
327 char * buf,
328 size_t bufsiz,
329 const char * fmt,
330 ...
331 )
332 {
333 va_list ap;
334 int rc;
335
336 va_start(ap, fmt);
337 rc = mvsnprintf(buf, bufsiz, fmt, ap);
338 va_end(ap);
339
340 return rc;
341 }
342
343
344 void
msyslog(int level,const char * fmt,...)345 msyslog(
346 int level,
347 const char * fmt,
348 ...
349 )
350 {
351 char buf[1024];
352 va_list ap;
353
354 va_start(ap, fmt);
355 mvsnprintf(buf, sizeof(buf), fmt, ap);
356 va_end(ap);
357 addto_syslog(level, buf);
358 }
359
360 void
mvsyslog(int level,const char * fmt,va_list ap)361 mvsyslog(
362 int level,
363 const char * fmt,
364 va_list ap
365 )
366 {
367 char buf[1024];
368 mvsnprintf(buf, sizeof(buf), fmt, ap);
369 addto_syslog(level, buf);
370 }
371
372
373 /*
374 * Initialize the logging
375 *
376 * Called once per process, including forked children.
377 */
378 void
init_logging(const char * name,u_int32 def_syslogmask,int is_daemon)379 init_logging(
380 const char * name,
381 u_int32 def_syslogmask,
382 int is_daemon
383 )
384 {
385 static int was_daemon;
386 char * cp;
387 const char * pname;
388
389 /*
390 * ntpd defaults to only logging sync-category events, when
391 * NLOG() is used to conditionalize. Other libntp clients
392 * leave it alone so that all NLOG() conditionals will fire.
393 * This presumes all bits lit in ntp_syslogmask can't be
394 * configured via logconfig and all lit is thereby a sentinel
395 * that ntp_syslogmask is still at its default from libntp,
396 * keeping in mind this function is called in forked children
397 * where it has already been called in the parent earlier.
398 * Forked children pass 0 for def_syslogmask.
399 */
400 if (INIT_NTP_SYSLOGMASK == ntp_syslogmask &&
401 0 != def_syslogmask)
402 ntp_syslogmask = def_syslogmask; /* set more via logconfig */
403
404 /*
405 * Logging. This may actually work on the gizmo board. Find a name
406 * to log with by using the basename
407 */
408 cp = strrchr(name, DIR_SEP);
409 if (NULL == cp)
410 pname = name;
411 else
412 pname = 1 + cp; /* skip DIR_SEP */
413 progname = estrdup(pname);
414 #ifdef SYS_WINNT /* strip ".exe" */
415 cp = strrchr(progname, '.');
416 if (NULL != cp && !strcasecmp(cp, ".exe"))
417 *cp = '\0';
418 #endif
419
420 #if !defined(VMS)
421
422 if (is_daemon)
423 was_daemon = TRUE;
424 # ifndef LOG_DAEMON
425 openlog(progname, LOG_PID);
426 # else /* LOG_DAEMON */
427
428 # ifndef LOG_NTP
429 # define LOG_NTP LOG_DAEMON
430 # endif
431 openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon)
432 ? LOG_NTP
433 : 0);
434 # ifdef DEBUG
435 if (debug)
436 setlogmask(LOG_UPTO(LOG_DEBUG));
437 else
438 # endif /* DEBUG */
439 setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
440 # endif /* LOG_DAEMON */
441 #endif /* !VMS */
442 }
443
444
445 /*
446 * change_logfile()
447 *
448 * Used to change from syslog to a logfile, or from one logfile to
449 * another, and to reopen logfiles after forking. On systems where
450 * ntpd forks, deals with converting relative logfile paths to
451 * absolute (root-based) because we reopen logfiles after the current
452 * directory has changed.
453 */
454 int
change_logfile(const char * fname,int leave_crumbs)455 change_logfile(
456 const char * fname,
457 int leave_crumbs
458 )
459 {
460 FILE * new_file;
461 const char * log_fname;
462 char * abs_fname;
463 #if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS)
464 char curdir[512];
465 size_t cd_octets;
466 size_t octets;
467 #endif /* POSIX */
468
469 REQUIRE(fname != NULL);
470 log_fname = fname;
471
472 /*
473 * In a forked child of a parent which is logging to a file
474 * instead of syslog, syslog_file will be NULL and both
475 * syslog_fname and syslog_abs_fname will be non-NULL.
476 * If we are given the same filename previously opened
477 * and it's still open, there's nothing to do here.
478 */
479 if (syslog_file != NULL && syslog_fname != NULL &&
480 0 == strcmp(syslog_fname, log_fname))
481 return 0;
482
483 if (0 == strcmp(log_fname, "stderr")) {
484 new_file = stderr;
485 abs_fname = estrdup(log_fname);
486 } else if (0 == strcmp(log_fname, "stdout")) {
487 new_file = stdout;
488 abs_fname = estrdup(log_fname);
489 } else {
490 if (syslog_fname != NULL &&
491 0 == strcmp(log_fname, syslog_fname))
492 log_fname = syslog_abs_fname;
493 #if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS)
494 if (log_fname != syslog_abs_fname &&
495 DIR_SEP != log_fname[0] &&
496 0 != strcmp(log_fname, "stderr") &&
497 0 != strcmp(log_fname, "stdout") &&
498 NULL != getcwd(curdir, sizeof(curdir))) {
499 cd_octets = strlen(curdir);
500 /* trim any trailing '/' */
501 if (cd_octets > 1 &&
502 DIR_SEP == curdir[cd_octets - 1])
503 cd_octets--;
504 octets = cd_octets;
505 octets += 1; /* separator '/' */
506 octets += strlen(log_fname);
507 octets += 1; /* NUL terminator */
508 abs_fname = emalloc(octets);
509 snprintf(abs_fname, octets, "%.*s%c%s",
510 (int)cd_octets, curdir, DIR_SEP,
511 log_fname);
512 } else
513 #endif
514 abs_fname = estrdup(log_fname);
515 TRACE(1, ("attempting to open log %s\n", abs_fname));
516 new_file = fopen(abs_fname, "a");
517 }
518
519 if (NULL == new_file) {
520 free(abs_fname);
521 return -1;
522 }
523
524 /* leave a pointer in the old log */
525 if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname))
526 msyslog(LOG_NOTICE, "switching logging to file %s",
527 abs_fname);
528
529 if (syslog_file != NULL &&
530 syslog_file != stderr && syslog_file != stdout &&
531 fileno(syslog_file) != fileno(new_file))
532 fclose(syslog_file);
533 syslog_file = new_file;
534 if (log_fname == syslog_abs_fname) {
535 free(abs_fname);
536 } else {
537 if (syslog_abs_fname != NULL &&
538 syslog_abs_fname != syslog_fname)
539 free(syslog_abs_fname);
540 if (syslog_fname != NULL)
541 free(syslog_fname);
542 syslog_fname = estrdup(log_fname);
543 syslog_abs_fname = abs_fname;
544 }
545 syslogit = FALSE;
546
547 return 0;
548 }
549
550
551 /*
552 * setup_logfile()
553 *
554 * Redirect logging to a file if requested with -l/--logfile or via
555 * ntp.conf logfile directive.
556 *
557 * This routine is invoked three different times in the sequence of a
558 * typical daemon ntpd with DNS lookups to do. First it is invoked in
559 * the original ntpd process, then again in the daemon after closing
560 * all descriptors. In both of those cases, ntp.conf has not been
561 * processed, so only -l/--logfile will trigger logfile redirection in
562 * those invocations. Finally, if DNS names are resolved, the worker
563 * child invokes this routine after its fork and close of all
564 * descriptors. In this case, ntp.conf has been processed and any
565 * "logfile" directive needs to be honored in the child as well.
566 */
567 void
setup_logfile(const char * name)568 setup_logfile(
569 const char * name
570 )
571 {
572 if (NULL == syslog_fname && NULL != name) {
573 if (-1 == change_logfile(name, TRUE))
574 msyslog(LOG_ERR, "Cannot open log file %s, %m",
575 name);
576 return ;
577 }
578 if (NULL == syslog_fname)
579 return;
580
581 if (-1 == change_logfile(syslog_fname, FALSE))
582 msyslog(LOG_ERR, "Cannot reopen log file %s, %m",
583 syslog_fname);
584 }
585
586 /* Helper for unit tests, where stdout + stderr are piped to the same
587 * stream. This works moderately reliable only if both streams are
588 * unbuffered or line buffered. Unfortunately stdout can be fully
589 * buffered on pipes or files...
590 */
591 int
change_iobufs(int how)592 change_iobufs(
593 int how
594 )
595 {
596 int retv = 0;
597
598 # ifdef HAVE_SETVBUF
599
600 int mode;
601
602 switch (how) {
603 case 0 : mode = _IONBF; break; /* no buffering */
604 case 1 : mode = _IOLBF; break; /* line buffering */
605 case 2 : mode = _IOFBF; break; /* full buffering */
606 default: mode = _IOLBF; break; /* line buffering */
607 }
608
609 retv = 1;
610 if (setvbuf(stdout, NULL, mode, BUFSIZ) != 0)
611 retv = -1;
612 if (setvbuf(stderr, NULL, mode, BUFSIZ) != 0)
613 retv = -1;
614
615 # else
616
617 UNUSED_ARG(how);
618
619 # endif
620
621 return retv;
622 }
623