Coverage Report

Created: 2026-08-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/log.c
Line
Count
Source
1
/*
2
 *   This library is free software; you can redistribute it and/or
3
 *   modify it under the terms of the GNU Lesser General Public
4
 *   License as published by the Free Software Foundation; either
5
 *   version 2.1 of the License, or (at your option) any later version.
6
 *
7
 *   This library is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
 *   Lesser General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU Lesser General Public
13
 *   License along with this library; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/** Support functions for logging in FreeRADIUS libraries
18
 *
19
 * @file src/lib/util/log.c
20
 *
21
 * @copyright 2003,2006 The FreeRADIUS server project
22
 */
23
RCSID("$Id: 56de09aa9d127a8a51c7ca3a471cc04a571588b7 $")
24
25
#include <freeradius-devel/util/debug.h>
26
#include <freeradius-devel/util/log.h>
27
#include <freeradius-devel/util/print.h>
28
#include <freeradius-devel/util/syserror.h>
29
#include <freeradius-devel/util/value.h>
30
31
#include <fcntl.h>
32
#include <stdatomic.h>
33
#ifdef HAVE_FEATURES_H
34
#  include <features.h>
35
#endif
36
#ifdef HAVE_SYSLOG_H
37
#  include <syslog.h>
38
#endif
39
40
FILE  *fr_log_fp = NULL;
41
int fr_debug_lvl = 0;
42
43
static _Thread_local TALLOC_CTX *fr_log_pool;
44
45
/** Latched once shutdown has freed every thread's log pool
46
 *
47
 * `fr_atexit_thread_trigger_all()` runs every registered thread destructor
48
 * on the calling (main) thread, so it frees the log pool memory for threads
49
 * whose TLS slot it can't reach (librdkafka's bg threads, anything spawned
50
 * by a third-party library that bypasses our schedule).  Those threads
51
 * still hold the now-dangling pointer in their `_Thread_local fr_log_pool`,
52
 * and will hand it to `talloc_new` on the next log call - "Bad talloc magic
53
 * value" abort.
54
 *
55
 * Once set, `fr_log_pool_init()` ignores the TLS slot entirely and returns
56
 * NULL; downstream `talloc_new(NULL)` / `talloc_asprintf(NULL, ...)` calls
57
 * just allocate top-level chunks for the duration of the log line.  No
58
 * pooling, no TLS, safe from any thread.
59
 */
60
static atomic_bool log_pools_disabled;
61
62
static uint32_t location_indent = 30;
63
static fr_event_list_t *log_el;     //!< Event loop we use for process logging data.
64
65
static int stderr_fd = -1;      //!< The original unmolested stderr file descriptor
66
static int stdout_fd = -1;      //!< The original unmolested stdout file descriptor
67
68
static fr_log_fd_event_ctx_t stdout_ctx;  //!< Logging ctx for stdout.
69
static fr_log_fd_event_ctx_t stderr_ctx;  //!< Logging ctx for stderr.
70
71
static int stdout_pipe[2];      //!< Pipe we use to transport stdout data.
72
static int stderr_pipe[2];      //!< Pipe we use to transport stderr data.
73
74
static FILE *devnull;       //!< File handle for /dev/null
75
76
bool fr_log_rate_limit = true;      //!< Whether repeated log entries should be rate limited
77
78
static _Thread_local fr_log_type_t log_msg_type;//!< The type of the last message logged.
79
            ///< Mainly uses for syslog.
80
81
/** Canonicalize error strings, removing tabs, and generate spaces for error marker
82
 *
83
 * @note talloc_free must be called on the buffer returned in spaces and text
84
 *
85
 * Used to produce error messages such as this:
86
 @verbatim
87
  I'm a string with a parser # error
88
                             ^ Unexpected character in string
89
 @endverbatim
90
 *
91
 * With code resembling this:
92
 @code{.c}
93
   ERROR("%s", parsed_str);
94
   ERROR("%s^ %s", space, text);
95
 @endcode
96
 *
97
 * @todo merge with above function (log_request_marker)
98
 *
99
 * @param sp Where to write a dynamically allocated buffer of spaces used to indent the error text.
100
 * @param text Where to write the canonicalized version of fmt (the error text).
101
 * @param ctx to allocate the spaces and text buffers in.
102
 * @param slen of error marker. Expects negative integer value, as returned by parse functions.
103
 * @param fmt to canonicalize.
104
 */
105
void fr_canonicalize_error(TALLOC_CTX *ctx, char **sp, char **text, ssize_t slen, char const *fmt)
106
71
{
107
71
  size_t offset, prefix, suffix;
108
71
  char *spaces, *p;
109
71
  char const *start;
110
71
  char *value;
111
71
  size_t inlen;
112
113
71
  offset = -slen;
114
115
71
  inlen = strlen(fmt);
116
71
  start = fmt;
117
71
  prefix = suffix = 0;
118
119
  /*
120
   *  Catch bad callers.
121
   */
122
71
  if (offset > inlen) {
123
0
    *sp = talloc_strdup(ctx, "");
124
0
    *text = talloc_strdup(ctx, "");
125
0
    return;
126
0
  }
127
128
  /*
129
   *  Too many characters before the inflection point.  Skip
130
   *  leading text until we have only 45 characters before it.
131
   */
132
71
  if (offset > 30) {
133
35
    size_t skip = offset - 30;
134
135
35
    start += skip;
136
35
    inlen -= skip;
137
35
    offset -= skip;
138
35
    prefix = 4;
139
35
  }
140
141
  /*
142
   *  Too many characters after the inflection point,
143
   *  truncate it to 30 characters after the inflection
144
   *  point.
145
   */
146
71
  if (inlen > (offset + 30)) {
147
41
    inlen = offset + 30;
148
41
    suffix = 4;
149
41
  }
150
151
  /*
152
   *  Allocate an array to hold just the text we need.
153
   */
154
71
  value = talloc_array(ctx, char, prefix + inlen + 1 + suffix);
155
71
  if (prefix) {
156
35
    memcpy(value, "... ", 4);
157
35
  }
158
71
  memcpy(value + prefix, start, inlen);
159
71
  if (suffix) {
160
41
    memcpy(value + prefix + inlen, "...", 3);
161
41
    value[prefix + inlen + 3] = '\0';
162
41
  }
163
71
  value[prefix + inlen + suffix] = '\0';
164
165
  /*
166
   *  Smash tabs to spaces for the input string.
167
   */
168
2.81k
  for (p = value; *p != '\0'; p++) {
169
2.74k
    if (*p == '\t') *p = ' ';
170
2.74k
  }
171
172
  /*
173
   *  Allocate the spaces array
174
   */
175
71
  spaces = talloc_array(ctx, char, prefix + offset + 1);
176
71
  memset(spaces, ' ', prefix + offset);
177
71
  spaces[prefix + offset] = '\0';
178
179
71
  *sp = spaces;
180
71
  *text = value;
181
71
}
182
183
/** Function to provide as the readable callback to the event loop
184
 *
185
 * Writes any data read from a file descriptor to the request log,
186
 * tries very hard not to chop lines in the middle, but will split
187
 * at 1024 byte boundaries if forced to.
188
 *
189
 * @param[in] el  UNUSED
190
 * @param[in] fd  UNUSED
191
 * @param[in] flags UNUSED
192
 * @param[in] uctx  Pointer to a log_fd_event_ctx_t
193
 */
