Coverage Report

Created: 2023-06-07 06:29

/src/ntpsec/libntp/msyslog.c
Line
Count
Source (jump to first uncovered line)
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
0
#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
2.64k
#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
48
humanlogtime(char buf[TIMESTAMP_LEN])
49
2.64k
{
50
2.64k
  time_t    cursec;
51
2.64k
  struct tm tmbuf, *tm;
52
53
2.64k
  cursec = time(NULL);
54
2.64k
  tm = localtime_r(&cursec, &tmbuf);
55
2.64k
  if (!tm) {
56
0
    strlcpy(buf, "-- --- --:--:--", TIMESTAMP_LEN);
57
0
    return;
58
0
  }
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
2.64k
  snprintf(buf, TIMESTAMP_LEN, "%04d-%02d-%02dT%02d:%02d:%02d",
72
2.64k
     tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
73
2.64k
     tm->tm_hour, tm->tm_min, tm->tm_sec);
74
2.64k
#endif /* ENABLE_CLASSIC_MODE */
75
2.64k
}
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
84
addto_syslog(
85
  int   level,
86
  const char *  msg
87
  )
88
2.64k
{
89
2.64k
  static char * prevcall_progname;
90
2.64k
  static char * prog;
91
2.64k
  const char  nl[] = "\n";
92
2.64k
  const char  empty[] = "";
93
2.64k
  FILE *    term_file;
94
2.64k
  bool    log_to_term;
95
2.64k
  bool    log_to_file;
96
2.64k
  int   pid;
97
2.64k
  const char *  nl_or_empty;
98
2.64k
  const char *  human_time;
99
2.64k
  char            tbuf[TIMESTAMP_LEN];
100
101
  /* setup program basename static var prog if needed */
102
2.64k
  if (progname != prevcall_progname) {
103
2
    prevcall_progname = progname;
104
2
    prog = strrchr(progname, DIR_SEP);
105
2
    if (prog != NULL) {
106
0
      prog++;
107
2
    } else {
108
2
      prog = progname;
109
2
    }
110
2
  }
111
112
2.64k
  log_to_term = termlogit;
113
2.64k
  log_to_file = false;
114
2.64k
  if (syslogit)
115
2.64k
    syslog(level, "%s", msg);
116
0
  else
117
0
    if (syslog_file != NULL)
118
0
      log_to_file = true;
119
2.64k
#if defined(DEBUG) && DEBUG
120
2.64k
  if (debug > 0) /* SPECIAL DEBUG */
121
0
    log_to_term = true;
122
2.64k
#endif
123
2.64k
  if (!(log_to_file || log_to_term))
124
0
    return;
125
126
  /* syslog() adds the timestamp, name, and pid */
127
2.64k
  if (msyslog_include_timestamp) {
128
2.64k
    humanlogtime(tbuf);
129
2.64k
    human_time = tbuf;
130
2.64k
  } else /* suppress gcc pot. uninit. warning */
131
0
    human_time = NULL;
132
2.64k
  if (termlogit_pid || log_to_file)
133
2.64k
    pid = getpid();
134
0
  else  /* suppress gcc pot. uninit. warning */
135
0
    pid = -1;
136
137
  /* syslog() adds trailing \n if not present */
138
2.64k
  if ('\n' != msg[strlen(msg) - 1]) {
139
2.64k
    nl_or_empty = nl;
140
2.64k
  } else {
141
0
    nl_or_empty = empty;
142
0
}
143
144
2.64k
  if (log_to_term) {
145
2.64k
    term_file = (level <= LOG_ERR)
146
2.64k
        ? stderr
147
2.64k
        : stdout;
148
2.64k
    if (msyslog_include_timestamp)
149
2.64k
      fprintf(term_file, "%s ", human_time);
150
2.64k
    if (termlogit_pid)
151
2.64k
      fprintf(term_file, "%s[%d]: ", prog, pid);
152
2.64k
    fprintf(term_file, "%s%s", msg, nl_or_empty);
153
2.64k
    fflush(term_file);
154
2.64k
  }
155
156
2.64k
  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
0
    char buf[PIPE_BUF];
168
0
    buf[0] = '\0';
169
0
    if (msyslog_include_timestamp)
170
0
      snprintf(buf, sizeof(buf), "%s ", human_time);
171
0
    snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
172
0
       "%s[%d]: %s%s", prog, pid, msg, nl_or_empty);