194
void fr_log_fd_event(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
195
0
{
196
0
  char      buffer[1024] = "";
197
0
  fr_log_fd_event_ctx_t *log_info = uctx;
198
0
  fr_sbuff_t    sbuff;
199
0
  fr_sbuff_marker_t m_start, m_end;
200
201
0
  fr_sbuff_term_t const   line_endings = FR_SBUFF_TERMS(L("\n"), L("\r"));
202
203
0
  if (log_info->lvl < fr_debug_lvl) {
204
0
    while (read(fd, buffer, sizeof(buffer)) > 0);
205
0
    return;
206
0
  }
207
208
0
#ifndef NDEBUG
209
0
  memset(buffer, 0x42, sizeof(buffer));
210
0
#endif
211
212
0
  fr_sbuff_init_out(&sbuff, buffer, sizeof(buffer));
213
0
  fr_sbuff_marker(&m_start, &sbuff);
214
0
  fr_sbuff_marker(&m_end, &sbuff);
215
216
0
  for (;;) {
217
0
    ssize_t   slen;
218
219
0
    slen = read(fd, fr_sbuff_current(&m_end), fr_sbuff_remaining(&m_end));
220
0
    if ((slen < 0) && (errno == EINTR)) continue;
221
222
0
    if (slen > 0) fr_sbuff_advance(&m_end, slen);
223
224
0
    while (fr_sbuff_ahead(&m_end) > 0) {
225
0
      fr_sbuff_adv_until(&sbuff, fr_sbuff_ahead(&m_end), &line_endings, '\0');
226
227
      /*
228
       *  Incomplete line, try and read the rest.
229
       */
230
0
      if ((slen > 0) && (fr_sbuff_used(&m_start) > 0) &&
231
0
          !fr_sbuff_is_terminal(&sbuff, &line_endings)) {
232
0
        break;
233
0
      }
234
235
0
      fr_log(log_info->dst, log_info->type,
236
0
             __FILE__, __LINE__,
237
0
             "%s%s%pV",
238
0
             log_info->prefix ? log_info->prefix : "",
239
0
             log_info->prefix ? " - " : "",
240
0
             fr_box_strvalue_len(fr_sbuff_current(&m_start), fr_sbuff_behind(&m_start)));
241
242
0
      fr_sbuff_advance(&sbuff, 1);  /* Skip the whitespace */
243
0
      fr_sbuff_set(&m_start, &sbuff);
244
0
    }
245
246
    /*
247
     *  Error or done
248
     */
249
0
    if (slen <= 0) break;
250
251
    /*
252
     *  Clear out the existing data
253
     */
254
0
    fr_sbuff_shift(&sbuff, fr_sbuff_used(&m_start), false);
255
0
  }
256
0
}
257
258
/** Maps log categories to message prefixes
259
 */
260
fr_table_num_ordered_t const fr_log_levels[] = {
261
  { L("Debug : "),    L_DBG   },
262
  { L("Info  : "),    L_INFO    },
263
  { L("Warn  : "),    L_WARN    },
264
  { L("Error : "),    L_ERR   },
265
  { L("Auth  : "),    L_AUTH    },
266
  { L("INFO  : "),    L_DBG_INFO  },
267
  { L("WARN  : "),    L_DBG_WARN  },
268
  { L("ERROR : "),    L_DBG_ERR },
269
  { L("WARN  : "),    L_DBG_WARN_REQ  },
270
  { L("ERROR : "),    L_DBG_ERR_REQ }
271
};
272
size_t fr_log_levels_len = NUM_ELEMENTS(fr_log_levels);
273
274
/** @name VT100 escape sequences
275
 *
276
 * These sequences may be written to VT100 terminals to change the
277
 * colour and style of the text.
278
 *
279
 @code{.c}
280
   fprintf(stdout, VTC_RED "This text will be coloured red" VTC_RESET);
281
 @endcode
282
 * @{
283
 */
284
#define VTC_RED   "\x1b[31m"  //!< Colour following text red.
285
#define VTC_YELLOW      "\x1b[33m"  //!< Colour following text yellow.
286
#define VTC_BOLD  "\x1b[1m" //!< Embolden following text.
287
0
#define VTC_RESET "\x1b[0m"  //!< Reset terminal text to default style/colour.
288
/** @} */
289
290
/** Maps log categories to VT100 style/colour escape sequences
291
 */
292
static fr_table_num_ordered_t const colours[] = {
293
  { L(VTC_BOLD),      L_INFO    },
294
  { L(VTC_RED),     L_ERR   },
295
  { L(VTC_BOLD VTC_YELLOW), L_WARN    },
296
  { L(VTC_BOLD VTC_RED),    L_DBG_ERR },
297
  { L(VTC_BOLD VTC_YELLOW), L_DBG_WARN  },
298
  { L(VTC_BOLD VTC_RED),    L_DBG_ERR_REQ },
299
  { L(VTC_BOLD VTC_YELLOW), L_DBG_WARN_REQ  },
300
};
301
static size_t colours_len = NUM_ELEMENTS(colours);
302
303
304
bool log_dates_utc = false;
305
306
fr_log_t default_log = {
307
  .colourise = false,   //!< Will be set later. Should be off before we do terminal detection.
308
  .fd = STDOUT_FILENO,
309
  .dst = L_DST_STDOUT,
310
  .file = NULL,
311
  .timestamp = L_TIMESTAMP_AUTO
312
};
313
314
/** Cleanup the memory pool used by vlog_request
315
 *
316
 */
317
static int _fr_log_pool_free(void *arg)
318
1
{
319
1
  if (talloc_free(arg) < 0) return -1;
320
1
  fr_log_pool = NULL;
321
1
  return 0;
322
1
}
323
324
/** Disable per-thread log pools for the rest of the process lifetime
325
 *
326
 * Call this from the main thread immediately after
327
 * `fr_atexit_thread_trigger_all()`, which frees every other thread's log
328
 * pool but can't reset their `_Thread_local` slot.  After this returns,
329
 * subsequent `fr_log` calls fall back to `talloc_new(NULL)` instead of
330
 * touching the (now dangling) TLS pool pointer.
331
 */
332
void fr_log_disable_pools(void)
333
0
{
334
0
  atomic_store_explicit(&log_pools_disabled, true, memory_order_relaxed);
335
0
}
336
337
/** talloc ctx to use when composing log messages
338
 *
339
 * Functions must ensure that they allocate a new ctx from the one returned
340
 * here, and that this ctx is freed before the function returns.
341
 *
342
 * @return talloc pool to use for scratch space, or NULL if pools have been
343
 *  disabled - callers must tolerate a NULL return.
344
 */
345
TALLOC_CTX *fr_log_pool_init(void)
346
32
{
347
32
  TALLOC_CTX  *pool;
348
349
  /*
350
   *  Once main has signalled shutdown the TLS slot may be a
351
   *  dangling pointer for any thread we don't own (librdkafka's
352
   *  bg threads etc.) - skip the pool entirely.
353
   */
354
32
  if (unlikely(fr_atexit_thread_local_alloc_disabled())) return NULL;
355
356
32
  pool = fr_log_pool;
357
32
  if (unlikely(!pool)) {
358
1
    if (fr_atexit_is_exiting()) return NULL; /* No new pools if we're exiting */
359
360
1
    pool = talloc_pool(NULL, 16384);
361
1
    if (!pool) {
362
0
      fr_perror("Failed allocating memory for vlog_request_pool");
363
0
      return NULL;
364
0
    }
365
1
    fr_atexit_thread_local(fr_log_pool, _fr_log_pool_free, pool);
366
1
  }
367
368
32
  return pool;
369
32
}
370
371
/** Send a server log message to its destination
372
 *
373
 * @param[in] log destination.
374
 * @param[in] type  of log message.
375
 * @param[in] file  src file the log message was generated in.
376
 * @param[in] line  number the log message was generated on.
377
 * @param[in] arg_names source text of each substitution argument, or NULL.
378
 * @param[in] fmt with printf style substitution tokens.
379
 * @param[in] ap  Substitution arguments.
380
 */
381
void _fr_vlog(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
382
        UNUSED char const * const arg_names[], char const *fmt, va_list ap)
383
909
{
384
909
  int   colourise = log->colourise;
385
909
  char    *buffer;
386
909
  TALLOC_CTX  *pool, *thread_log_pool;
387
909
  char const  *fmt_colour = "";
388
909
  char const  *fmt_location = "";
389
909
  char    fmt_time[50];
390
909
  char const  *fmt_type = "";
391
909
  char    *fmt_msg;
392
393
909
  static char const *spaces = "                                    "; /* 40 */
394
395
909
  fmt_time[0] = '\0';
396
397
  /*
398
   *  If we don't want any messages, then
399
   *  throw them away.
400
   */
401
909
  if (log->dst == L_DST_NULL) return;
402
403
0
  thread_log_pool = fr_log_pool_init();
404
0
  pool = talloc_new(thread_log_pool);  /* Track our local allocations */
405
406
  /*
407
   *  Set colourisation
408
   */
409
0
  if (colourise) {
410
0
    fmt_colour = fr_table_str_by_value(colours, type, NULL);
411
0
    if (!fmt_colour) colourise = false;
412
0
  }
413
414
  /*
415
   *  Print src file/line
416
   */
417
0
  if (log->line_number) {
418
0
    size_t  len;
419
0
    int pad = 0;
420
0
    char  *str;
421
422
0
    str = talloc_asprintf(pool, "%s:%i", file, line);
423
0
    len = talloc_strlen(str);
424
425
    /*
426
     *  Only increase the indent
427
     */
428
0
    if (len > location_indent) {
429
0
      location_indent = len;
430
0
    } else {
431
0
      pad = location_indent - len;
432
0
    }
433
434
0
    fmt_location = talloc_asprintf_append_buffer(str, "%.*s : ", pad, spaces);
435
0
  }
436
  /*
437
   *  Determine if we need to add a timestamp to the start of the message
438
   */
439
0
  switch (log->timestamp) {
440
0
  case L_TIMESTAMP_OFF:
441
0
    break;
442
443
  /*
444
   *  If we're not logging to syslog, and the debug level is -xxx
445
   *  then log timestamps by default.
446
   */
447
0
  case L_TIMESTAMP_AUTO:
448
0
    if (log->dst == L_DST_SYSLOG) break;
449
0
    if ((log->dst != L_DST_FILES) && (fr_debug_lvl <= L_DBG_LVL_2)) break;
450
0
    FALL_THROUGH;
451
452
0
  case L_TIMESTAMP_ON:
453
0
  {
454
0
    fr_unix_time_t now = fr_time_to_unix_time(fr_time());
455
0
    fr_sbuff_t time_sbuff = FR_SBUFF_OUT(fmt_time, sizeof(fmt_time));
456
0
    fr_unix_time_to_str(&time_sbuff, now, FR_TIME_RES_USEC, log->dates_utc);
457
0
    break;
458
0
  }
459
0
  }
460
461
  /*
462
   *  Add ERROR or WARNING prefixes to messages not going to
463
   *  syslog.  It's redundant for syslog because of syslog
464
   *  facilities.
465
   */
466
0
  if (log->dst != L_DST_SYSLOG) {
467
    /*
468
     *  We always print "WARN" and "ERROR" prefixes.
469
     */
470
0
    switch (type) {
471
0
    case L_DBG_WARN:
472
0
    case L_DBG_ERR:
473
0
      fmt_type = fr_table_str_by_value(fr_log_levels, type, NULL);
474
0
      break;
475
476
0
    default:
477
      /*
478
       *  Otherwise, print the other info levels only if we're asked to print the level,
479
       *  and we're not colourizing the output.  If we're colourizing the output, then
480
       *  the colors indicate the debug level (info, warning, error), and we don't need
481
       *  any prefix.
482
       */
483
0
      if (log->print_level && !log->colourise) fmt_type = fr_table_str_by_value(fr_log_levels, type, ": ");
484
0
      break;
485
0
    }
486
0
  }
487
488
  /*
489
   *  Sanitize output.
490
   *
491
   *  Most strings should be escaped before they get here.
492
   */
493
0
  {
494
0
    char  *p, *end;
495
496
0
    p = fmt_msg = fr_vasprintf(pool, fmt, ap);
497
0
    end = p + talloc_strlen(fmt_msg);
498
499
    /*
500
     *  Filter out control chars and non UTF8 chars
501
     */
502
0
    for (p = fmt_msg; p < end; p++) {
503
0
      int clen;
504
505
0
      switch (*p) {
506
0
      case '\r':
507
0
      case '\n':
508
0
        *p = ' ';
509
0
        break;
510
511
0
      case '\t':
512
0
        continue;
513
514
0
      default:
515
0
        clen = fr_utf8_char((uint8_t *)p, -1);
516
0
        if (!clen) {
517
0
          *p = '?';
518
0
          continue;
519
0
        }
520
0
        p += (clen - 1);
521
0
        break;
522
0
      }
523
0
    }
524
0
  }
525
526
0
  switch (log->dst) {
527
528
0
#ifdef HAVE_SYSLOG_H
529
0
  case L_DST_SYSLOG:
530
0
  {
531
0
    int syslog_priority = L_DBG;
532
533
0
    switch (type) {
534
0
    case L_DBG:
535
0
    case L_DBG_INFO:
536
0
    case L_DBG_WARN:
537
0
    case L_DBG_ERR:
538
0
    case L_DBG_ERR_REQ:
539
0
    case L_DBG_WARN_REQ:
540
0
      syslog_priority= LOG_DEBUG;
541
0
      break;
542
543
0
    case L_INFO:
544
0
      syslog_priority = LOG_INFO;
545
0
      break;
546
547
0
    case L_WARN:
548
0
      syslog_priority = LOG_WARNING;
549
0
      break;
550
551
0
    case L_ERR:
552
0
      syslog_priority = LOG_ERR;
553
0
      break;
554
555
0
    case L_AUTH:
556
0
      syslog_priority = LOG_AUTH | LOG_INFO;
557
0
      break;
558
0
    }
559
0
    syslog(syslog_priority,
560
0
           "%s" /* time */
561
0
           "%s" /* time sep */
562
0
           "%s",  /* message */
563
0
           fmt_time,
564
0
           fmt_time[0] ? ": " : "",
565
0
           fmt_msg);
566
0
  }
567
0
    break;
568
0
#endif
569
570
0
  case L_DST_FILES:
571
0
  case L_DST_STDOUT:
572
0
  case L_DST_STDERR:
573
0
  {
574
0
    size_t len, wrote;
575
576
0
    buffer = talloc_asprintf(pool,
577
0
           "%s" /* colourise */
578
0
           "%s" /* location */
579
0
           "%s" /* time */
580
0
           "%s" /* time sep */
581
0
           "%s" /* message type */
582
0
           "%s" /* message */
583
0
           "%s" /* colourise reset */
584
0
           "\n",
585
0
           colourise ? fmt_colour : "",
586
0
           fmt_location,
587
0
           fmt_time,
588
0
           fmt_time[0] ? ": " : "",
589
0
           fmt_type,
590
0
           fmt_msg,
591
0
           colourise ? VTC_RESET : "");
592
593
0
    len = talloc_strlen(buffer);
594
0
    wrote = write(log->fd, buffer, len);
595
0
    if (wrote < len) return;
596
0
  }
597
0
    break;
598
599
0
  default:
600
0
  case L_DST_NULL:  /* should have been caught above */
601
0
    break;
602
0
  }
603
604
0
  talloc_free(pool); /* clears all temporary allocations */
605
606
0
  return;
607
0
}
608
609
/** Send a server log message to its destination
610
 *
611
 * @param log   destination.
612
 * @param type    of log message.
613
 * @param file    where the log message originated
614
 * @param line    where the log message originated
615
 * @param arg_names source text of each substitution argument, or NULL.
616
 * @param fmt   with printf style substitution tokens.
617
 * @param ...   Substitution arguments.
618
 */
619
void _fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
620
       char const * const arg_names[], char const *fmt, ...)
621
909
{
622
909
  va_list ap;
623
624
  /*
625
   *  Non-debug message, or debugging is enabled.  Log it.
626
   */
627
909
  if (!(((type & L_DBG) == 0) || (fr_debug_lvl > 0))) return;
628
629
909
  va_start(ap, fmt);
630
909
  _fr_vlog(log, type, file, line, arg_names, fmt, ap);
631
909
  va_end(ap);
632
909
}
633
634
/** Drain any outstanding messages from the fr_strerror buffers
635
 *
636
 * This function drains any messages from fr_strerror buffer prefixing
637
 * the first message with fmt + args.
638
 *
639
 * If a prefix is specified in rules, this is prepended to all lines
640
 * logged.  The prefix is useful for adding context, i.e. configuration
641
 * file and line number information.
642
 *
643
 * @param[in] log destination.
644
 * @param[in] type  of log message.
645
 * @param[in] file  src file the log message was generated in.
646
 * @param[in] line  number the log message was generated on.
647
 * @param[in] f_rules for printing multiline errors.
648
 * @param[in] arg_names source text of each substitution argument, or NULL.
649
 * @param[in] fmt with printf style substitution tokens.
650
 * @param[in] ap  Substitution arguments.
651
 */
652
void _fr_vlog_perror(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
653
         fr_log_perror_format_t const *f_rules,
654
         UNUSED char const * const arg_names[], char const *fmt, va_list ap)