173
0
    IGNORE(write(fileno(syslog_file), buf, strlen(buf)));
174
0
  }
175
2.64k
}
176
177
178
void
179
msyslog(
180
  int   level,
181
  const char *  fmt,
182
  ...
183
  )
184
2.64k
{
185
2.64k
  char  buf[1024];
186
2.64k
  va_list ap;
187
188
2.64k
  va_start(ap, fmt);
189
2.64k
  vsnprintf(buf, sizeof(buf), fmt, ap);
190
2.64k
  va_end(ap);
191
2.64k
  addto_syslog(level, buf);
192
2.64k
}
193
194
195
/*
196
 * Initialize the logging
197
 *
198
 * Called once per process, including forked children.
199
 */
200
void
201
init_logging(
202
  const char *  name,
203
  uint32_t    def_syslogmask,
204
  int   is_daemon
205
  )
206
0
{
207
0
  static bool was_daemon;
208
0
  const char *  cp;
209
0
  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
0
  if (INIT_NTP_SYSLOGMASK == ntp_syslogmask &&
223
0
      0 != def_syslogmask)
224
0
    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
0
  cp = strrchr(name, DIR_SEP);
231
0
  if (NULL == cp) {
232
0
    pname = name;
233
0
  } else {
234
0
    pname = 1 + cp; /* skip DIR_SEP */
235
0
  }
236
0
  progname = estrdup(pname);
237
238
0
  if (is_daemon)
239
0
    was_daemon = true;
240
# ifndef LOG_DAEMON
241
  openlog(progname, LOG_PID);
242
# else /* LOG_DAEMON */
243
244
0
#  ifndef LOG_NTP
245
0
# define  LOG_NTP LOG_DAEMON
246
0
#  endif
247
0
  openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon)
248
0
                ? LOG_NTP
249
0
                : 0);
250
0
#  ifdef DEBUG
251
0
  if (debug) /* SPECIAL DEBUG */
252
0
    setlogmask(LOG_UPTO(LOG_DEBUG));
253
0
  else
254
0
#  endif /* DEBUG */
255
0
    setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
256
0
# endif /* LOG_DAEMON */
257
0
}
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
270
change_logfile(
271
  const char *  fname,
272
  bool    leave_crumbs
273
  )
274
0
{
275
0
  FILE *    new_file;
276
0
  const char *  log_fname;
277
0
  char *    abs_fname;
278
0
  char    curdir[512];
279
0
  size_t    cd_octets;
280
0
  size_t    octets;
281
282
  //REQUIRE(fname != NULL);
283
0
  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
0
  if (syslog_file != NULL && syslog_fname != NULL &&
293
0
      0 == strcmp(syslog_fname, log_fname)) {
294
0
    return 0;
295
0
  }
296
297
0
  if (0 == strcmp(log_fname, "stderr")) {
298
0
    new_file = stderr;
299
0
    abs_fname = estrdup(log_fname);
300
0
  } else if (0 == strcmp(log_fname, "stdout")) {
301
0
    new_file = stdout;
302
0
    abs_fname = estrdup(log_fname);
303
0
  } else {
304
0
    if (syslog_fname != NULL &&
305
0
        0 == strcmp(log_fname, syslog_fname)) {
306
0
      log_fname = syslog_abs_fname;
307
0
    }
308
0
    if (log_fname != syslog_abs_fname &&
309
0
        DIR_SEP != log_fname[0] &&
310
0
        0 != strcmp(log_fname, "stderr") &&
311
0
        0 != strcmp(log_fname, "stdout") &&
312
0
        NULL != getcwd(curdir, sizeof(curdir))) {
313
0
      cd_octets = strlen(curdir);
314
      /* trim any trailing '/' */
315
0
      if (cd_octets > 1 &&
316
0
          DIR_SEP == curdir[cd_octets - 1])
317
0
        cd_octets--;
318
0
      octets = cd_octets;
319
0
      octets += 1;  /* separator '/' */
320
0
      octets += strlen(log_fname);
321
0
      octets += 1;  /* NUL terminator */
322
0
      abs_fname = emalloc(octets);
323
0
      snprintf(abs_fname, octets, "%.*s%c%s",
324
0
         (int)cd_octets, curdir, DIR_SEP,
325
0
         log_fname);
326
0
    } else
327
0
      abs_fname = estrdup(log_fname);
328
0
    DPRINT(1, ("attempting to open log %s\n", abs_fname));
329
0
    new_file = fopen(abs_fname, "a");
330
0
  }
331
332
0
  if (NULL == new_file) {
333
0
    free(abs_fname);
334
0
    return -1;
335
0
  }
336
337
  /* leave a pointer in the old log */
338
0
  if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname))