655
30
{
656
30
  char const        *error;
657
30
  static fr_log_perror_format_t   default_f_rules;
658
659
30
  TALLOC_CTX              *thread_log_pool;
660
30
  fr_sbuff_marker_t     prefix_m;
661
662
30
  fr_sbuff_t        sbuff;
663
30
  fr_sbuff_uctx_talloc_t      tctx;
664
665
  /*
666
   *  Non-debug message, or debugging is enabled.  Log it.
667
   */
668
30
  if (!(((type & L_DBG) == 0) || (fr_debug_lvl > 0))) return;
669
670
30
  if (!f_rules) f_rules = &default_f_rules;
671
672
30
  thread_log_pool = fr_log_pool_init();
673
674
  /*
675
   *  Setup the aggregation buffer
676
   */
677
30
  fr_sbuff_init_talloc(thread_log_pool, &sbuff, &tctx, 1024, 16384);
678
679
  /*
680
   *  Add the prefix for the first line
681
   */
682
30
  if (f_rules->first_prefix) (void) fr_sbuff_in_strcpy(&sbuff, f_rules->first_prefix);
683
684
  /*
685
   *  Add the (optional) message, and/or (optional) error
686
   *  with the error_sep.
687
   *  i.e. <msg>: <error>
688
   */
689
30
  error = fr_strerror_pop();
690
691
30
  if (!error && !fmt) return; /* NOOP */
692
693
30
  if (fmt) {
694
30
    va_list aq;
695
696
30
    va_copy(aq, ap);
697
30
    fr_sbuff_in_vsprintf(&sbuff, fmt, aq);
698
30
    va_end(aq);
699
30
  }
700
701
30
  if (error && (fmt || f_rules->first_prefix)) {
702
30
    if (fmt) (void) fr_sbuff_in_strcpy(&sbuff, ": ");
703
30
    (void) fr_sbuff_in_strcpy(&sbuff, error);
704
30
  }
705
706
30
  error = fr_sbuff_start(&sbuff);   /* may not be talloced with const */
707
708
  /*
709
   *  Log the first line
710
   */
711
30
  fr_log(log, type, file, line, "%s", error);
712
713
30
  fr_sbuff_set_to_start(&sbuff);
714
30
  if (f_rules->subsq_prefix) {
715
2
    (void) fr_sbuff_in_strcpy(&sbuff, f_rules->subsq_prefix);
716
2
    fr_sbuff_marker(&prefix_m, &sbuff);
717
2
  }
718
719
  /*
720
   *  Print out additional error lines
721
   */
722
30
  while ((error = fr_strerror_pop())) {
723
0
    if (f_rules->subsq_prefix) {
724
0
      fr_sbuff_set(&sbuff, &prefix_m);
725
0
      (void) fr_sbuff_in_strcpy(&sbuff, error); /* may not be talloced with const */
726
0
      error = fr_sbuff_start(&sbuff);
727
0
    }
728
729
0
    fr_log(log, type, file, line, "%s", error);
730
0
  }
731
732
30
  talloc_free(sbuff.buff);
733
30
}
734
735
/** Drain any outstanding messages from the fr_strerror buffers
736
 *
737
 * This function drains any messages from fr_strerror buffer adding a prefix (fmt)
738
 * to the first message.
739
 *
740
 * @param[in] log destination.
741
 * @param[in] type  of log message.
742
 * @param[in] file  src file the log message was generated in.
743
 * @param[in] line  number the log message was generated on.
744
 * @param[in] rules for printing multiline errors.
745
 * @param[in] arg_names source text of each substitution argument, or NULL.
746
 * @param[in] fmt with printf style substitution tokens.
747
 * @param[in] ... Substitution arguments.
748
 */
749
void _fr_log_perror(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
750
        fr_log_perror_format_t const *rules,
751
        char const * const arg_names[], char const *fmt, ...)
752
28
{
753
28
  va_list ap;
754
755
28
  va_start(ap, fmt);
756
28
  _fr_vlog_perror(log, type, file, line, rules, arg_names, fmt, ap);
757
28
  va_end(ap);
758
28
}
759
760
DIAG_OFF(format-nonliteral)
761
/** Print out an error marker
762
 *
763
 * @param[in] log   destination.
764
 * @param[in] type    of log message.
765
 * @param[in] file    src file the log message was generated in.
766
 * @param[in] line    number the log message was generated on.
767
 * @param[in] str   Subject string we're printing a marker for.
768
 * @param[in] str_len   Subject string length.  Use SIZE_MAX for the
769
 *        length of the string.
770
 * @param[in] marker_idx  Where to place the marker.  May be negative.
771
 * @param[in] marker    text to print at marker_idx.
772
 * @param[in] line_prefix_fmt Prefix to add to the marker messages.
773
 * @param[in] ...   Arguments for line_prefix_fmt.
774
 */
775
void fr_log_marker(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
776
       char const *str, size_t str_len,
777
       ssize_t marker_idx, char const *marker, char const *line_prefix_fmt, ...)
778
0
{
779
0
  char const    *ellipses = "";
780
0
  va_list     ap;
781
0
  TALLOC_CTX    *thread_log_pool = fr_log_pool_init();
782
0
  char      *line_prefix = NULL;
783
0
  static char const marker_spaces[] = "                                                            "; /* 60 */
784
785
0
  if (str_len == SIZE_MAX) str_len = strlen(str);
786
787
0
  if (marker_idx < 0) marker_idx = marker_idx * -1;
788
789
0
  if ((size_t)marker_idx >= sizeof(marker_spaces)) {
790
0
    size_t offset = (marker_idx - (sizeof(marker_spaces) - 1)) + (sizeof(marker_spaces) * 0.75);
791
0
    marker_idx -= offset;
792
793
0
    if (offset > str_len) offset = str_len;
794
0
    str += offset;
795
0
    str_len -= offset;
796
797
0
    ellipses = "... ";
798
0
  }
799
800
0
  if (line_prefix_fmt) {
801
0
    va_start(ap, line_prefix_fmt);
802
0
    line_prefix = fr_vasprintf(thread_log_pool, line_prefix_fmt, ap);
803
0
    va_end(ap);
804
0
  }
805
806
0
  fr_log(log, type, file, line, "%s%s%.*s",
807
0
         line_prefix ? line_prefix : "", ellipses, (int)str_len, str);
808
0
  fr_log(log, type, file, line, "%s%s%.*s^ %s",
809
0
         line_prefix ? line_prefix : "", ellipses, (int)marker_idx, marker_spaces, marker);
810
811
0
  if (line_prefix_fmt) talloc_free(line_prefix);
812
0
}
813
814
/** Print out hex block
815
 *
816
 * @param[in] log   destination.
817
 * @param[in] type    of log message.
818
 * @param[in] file    src file the log message was generated in.
819
 * @param[in] line    number the log message was generated on.
820
 * @param[in] data    to print.
821
 * @param[in] data_len    length of data.
822
 * @param[in] line_prefix_fmt Prefix to add to the marker messages.
823
 * @param[in] ...   Arguments for line_prefix_fmt.
824
 */
825
void fr_log_hex(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
826
    uint8_t const *data, size_t data_len, char const *line_prefix_fmt, ...)
827
0
{
828
0
  size_t    i, j, len;
829
0
  char    buffer[(0x10 * 3) + 1];
830
0
  char    *p, *end = buffer + sizeof(buffer);
831
0
  TALLOC_CTX  *thread_log_pool = fr_log_pool_init();
832
0
  char    *line_prefix = NULL;
833
834
0
  if (line_prefix_fmt) {
835
0
    va_list ap;
836
837
0
    va_start(ap, line_prefix_fmt);
838
0
    line_prefix = fr_vasprintf(thread_log_pool, line_prefix_fmt, ap);
839
0
    va_end(ap);
840
0
  }
841
842
0
  for (i = 0; i < data_len; i += 0x10) {
843
0
    len = 0x10;
844
0
    if ((i + len) > data_len) len = data_len - i;
845
846
0
    for (p = buffer, j = 0; j < len; j++, p += 3) snprintf(p, end - p, "%02x ", data[i + j]);
847
848
0
    if (line_prefix_fmt) {
849
0
      fr_log(log, type, file, line, "%s%04x: %s",
850
0
             line_prefix, (unsigned int) i, buffer);
851
0
    } else {
852
0
      fr_log(log, type, file, line, "%04x: %s", (unsigned int) i, buffer);
853
0
    }
854
0
  }
855
856
0
  if (line_prefix_fmt) talloc_free(line_prefix);
857
0
}
858
859
/** Print out hex block
860
 *
861
 * @param[in] log   destination.
862
 * @param[in] type    of log message.
863
 * @param[in] file    src file the log message was generated in.
864
 * @param[in] line    number the log message was generated on.
865
 * @param[in] data    to print.
866
 * @param[in] data_len    length of data.
867
 * @param[in] marker_idx  Where to place the marker.  May be negative.
868
 * @param[in] marker    text to print at marker_idx.
869
 * @param[in] line_prefix_fmt Prefix to add to the marker messages.
870
 * @param[in] ...   Arguments for line_prefix_fmt.
871
 */
872
void fr_log_hex_marker(fr_log_t const *log, fr_log_type_t type, char const *file, int line,
873
           uint8_t const *data, size_t data_len,
874
           ssize_t marker_idx, char const *marker, char const *line_prefix_fmt, ...)