339
0
    msyslog(LOG_NOTICE, "LOG: switching logging to file %s",
340
0
      abs_fname);
341
342
0
  if (syslog_file != NULL &&
343
0
      syslog_file != stderr && syslog_file != stdout &&
344
0
      fileno(syslog_file) != fileno(new_file)) {
345
0
    fclose(syslog_file);
346
0
  }
347
0
  syslog_file = new_file;
348
0
  if (log_fname == syslog_abs_fname) {
349
0
    free(abs_fname);
350
0
  } else {
351
0
    if (syslog_abs_fname != NULL &&
352
0
        syslog_abs_fname != syslog_fname) {
353
0
      free(syslog_abs_fname);
354
0
    }
355
0
    if (syslog_fname != NULL) {
356
0
      free(syslog_fname);
357
0
    }
358
0
    syslog_fname = estrdup(log_fname);
359
0
    syslog_abs_fname = abs_fname;
360
0
  }
361
0
  syslogit = false;
362
363
0
  return 0;
364
0
}
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
384
setup_logfile(
385
  const char *  name
386
  )
387
0
{
388
0
  if (NULL == syslog_fname && NULL != name) {
389
0
    if (-1 == change_logfile(name, true))
390
0
      msyslog(LOG_ERR, "LOG: Cannot open log file %s, %s",
391
0
        name, strerror(errno));
392
0
    return ;
393
0
  }
394
0
  if (NULL == syslog_fname) {
395
0
    return;
396
0
  }
397
398
0
  if (-1 == change_logfile(syslog_fname, false))
399
0
    msyslog(LOG_ERR, "LOG: Cannot reopen log file %s, %s",
400
0
      syslog_fname, strerror(errno));
401
0
}
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
411
check_logfile(void)
412
0
{
413
0
  FILE *  new_file;
414
415
0
  if (NULL == syslog_file) {
416
0
    return;  /* no log file, no clutter */
417
0
  }
418
419
0
  new_file = fopen(syslog_fname, "a");
420
0
  if (NULL == new_file) {
421
0
    msyslog(LOG_ERR, "LOG: check_logfile: couldn't open %s %s",
422
0
                        syslog_fname, strerror(errno));
423
0
    return;
424
0
  }
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
0
  if (ftell(syslog_file) == ftell(new_file)) {
434
0
    fclose(new_file);
435
0
    return;
436
0
  }
437
438
0
  msyslog(LOG_INFO, "LOG: check_logfile: closing old file");
439
0
  fclose(syslog_file);
440
0
  syslog_file = new_file;
441
0
  msyslog(LOG_INFO, "LOG: check_logfile: using %s", syslog_fname);
442
0
}
443
444
/* Hack because there are 2 APIs to strerror_r()  */
445
0
void ntp_strerror_r(int errnum, char *buf, size_t buflen) {
446
0
#ifdef STRERROR_CHAR
447
0
  char *answer = strerror_r(errnum, buf, buflen);
448
0
  if (answer != buf) {
449
0
    strlcpy(buf, answer, buflen);
450
0
  }
451
#else
452
  int answer = strerror_r(errnum, buf, buflen);
453
  UNUSED_LOCAL(answer);
454
#endif
455
0
}
456