875
0
{
876
0
  size_t    i, j, len;
877
0
  char    buffer[(0x10 * 3) + 1];
878
0
  char    *p, *end = buffer + sizeof(buffer);
879
0
  TALLOC_CTX  *thread_log_pool = fr_log_pool_init();
880
881
0
  char    *line_prefix = NULL;
882
0
  static char spaces[3 * 0x10]; /* Bytes per line */
883
884
0
  if (!*spaces) memset(spaces, ' ', sizeof(spaces) - 1); /* Leave a \0 */
885
886
0
  if (marker_idx < 0) marker_idx = marker_idx * -1;
887
0
  if (line_prefix_fmt) {
888
0
    va_list ap;
889
890
0
    va_start(ap, line_prefix_fmt);
891
0
    line_prefix = fr_vasprintf(thread_log_pool, line_prefix_fmt, ap);
892
0
    va_end(ap);
893
0
  }
894
895
0
  for (i = 0; i < data_len; i += 0x10) {
896
0
    len = 0x10;
897
0
    if ((i + len) > data_len) len = data_len - i;
898
899
0
    for (p = buffer, j = 0; j < len; j++, p += 3) snprintf(p, end - p, "%02x ", data[i + j]);
900
901
0
    if (line_prefix_fmt) {
902
0
      fr_log(log, type, file, line, "%s%04x: %s",
903
0
             line_prefix, (unsigned int) i, buffer);
904
0
    } else {
905
0
      fr_log(log, type, file, line, "%04x: %s", (unsigned int) i, buffer);
906
0
    }
907
908
    /*
909
     *  Marker is on this line
910
     */
911
0
    if (((size_t)marker_idx >= i) && ((size_t)marker_idx < (i + 0x10))) {
912
0
      if (line_prefix_fmt) {
913
0
        fr_log(log, type, file, line, "%s      %.*s^ %s", line_prefix,
914
0
               (int)((marker_idx - i) * 3), spaces, marker);
915
0
      } else {
916
0
        fr_log(log, type, file, line, "      %.*s^ %s",
917
0
               (int)((marker_idx - i) * 3), spaces, marker);
918
0
      }
919
0
    }
920
0
  }
921
922
0
  if (line_prefix_fmt) talloc_free(line_prefix);
923
0
}
924
DIAG_ON(format-nonliteral)
925
/** On fault, reset STDOUT and STDERR to something useful
926
 *
927
 * @return 0
928
 */
929
static int _restore_std_legacy(UNUSED int sig)
930
0
{
931
0
  if ((stderr_fd > 0) && (stdout_fd > 0)) {
932
0
    dup2(stdout_fd, STDOUT_FILENO);
933
0
    dup2(stderr_fd, STDERR_FILENO);
934
0
    return 0;
935
0
  }
936
937
0
  return 0;
938
0
}
939
940
/** Initialise file descriptors based on logging destination
941
 *
942
 * @param log Logger to manipulate.
943
 * @param daemonize Whether the server is starting as a daemon.
944
 * @return
945
 *  - 0 on success.
946
 *  - -1 on failure.
947
 */
948
int fr_log_init_legacy(fr_log_t *log, bool daemonize)
949
0
{
950
0
  int devnull_legacy;
951
952
0
  fr_log_rate_limit = daemonize;
953
954
  /*
955
   *  If we're running in foreground mode, save STDIN /
956
   *  STDERR as higher FDs, which won't get used by anyone
957
   *  else.  When we fork/exec a program, its STD FDs will
958
   *  get set to pipes.  We later set STDOUT / STDERR to
959
   *  /dev/null, so that any library trying to write to them
960
   *  doesn't screw anything up.
961
   *
962
   *  Then, when something goes wrong, restore them so that
963
   *  any debugger called from the panic action has access
964
   *  to STDOUT / STDERR.
965
   */
966
0
  if (!daemonize) {
967
0
    fr_fault_set_cb(_restore_std_legacy);
968
969
0
    stdout_fd = dup(STDOUT_FILENO);
970
0
    stderr_fd = dup(STDERR_FILENO);
971
0
  }
972
973
0
  devnull_legacy = open("/dev/null", O_RDWR);
974
0
  if (devnull_legacy < 0) {
975
0
    fr_strerror_printf("Error opening /dev/null: %s", fr_syserror(errno));
976
0
    return -1;
977
0
  }
978
979
  /*
980
   *  STDOUT & STDERR go to /dev/null, unless we have "-x",
981
   *  then STDOUT & STDERR go to the "-l log" destination.
982
   *
983
   *  The complexity here is because "-l log" can go to
984
   *  STDOUT or STDERR, too.
985
   */
986
0
  if (log->dst == L_DST_STDOUT) {
987
0
    setlinebuf(stdout);
988
0
    log->fd = STDOUT_FILENO;
989
990
    /*
991
     *  If we're debugging, allow STDERR to go to
992
     *  STDOUT too, for executed programs.
993
     *
994
     *  Allow stdout when running in foreground mode
995
     *  as it's useful for some profiling tools,
996
     *  like mutrace.
997
     */
998
0
    if (fr_debug_lvl || !daemonize) {
999
0
      dup2(STDOUT_FILENO, STDERR_FILENO);
1000
0
    } else {
1001
0
      dup2(devnull_legacy, STDERR_FILENO);
1002
0
    }
1003
1004
0
  } else if (log->dst == L_DST_STDERR) {
1005
0
    setlinebuf(stderr);
1006
0
    log->fd = STDERR_FILENO;
1007
1008
    /*
1009
     *  If we're debugging, allow STDOUT to go to
1010
     *  STDERR too, for executed programs.
1011
     *
1012
     *  Allow stdout when running in foreground mode
1013
     *  as it's useful for some profiling tools,
1014
     *  like mutrace.
1015
     */
1016
0
    if (fr_debug_lvl || !daemonize) {
1017
0
      dup2(STDERR_FILENO, STDOUT_FILENO);
1018
0
    } else {
1019
0
      dup2(devnull_legacy, STDOUT_FILENO);
1020
0
    }
1021
1022
0
  } else if (log->dst == L_DST_SYSLOG) {
1023
    /*
1024
     *  Discard STDOUT and STDERR no matter what the
1025
     *  status of debugging.  Syslog isn't a file
1026
     *  descriptor, so we can't use it.
1027
     */
1028
0
    dup2(devnull_legacy, STDOUT_FILENO);
1029
0
    dup2(devnull_legacy, STDERR_FILENO);
1030
0
    log->print_level = false;
1031
1032
0
  } else if (fr_debug_lvl) {
1033
    /*
1034
     *  If we're debugging, allow STDOUT and STDERR to
1035
     *  go to the log file.
1036
     */
1037
0
    dup2(log->fd, STDOUT_FILENO);
1038
0
    dup2(log->fd, STDERR_FILENO);
1039
1040
0
  } else {
1041
    /*
1042
     *  Not debugging, and the log isn't STDOUT or
1043
     *  STDERR.  Ensure that we move both of them to
1044
     *  /dev/null, so that the calling terminal can
1045
     *  exit, and the output from executed programs
1046
     *  doesn't pollute STDOUT / STDERR.
1047
     */
1048
0
    dup2(devnull_legacy, STDOUT_FILENO);
1049
0
    dup2(devnull_legacy, STDERR_FILENO);
1050
0
  }
1051
1052
0
  close(devnull_legacy);
1053
1054
0
  fr_fault_set_log_fd(log->fd);
1055
1056
0
  return 0;
1057
0
}
1058
1059
DIAG_ON(format-nonliteral)
1060
1061
/** Initialise log dst for stdout, stderr or /dev/null
1062
 *
1063
 * @param[out] log  Destination to initialise.
1064
 * @param[in] dst_type  The specific type of log destination to initialise.
1065
 * @return
1066
 *  - 0 on success.
1067
 *  - -1 on failure.
1068
 */
1069
int fr_log_init_std(fr_log_t *log, fr_log_dst_t dst_type)
1070
0
{
1071
0
  memset(log, 0, sizeof(*log));
1072
1073
0
  log->dst = dst_type;
1074
0
  switch (log->dst) {
1075
0
  case L_DST_STDOUT:
1076
0
    log->handle = stdout;
1077
0
    break;
1078
1079
0
  case L_DST_STDERR:
1080
0
    log->handle = stderr;
1081
0
    break;
1082
1083
0
  case L_DST_NULL:
1084
0
    log->handle = devnull;
1085
0
    break;
1086
1087
0
  default:
1088
0
    fr_strerror_const("Invalid dst type for FD log destination");
1089
0
    return -1;
1090
0
  }
1091
1092
0
  return 0;
1093
0
}
1094
1095
/** Initialise a file logging destination to a FILE*
1096
 *
1097
 * @param[out] log  Destination to initialise.
1098
 * @param[in] fp  pre-existing handle
1099
 * @return
1100
 *  - 0 on success.
1101
 *  - -1 on failure.
1102
 */
1103
int fr_log_init_fp(fr_log_t *log, FILE *fp)
1104
0
{
1105
0
  memset(log, 0, sizeof(*log));
1106
1107
0
  log->dst = L_DST_FILES;
1108
0
  log->handle = fp;
1109
1110
0
  setlinebuf(log->handle);
1111
0
  log->fd = fileno(log->handle);
1112
1113
0
  return 0;
1114
0
}
1115
1116
/** Initialise a file logging destination
1117
 *
1118
 * @param[out] log  Destination to initialise.
1119
 * @param[in] file  to open handle for.
1120
 * @return
1121
 *  - 0 on success.
1122
 *  - -1 on failure.
1123
 */
1124
int fr_log_init_file(fr_log_t *log, char const *file)
1125
0
{
1126
0
  FILE *fp;
1127
1128
0
  if (unlikely((fp = fopen(file, "a")) == NULL)) {
1129
0
    fr_strerror_printf("Failed opening log file \"%s\": %s", file, fr_syserror(errno));
1130
0
    return -1;
1131
0
  }
1132
1133
0
  if (fr_log_init_fp(log, fp) < 0) return -1;
1134
1135
  /*
1136
   *  The init over-rode any filename, so we reset it here.
1137
   */
1138
0
  log->file = file;
1139
0
  return 0;
1140
0
}
1141
1142
/** Write complete lines to syslog
1143
 *
1144
 */
1145
static ssize_t _syslog_write(UNUSED void *cookie, const char *buf, size_t size)
1146
0
{
1147
0
  static int syslog_priority_table[] = {
1148
0
    [L_DBG] = LOG_DEBUG,
1149
1150
0
    [L_INFO] = LOG_INFO,
1151
0
    [L_DBG_INFO] = LOG_INFO,
1152
1153
0
    [L_ERR] = LOG_ERR,
1154
0
    [L_DBG_ERR] = LOG_ERR,
1155
0
    [L_DBG_ERR_REQ] = LOG_ERR,
1156
1157
0
    [L_WARN] = LOG_WARNING,
1158
0
    [L_DBG_WARN] = LOG_WARNING,
1159
0
    [L_DBG_WARN_REQ] = LOG_WARNING,
1160
1161
0
    [L_AUTH] = LOG_AUTH | LOG_INFO
1162
0
  };
1163
1164
0
  syslog(syslog_priority_table[log_msg_type], "%.*s", (int)size, buf);
1165
1166
0
  return size;
1167
0
}
1168
1169
/** Initialise a syslog logging destination
1170
 *
1171
 * @param[out] log  Destination to initialise.
1172
 * @return
1173
 *  - 0 on success.
1174
 *  - -1 on failure.
1175
 */
1176
int fr_log_init_syslog(fr_log_t *log)
1177
0
{
1178
0
  memset(log, 0, sizeof(*log));
1179
1180
0
  log->dst = L_DST_SYSLOG;
1181
0
  if (unlikely((log->handle = fopencookie(log, "w",
1182
0
                  (cookie_io_functions_t){
1183
0
                    .write = _syslog_write,
1184
0
                  })) == NULL)) {
1185
0
    fr_strerror_printf("Failed opening syslog transpor: %s", fr_syserror(errno));
1186
0
    return -1;
1187
0
  }
1188
1189
0
  setlinebuf(log->handle);
1190
1191
0
  return 0;
1192
0
}
1193
1194
/** Initialise a function based logging destination
1195
 *
1196
 * @note Cookie functions receive the fr_log_t which contains the uctx, not the uctx directly.
1197
 *
1198
 * @param[out] log  Destination to initialise.
1199
 * @param[in] write Called when a complete log line is ready for writing.
1200
 * @param[in] close May be NULL.  Called when the logging destination has been closed.
1201
 * @param[in] uctx  for the write and close functions.
1202
 * @return
1203
 *  - 0 on success.
1204
 *  - -1 on failure.
1205
 */
1206
int fr_log_init_func(fr_log_t *log, cookie_write_function_t write, cookie_close_function_t close, void *uctx)
1207
0
{
1208
0
  memset(log, 0, sizeof(*log));
1209
1210
0
  log->dst = L_DST_FUNC;
1211
1212
0
  if (unlikely((log->handle = fopencookie(log, "w",
1213
0
                  (cookie_io_functions_t){
1214
0
                    .write = write,
1215
0
                    .close = close
1216
0
                  })) == NULL)) {
1217
0
    fr_strerror_printf("Failed opening func transport: %s", fr_syserror(errno));
1218
0
    return -1;
1219
0
  }
1220
1221
0
  setlinebuf(log->handle);
1222
0
  log->uctx = uctx;
1223
1224
0
  return 0;
1225
0
}
1226
1227
/** Universal close function for all logging destinations
1228
 *
1229
 */
1230
int fr_log_close(fr_log_t *log)
1231
0
{
1232
0
  switch (log->dst) {
1233
0
  case L_DST_STDOUT:
1234
0
  case L_DST_STDERR:
1235
0
  case L_DST_NULL:
1236
0
    return 0;
1237
1238
  /*
1239
   *  Other log dsts
1240
   */
1241
0
  case L_DST_FILES:
1242
0
  case L_DST_FUNC:
1243
0
  case L_DST_SYSLOG:
1244
0
    if (log->handle && (fclose(log->handle) < 0)) {
1245
0
      fr_strerror_printf("Failed closing file handle: %s", fr_syserror(errno));
1246
0
      return -1;
1247
0
    }
1248
0
    return 0;
1249
1250
0
  case L_DST_NUM_DEST:
1251
0
    break;
1252
0
  }
1253
1254
0
  fr_strerror_printf("Failed closing invalid log dst %u", log->dst);
1255
0
  return -1;
1256
0
}
1257
1258
/** Manipulate stderr and stdout so that was capture all data send to it from libraries
1259
 *
1260
 * @param[in] el  The event list we use to process logging data.
1261
 * @param[in] daemonize Whether the server is starting as a daemon.
1262
 * @return
1263
 *  - 0 on success.
1264
 *  - -1 on failure.
1265
 */
1266
int fr_log_global_init(fr_event_list_t *el, bool daemonize)
1267
0
{
1268
0
  log_el = el;
1269
1270
0
  fr_log_rate_limit = daemonize;
1271
1272
  /*
1273
   *  dup the current stdout/stderr FDs and close
1274
   *      the FDs in the STDOUT/STDERR slots to get
1275
   *  the reference count back to one.
1276
   */
1277
0
  stdout_fd = dup(STDOUT_FILENO);
1278
0
  if (unlikely(stdout_fd < 0)) {
1279
0
    fr_strerror_printf("Failed cloning stdout FD: %s", fr_syserror(errno));
1280
0
    return -1;
1281
0
  }
1282
1283
  /*
1284
   *  Create two unidirection pipes, duping one end
1285
   *      to the stdout/stderr slots and inserting the
1286
   *  other into our event loop
1287
   */
1288
0
  if (unlikely(pipe(stdout_pipe) < 0)) {
1289
0
    fr_strerror_printf("Failed creating logging pipes: %s", fr_syserror(errno));
1290
0
  error_0:
1291
0
    log_el = NULL;
1292
0
    close(stdout_fd);
1293
0
    return -1;
1294
0
  }
1295
1296
  /*
1297
   *  This closes the other ref to the stdout FD.
1298
   */
1299
0
  if (unlikely(dup2(stdout_pipe[0], STDOUT_FILENO) < 0)) {
1300
0
    fr_strerror_printf("Failed copying pipe end over stdout: %s", fr_syserror(errno));
1301
0
  error_1:
1302
0
    close(stdout_pipe[0]);
1303
0
    stdout_pipe[0] = -1;
1304
0
    close(stdout_pipe[1]);
1305
0
    stdout_pipe[1] = -1;
1306
0
    goto error_0;
1307
0
  }
1308
1309
0
  stdout_ctx.dst = &default_log;
1310
0
  stdout_ctx.prefix = "(stdout)";
1311
0
  stdout_ctx.type = L_DBG;
1312
0
  stdout_ctx.lvl = L_DBG_LVL_2;
1313
1314
  /*
1315
   *  Now do stderr...
1316
   */
1317
0
  if (unlikely(fr_event_fd_insert(NULL, NULL, el, stdout_pipe[1], fr_log_fd_event, NULL, NULL, &stdout_ctx) < 0)) {
1318
0
    fr_strerror_const_push("Failed adding stdout handler to event loop");
1319
0
  error_2:
1320
0
    dup2(STDOUT_FILENO, stdout_fd);  /* Copy back the stdout FD */
1321
0
    goto error_1;
1322
0
  }
1323
1324
0
  stderr_fd = dup(STDERR_FILENO);
1325
0
  if (unlikely(stderr_fd < 0)) {
1326
0
    fr_strerror_printf("Failed cloning stderr FD: %s", fr_syserror(errno));
1327
1328
0
  error_3:
1329
0
    fr_event_fd_delete(el, stdout_pipe[1], FR_EVENT_FILTER_IO);
1330
0
    goto error_2;
1331
0
  }
1332
1333
0
  if (unlikely(pipe(stderr_pipe) < 0)) {
1334
0
    fr_strerror_printf("Failed creating logging pipes: %s", fr_syserror(errno));
1335
0
  error_4:
1336
0
    close(stderr_fd);
1337
0
    goto error_3;
1338
0
  }
1339
1340
0
  if (unlikely(dup2(stderr_pipe[0], STDERR_FILENO) < 0)) {
1341
0
    fr_strerror_printf("Failed copying pipe end over stderr: %s", fr_syserror(errno));
1342
0
  error_5:
1343
0
    close(stderr_pipe[0]);
1344
0
    stderr_pipe[0] = -1;
1345
0
    close(stderr_pipe[1]);
1346
0
    stderr_pipe[1] = -1;
1347
0
    goto error_4;
1348
0
  }
1349
1350
0
  stderr_ctx.dst = &default_log;
1351
0
  stderr_ctx.prefix = "(stderr)";
1352
0
  stderr_ctx.type = L_ERR;
1353
0
  stderr_ctx.lvl = L_DBG_LVL_OFF; /* Log at all debug levels */
1354
1355
0
  if (unlikely(fr_event_fd_insert(NULL, NULL, el, stderr_pipe[1], fr_log_fd_event, NULL, NULL, &stderr_ctx) < 0)) {
1356
0
    fr_strerror_const_push("Failed adding stdout handler to event loop");
1357
0
  error_6:
1358
0
    dup2(STDERR_FILENO, stderr_fd);  /* Copy back the stderr FD */
1359
0
    goto error_5;
1360
0
  }
1361
1362
0
  fr_fault_set_log_fd(STDERR_FILENO);
1363
0
  fr_fault_set_cb(_restore_std_legacy);   /* Restore the original file descriptors if we experience a fault */
1364
1365
  /*
1366
   *  Setup our standard file *s
1367
   */
1368
0
  setlinebuf(stdout);
1369
0
  setlinebuf(stderr);
1370
1371
0
  devnull = fopen("/dev/null", "w");
1372
0
  if (unlikely(!devnull)) {
1373
0
    fr_strerror_printf("Error opening /dev/null: %s", fr_syserror(errno));
1374
0
    goto error_6;
1375
0
  }
1376
1377
0
  fr_log_init_std(&default_log, L_DST_STDOUT);
1378
1379
0
  return 0;
1380
0
}
1381
1382
/** Restores the original stdout and stderr FDs, closes the pipes and removes them from the event loop
1383
 *
1384
 */
1385
void fr_log_global_free(void)
1386
0
{
1387
0
  if (!log_el) return;
1388
1389
0
  fr_event_fd_delete(log_el, stdout_pipe[1], FR_EVENT_FILTER_IO);
1390
0
  close(stdout_pipe[1]);
1391
0
  stdout_pipe[1] = -1;
1392
0
  fr_event_fd_delete(log_el, stderr_pipe[1], FR_EVENT_FILTER_IO);
1393
0
  close(stderr_pipe[1]);
1394
0
  stderr_pipe[1] = -1;
1395
1396
0
  _restore_std_legacy(0); /* Will close stdout_pipe[0] and stderr_pipe[0] with dup2 */
1397
1398
0
  stdout_pipe[0] = -1;
1399
0
  stderr_pipe[0] = -1;
1400
1401
0
  fclose(devnull);
1402
0
}