Coverage Report

Created: 2026-10-18 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/log.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program 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
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: 6c865dacd2164db9b6c9550a3a908f0651c342e0 $
19
 *
20
 * @brief Logging functions used by the server core.
21
 * @file src/lib/server/log.c
22
 *
23
 * @copyright 2000,2006 The FreeRADIUS server project
24
 * @copyright 2000 Miquel van Smoorenburg (miquels@cistron.nl)
25
 * @copyright 2000 Alan DeKok (aland@freeradius.org)
26
 * @copyright 2001 Chad Miller (cmiller@surfsouth.com)
27
 */
28
RCSID("$Id: 6c865dacd2164db9b6c9550a3a908f0651c342e0 $")
29
30
#include <freeradius-devel/util/debug.h>
31
#include <freeradius-devel/util/dict.h>
32
#include <freeradius-devel/util/log.h>
33
#include <freeradius-devel/util/syserror.h>
34
#include <freeradius-devel/util/file.h>
35
36
#include <freeradius-devel/server/log.h>
37
#include <freeradius-devel/server/pair.h>
38
#include <freeradius-devel/server/util.h>
39
40
#include <freeradius-devel/unlang/xlat.h>
41
42
#ifdef HAVE_SYS_STAT_H
43
#  include <sys/stat.h>
44
#endif
45
46
#include <fcntl.h>
47
48
#ifdef HAVE_SYSLOG_H
49
#  include <syslog.h>
50
#endif
51
52
#include <sys/file.h>
53
#include <pthread.h>
54
55
static _Thread_local TALLOC_CTX *fr_vlog_request_pool;
56
static _Thread_local fr_sbuff_t *fr_log_request_oid_buff;
57
58
/** Syslog facility table
59
 *
60
 * Maps syslog facility keywords, to the syslog facility macros defined
61
 * in the system's syslog.h.
62
 *
63
 * @note Not all facilities are supported by every operating system.
64
 *       If a facility is unavailable it will not appear in the table.
65
 */
66
fr_table_num_sorted_t const syslog_facility_table[] = {
67
#ifdef LOG_AUTH
68
  { L("auth"),    LOG_AUTH  },
69
#endif
70
71
#ifdef LOG_AUTHPRIV
72
  { L("authpriv"),  LOG_AUTHPRIV  },
73
#endif
74
75
#ifdef LOG_CRON
76
  { L("cron"),    LOG_CRON  },
77
#endif
78
79
#ifdef LOG_DAEMON
80
  { L("daemon"),    LOG_DAEMON  },
81
#endif
82
83
#ifdef LOG_FTP
84
  { L("ftp"),   LOG_FTP   },
85
#endif
86
87
#ifdef LOG_KERN
88
  { L("kern"),    LOG_KERN  },
89
#endif
90
91
#ifdef LOG_LOCAL0
92
  { L("local0"),    LOG_LOCAL0  },
93
#endif
94
95
#ifdef LOG_LOCAL1
96
  { L("local1"),    LOG_LOCAL1  },
97
#endif
98
99
#ifdef LOG_LOCAL2
100
  { L("local2"),    LOG_LOCAL2  },
101
#endif
102
103
#ifdef LOG_LOCAL3
104
  { L("local3"),    LOG_LOCAL3  },
105
#endif
106
107
#ifdef LOG_LOCAL4
108
  { L("local4"),    LOG_LOCAL4  },
109
#endif
110
111
#ifdef LOG_LOCAL5
112
  { L("local5"),    LOG_LOCAL5  },
113
#endif
114
115
#ifdef LOG_LOCAL6
116
  { L("local6"),    LOG_LOCAL6  },
117
#endif
118
119
#ifdef LOG_LOCAL7
120
  { L("local7"),    LOG_LOCAL7  },
121
#endif
122
123
#ifdef LOG_LPR
124
  { L("lpr"),   LOG_LPR   },
125
#endif
126
127
#ifdef LOG_MAIL
128
  { L("mail"),    LOG_MAIL  },
129
#endif
130
131
#ifdef LOG_NEWS
132
  { L("news"),    LOG_NEWS  },
133
#endif
134
135
#ifdef LOG_USER
136
  { L("user"),    LOG_USER  },
137
#endif
138
139
#ifdef LOG_UUCP
140
  { L("uucp"),    LOG_UUCP  }
141
#endif
142
};
143
size_t syslog_facility_table_len = NUM_ELEMENTS(syslog_facility_table);
144
145
/** Syslog severity table
146
 *
147
 * Maps syslog severity keywords, to the syslog severity macros defined
148
 * in the system's syslog.h file.
149
 *
150
 */
151
fr_table_num_sorted_t const syslog_severity_table[] = {
152
#ifdef LOG_ALERT
153
  { L("alert"),   LOG_ALERT },
154
#endif
155
156
#ifdef LOG_CRIT
157
  { L("critical"),  LOG_CRIT  },
158
#endif
159
160
#ifdef LOG_DEBUG
161
  { L("debug"),   LOG_DEBUG },
162
#endif
163
164
#ifdef LOG_EMERG
165
  { L("emergency"), LOG_EMERG },
166
#endif
167
168
#ifdef LOG_ERR
169
  { L("error"),   LOG_ERR   },
170
#endif
171
172
#ifdef LOG_INFO
173
  { L("info"),    LOG_INFO  },
174
#endif
175
176
#ifdef LOG_NOTICE
177
  { L("notice"),    LOG_NOTICE  },
178
#endif
179
180
#ifdef LOG_WARNING
181
  { L("warning"),   LOG_WARNING },
182
#endif
183
};
184
size_t syslog_severity_table_len = NUM_ELEMENTS(syslog_severity_table);
185
186
fr_table_num_sorted_t const log_destination_table[] = {
187
  { L("file"),    L_DST_FILES },
188
  { L("files"),   L_DST_FILES },
189
  { L("null"),    L_DST_NULL  },
190
  { L("stderr"),    L_DST_STDERR  },
191
  { L("stdout"),    L_DST_STDOUT  },
192
  { L("syslog"),    L_DST_SYSLOG  },
193
};
194
size_t log_destination_table_len = NUM_ELEMENTS(log_destination_table);
195
196
static char const spaces[] = "                                                                                                                        ";
197
198
static fr_dict_t const *dict_freeradius;
199
200
extern fr_dict_autoload_t log_dict[];
201
fr_dict_autoload_t log_dict[] = {
202
  { .out = &dict_freeradius, .proto = "freeradius" },
203
  DICT_AUTOLOAD_TERMINATOR
204
};
205
206
static fr_dict_attr_t const *attr_module_failure_message;
207
208
extern fr_dict_attr_autoload_t log_dict_attr[];
209
fr_dict_attr_autoload_t log_dict_attr[] = {
210
  { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
211
  DICT_AUTOLOAD_TERMINATOR
212
};
213
214
typedef struct {
215
  char const  *name;    //!< name of this logging destination
216
  fr_log_t  *log;   //!< pointer to the log structure
217
  CONF_SECTION  *cs;    //!< where this log configuration came from
218
219
  fr_rb_node_t  name_node;      //!< tree by name
220
  fr_rb_node_t  filename_node;  //!< tree by name
221
} fr_log_track_t;
222
223
typedef struct {
224
  char const  *name;    //!< name of this logging source
225
  uint32_t  id;   //!< LOG_ID of this source
226
  fr_log_t  *original;  //!< the original fr_log_t
227
  fr_log_t  **log;    //!< where the logs should go
228
229
  fr_rb_node_t  name_node;  //!< tree by name only
230
  fr_rb_node_t  id_node;  //!< tree by ID
231
} fr_log_src_t;
232
233
static fr_rb_tree_t *dst_tree = NULL;
234
static fr_rb_tree_t *filename_tree = NULL;
235
static fr_rb_tree_t *src_tree = NULL;
236
237
/** Send a server log message to its destination without evaluating its debug level
238
 *
239
 * @param[in] log destination.
240
 * @param[in] type  of log message.
241
 * @param[in] file  src file the log message was generated in.
242
 * @param[in] line  number the log message was generated on.
243
 * @param[in] fmt with printf style substitution tokens.
244
 * @param[in] ... Substitution arguments.
245
 */
246
247
static CC_HINT(format (printf, 5, 6))
248
void log_always(fr_log_t const *log, fr_log_type_t type,
249
    char const *file, int line,
250
    char const *fmt, ...)
251
0
{
252
0
  va_list ap;
253
254
0
  va_start(ap, fmt);
255
0
  fr_vlog(log, type, file, line, fmt, ap);
256
0
  va_end(ap);
257
0
}
258
259
/** Whether a request specific debug message should be logged
260
 *
261
 * @param lvl of debugging this message should be logged at.
262
 * @param request The current request.
263
 * @return
264
 *  - true if message should be logged.
265
 *  - false if message shouldn't be logged.
266
 */
267
inline bool log_rdebug_enabled(fr_log_lvl_t lvl, request_t const *request)
268
0
{
269
0
  if (!request->log.dst) return false;
270
271
0
  return (request->log.lvl >= lvl);
272
0
}
273
274
/** Cleanup the memory pool used by vlog_request
275
 *
276
 */
277
static int _fr_vlog_request_pool_free(void *arg)
278
0
{
279
0
  return talloc_free(arg);
280
0
}
281
282
/** Send a log message to its destination, possibly including fields from the request
283
 *
284
 * @param[in] type  of log message, #L_ERR, #L_WARN, #L_INFO, #L_DBG.
285
 * @param[in] lvl Minimum required server or request level to output this message.
286
 * @param[in] request The current request.
287
 * @param[in] file  src file the log message was generated in.
288
 * @param[in] line  number the log message was generated on.
289
 * @param[in] fmt with printf style substitution tokens.
290
 * @param[in] ap  Substitution arguments.
291
 * @param[in] uctx  The #fr_log_t specifying the destination for log messages.
292
 */
293
void vlog_request(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request,
294
      char const *file, int line,
295
      char const *fmt, va_list ap, void *uctx)
296
0
{
297
0
  char const  *filename;
298
0
  FILE    *fp = NULL;
299
300
0
  char    *p;
301
0
  char const  *extra = "";
302
0
  uint8_t   unlang_indent;
303
0
  va_list   aq;
304
305
0
  char const  *fmt_location = "";
306
0
  char const  *fmt_prefix = "";
307
0
  char const  *fmt_module = "";
308
0
  char const  *fmt_exp;
309
310
0
  fr_log_t  *log_dst = uctx;
311
0
  TALLOC_CTX  *pool;
312
313
  /*
314
   *  No output means no output.
315
   */
316
0
  if (!log_dst) return;
317
0
  if (!log_rdebug_enabled(lvl, request)) return;
318
319
  /*
320
   *  Allocate a thread local, 4k pool so we don't
321
   *      need to keep allocating memory on the heap.
322
   */
323
0
  pool = fr_vlog_request_pool;
324
0
  if (!pool) {
325
0
    pool = talloc_pool(NULL, 4096);
326
0
    if (!pool) {
327
0
      fr_perror("Failed allocating memory for vlog_request_pool");
328
0
      return;
329
0
    }
330
0
    fr_atexit_thread_local(fr_vlog_request_pool, _fr_vlog_request_pool_free, pool);
331
0
  }
332
333
0
  filename = log_dst->file;
334
335
  /*
336
   *  Debug messages get treated specially.
337
   */
338
0
  if ((type & L_DBG) != 0) {
339
    /*
340
     *  If we're debugging to a file, then use that.
341
     *
342
     *  @todo: have fr_vlog() take a fr_log_t*, so
343
     *  that we can cache the opened descriptor, and
344
     *  we don't need to re-open it on every log
345
     *  message.
346
     */
347
0
    switch (log_dst->dst) {
348
0
    case L_DST_FILES:
349
0
      fp = fopen(log_dst->file, "a");
350
0
      if (!fp) goto finish;
351
0
      break;
352
353
0
#if defined(HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
354
0
    case L_DST_FUNC:
355
0
    {
356
0
#  ifdef HAVE_FOPENCOOKIE
357
0
      cookie_io_functions_t io;
358
359
      /*
360
       *  These must be set separately as they have different prototypes.
361
       */
362
0
      io.read = NULL;
363
0
      io.seek = NULL;
364
0
      io.close = NULL;
365
0
      io.write = log_dst->cookie_write;
366
367
0
      fp = fopencookie(log_dst->cookie, "w", io);
368
#  else
369
      fp = funopen(log_dst->cookie, NULL, log_dst->cookie_write, NULL, NULL);
370
371
#  endif
372
0
      if (!fp) goto finish;
373
0
    }
374
0
    break;
375
0
#endif
376
0
    default:
377
0
      break;
378
0
    }
379
0
    goto print_fmt;
380
0
  }
381
382
0
  if (filename) {
383
0
    char    *exp;
384
0
    log_dst_t *dst;
385
386
0
    dst = request->log.dst;
387
388
    /*
389
     *  Prevent infinitely recursive calls if
390
     *  xlat_aeval attempts to write to the request log.
391
     */
392
0
    request->log.dst = NULL;
393
394
    /*
395
     *  This is SLOW!  Doing it for every log message
396
     *  in every request is NOT recommended!
397
     */
398
0
    if (xlat_aeval(request, &exp, request, filename, rad_filename_escape, NULL) < 0) return;
399
400
    /*
401
     *  Restore the original logging function
402
     */
403
0
    request->log.dst = dst;
404
405
    /*
406
     *  Ensure the directory structure exists, for
407
     *  where we're going to write the log file.
408
     */
409
0
    p = strrchr(exp, FR_DIR_SEP);
410
0
    if (p) {
411
0
      *p = '\0';
412
0
      if (fr_mkdir(NULL, exp, -1, S_IRWXU, NULL, NULL) < 0) {
413
0
        ERROR("Failed creating %s: %s", exp, fr_syserror(errno));
414
0
        talloc_free(exp);
415
0
        return;
416
0
      }
417
0
      *p = FR_DIR_SEP;
418
0
    }
419
420
0
    fp = fopen(exp, "a");
421
0
    talloc_free(exp);
422
0
  }
423
424
0
print_fmt:
425
  /*
426
   *  Request prefix i.e.
427
   *
428
   *  (0) <fmt>
429
   */
430
0
  if (request->name) {
431
0
    if ((request->seq_start == 0) || (request->number == request->seq_start)) {
432
0
      fmt_prefix = talloc_typed_asprintf(pool, "(%s)  ", request->name);
433
0
    } else {
434
0
      fmt_prefix = talloc_typed_asprintf(pool, "(%s,%" PRIu64 ")  ",
435
0
                 request->name, request->seq_start);
436
0
    }
437
0
  }
438
439
  /*
440
   *  Make sure the indent isn't set to something crazy
441
   */
442
0
  unlang_indent = request->log.indent.unlang > sizeof(spaces) - 1 ?
443
0
      sizeof(spaces) - 1 :
444
0
      request->log.indent.unlang;
445
446
  /*
447
   *  Module name and indentation i.e.
448
   *
449
   *  test -     <fmt>
450
   */
451
0
  if (request->module) {
452
0
    uint8_t module_indent = request->log.indent.module > sizeof(spaces) - 1 ?
453
0
          sizeof(spaces) - 1 :
454
0
          request->log.indent.module;
455
0
    fmt_module = talloc_typed_asprintf(pool, "%s - %.*s", request->module, module_indent, spaces);
456
0
  }
457
458
  /*
459
   *  If we don't copy the original ap we get a segfault from vasprintf. This is apparently
460
   *  due to ap sometimes being implemented with a stack offset which is invalidated if
461
   *  ap is passed into another function. See here:
462
   *  http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
463
   *
464
   *  I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
465
   *  running unit tests which generate errors under CI.
466
   */
467
0
  va_copy(aq, ap);
468
0
  if (!log_dst->suppress_secrets) {
469
0
    fmt_exp = fr_vasprintf(pool, fmt, aq);
470
0
  } else {
471
0
    fmt_exp = fr_vasprintf_secure(pool, fmt, aq);
472
0
  }
473
0
  va_end(aq);
474
475
  /*
476
   *  Logging to a file descriptor
477
   */
478
0
  if (fp) {
479
0
    char time_buff[64]; /* The current timestamp */
480
481
0
    time_t timeval;
482
0
    timeval = time(NULL);
483
484
#if 0
485
    fmt_location = talloc_typed_asprintf(pool, "%s[%i]: ", file, line);
486
#endif
487
488
0
#ifdef HAVE_GMTIME_R
489
0
    if (log_dates_utc) {
490
0
      struct tm utc;
491
0
      gmtime_r(&timeval, &utc);
492
0
      ASCTIME_R(&utc, time_buff, sizeof(time_buff));
493
0
    } else
494
0
#endif
495
0
    {
496
0
      CTIME_R(&timeval, time_buff, sizeof(time_buff));
497
0
    }
498
499
    /*
500
     *  Strip trailing new lines
501
     */
502
0
    p = strrchr(time_buff, '\n');
503
0
    if (p) p[0] = '\0';
504
505
0
    fprintf(fp,
506
0
      "%s"    /* location */
507
0
      "%s"    /* prefix */
508
0
      "%s : "   /* time */
509
0
      "%s"    /* facility */
510
0
      "%.*s"    /* indent */
511
0
      "%s"    /* module */
512
0
      "%s"    /* message */
513
0
      "\n",
514
0
      fmt_location,
515
0
      fmt_prefix,
516
0
      time_buff,
517
0
      fr_table_str_by_value(fr_log_levels, type, ""),
518
0
      unlang_indent, spaces,
519
0
      fmt_module,
520
0
      fmt_exp);
521
0
    fclose(fp);
522
0
    goto finish;
523
0
  }
524
525
  /*
526
   *  Logging everywhere else
527
   *
528
   *  Add the WARN / ERROR message after the request number, so it's clearer to the user that the
529
   *  message is associated with the request.  Set the type too, which lets fr_vlog() know that it
530
   *  shouldn't add the prefix.
531
   */
532
0
  switch (type) {
533
0
  case L_DBG_WARN:
534
0
    extra = "WARNING: ";
535
0
    type = L_DBG_WARN_REQ;
536
0
    break;
537
538
0
  case L_DBG_ERR:
539
0
    extra = "ERROR: ";
540
0
    type = L_DBG_ERR_REQ;
541
0
    break;
542
0
  default:
543
0
    break;
544
0
  }
545
546
0
  log_always(log_dst, type, file, line,
547
0
       "%s"     /* prefix */
548
0
       "%.*s"   /* indent */
549
0
       "%s"     /* module */
550
0
       "%s"     /* extra */
551
0
       "%s",    /* message */
552
0
       fmt_prefix,
553
0
       unlang_indent, spaces,
554
0
       fmt_module,
555
0
       extra,
556
0
       fmt_exp);
557
558
0
finish:
559
0
  talloc_free_children(pool);
560
0
}
561
562
/** Add a module failure message fr_pair_t to the request
563
 *
564
 * @param[in] request The current request.
565
 * @param[in] fmt with printf style substitution tokens.
566
 * @param[in] ap  Substitution arguments.
567
 */
568
static void vlog_module_failure_msg(request_t *request, char const *fmt, va_list ap)
569
0
{
570
0
  char    *p;
571
0
  fr_pair_t *vp;
572
0
  va_list   aq;
573
574
0
  if (!fmt || !request || !request->packet) return;
575
576
0
  fr_assert(attr_module_failure_message);
577
578
0
  MEM(pair_prepend_request(&vp, attr_module_failure_message) >= 0);
579
580
  /*
581
   *  If we don't copy the original ap we get a segfault from vasprintf. This is apparently
582
   *  due to ap sometimes being implemented with a stack offset which is invalidated if
583
   *  ap is passed into another function. See here:
584
   *  http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
585
   *
586
   *  I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
587
   *  running unit tests which generate errors under CI.
588
   */
589
0
  va_copy(aq, ap);
590
0
  p = fr_vasprintf(vp, fmt, aq);
591
0
  va_end(aq);
592
593
0
  if (request->module && (request->module[0] != '\0')) {
594
0
    fr_pair_value_aprintf(vp, "%s: %s", request->module, p);
595
0
    talloc_free(p);
596
0
  } else {
597
0
    fr_pair_value_strdup_shallow(vp, p, true);
598
0
  }
599
0
}
600
601
/** Marshal variadic log arguments into a va_list and pass to normal logging functions
602
 *
603
 * @see log_request_error for more details.
604
 *
605
 * @param[in] type  the log category.
606
 * @param[in] lvl of debugging this message should be logged at.
607
 * @param[in] request The current request.
608
 * @param[in] file  src file the log message was generated in.
609
 * @param[in] line  number the log message was generated on.
610
 * @param[in] fmt with printf style substitution tokens.
611
 * @param[in] ... Substitution arguments.
612
 */
613
void log_request(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request,
614
     char const *file, int line, char const *fmt, ...)
615
0
{
616
0
  va_list   ap;
617
618
0
  if (!request->log.dst) return;
619
620
0
  va_start(ap, fmt);
621
0
  log_dst_foreach(request->log.dst, dst) {
622
0
    if ((lvl > request->log.lvl) && (lvl > dst->lvl)) continue;
623
624
0
    dst->func(type, lvl, request, file, line, fmt, ap, dst->uctx);
625
0
  }
626
0
  va_end(ap);
627
0
}
628
629
/** Marshal variadic log arguments into a va_list and pass to error logging functions
630
 *
631
 * This could all be done in a macro, but it turns out some implementations of the
632
 * variadic macros do not work at all well if the va_list being written to is further
633
 * up the stack (which is required as you still need a function to convert the ellipsis
634
 * into a va_list).
635
 *
636
 * So, we use this small wrapper function instead, which will hopefully guarantee
637
 * consistent behaviour.
638
 *
639
 * @param[in] type  the log category.
640
 * @param[in] lvl of debugging this message should be logged at.
641
 * @param[in] request The current request.
642
 * @param[in] file  src file the log message was generated in.
643
 * @param[in] line  number the log message was generated on.
644
 * @param[in] fmt with printf style substitution tokens.
645
 * @param[in] ... Substitution arguments.
646
 */
647
void log_request_error(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request,
648
           char const *file, int line, char const *fmt, ...)
649
0
{
650
0
  va_list   ap;
651
652
0
  if (!request->log.dst) return;
653
654
0
  va_start(ap, fmt);
655
0
  log_dst_foreach(request->log.dst, dst) {
656
0
    va_list copy;
657
0
    va_copy(copy, ap);
658
659
0
    dst->func(type, lvl, request, file, line, fmt, copy, dst->uctx);
660
661
0
    va_end(copy);
662
0
  }
663
664
0
  if ((type == L_ERR) || (type == L_DBG_ERR) || (type == L_DBG_ERR_REQ)) {
665
0
    vlog_module_failure_msg(request, fmt, ap);
666
0
  }
667
668
0
  va_end(ap);
669
0
}
670
671
/** Drain any outstanding messages from the fr_strerror buffers
672
 *
673
 * This function drains any messages from fr_strerror buffer adding a prefix (fmt)
674
 * to the first message.
675
 *
676
 * @param[in] type  the log category.
677
 * @param[in] lvl of debugging this message should be logged at.
678
 * @param[in] request The current request.
679
 * @param[in] file  src file the log message was generated in.
680
 * @param[in] line  number the log message was generated on.
681
 * @param[in] fmt with printf style substitution tokens.
682
 * @param[in] ... Substitution arguments.
683
 */
684
void log_request_perror(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request,
685
      char const *file, int line, char const *fmt, ...)
686
0
{
687
0
  char const *error;
688
689
0
  if (!request->log.dst) return;
690
691
  /*
692
   *  No strerror gets us identical behaviour to log_request_error
693
   */
694
0
  error = fr_strerror_pop();
695
0
  if (!error) {
696
0
    va_list   ap;
697
698
0
    if (!fmt) return; /* NOOP */
699
700
0
    va_start(ap, fmt);
701
0
    log_dst_foreach(request->log.dst, dst) {
702
0
      va_list copy;
703
0
      va_copy(copy, ap);
704
705
0
      dst->func(type, lvl, request, file, line, fmt, copy, dst->uctx);
706
707
0
      va_end(copy);
708
0
    }
709
0
    if ((type == L_ERR) || (type == L_DBG_ERR) || (type == L_DBG_ERR_REQ)) {
710
0
      vlog_module_failure_msg(request, fmt, ap);
711
0
    }
712
0
    va_end(ap);
713
714
0
    return;     /* DONE */
715
0
  }
716
717
  /*
718
   *  Concatenate fmt with fr_strerror()
719
   */
720
0
  if (fmt) {
721
0
    va_list ap;
722
0
    char *tmp;
723
724
0
    va_start(ap, fmt);
725
0
    tmp = fr_vasprintf(request, fmt, ap);
726
0
    va_end(ap);
727
728
0
    if (!tmp) return;
729
730
0
    log_request_error(type, lvl, request, file, line, "%s: %s", tmp, error);
731
0
    talloc_free(tmp);
732
0
  } else {
733
0
    log_request_error(type, lvl, request, file, line, "%s", error);
734
0
  }
735
736
  /*
737
   *  Only the first message gets the prefix
738
   */
739
0
  while ((error = fr_strerror_pop())) log_request_error(type, lvl, request, file, line, "%s", error);
740
0
}
741
742
/** Cleanup the memory pool used by the OID sbuff
743
 *
744
 */
745
static int _fr_log_request_oid_buff_free(void *arg)
746
0
{
747
0
  return talloc_free(arg);
748
0
}
749
750
/** Allocate an extensible sbuff for printing OID strings
751
 *
752
 */
753
static inline CC_HINT(always_inline) fr_sbuff_t *log_request_oid_buff(void)
754
0
{
755
0
  fr_sbuff_t    *sbuff;
756
0
  fr_sbuff_uctx_talloc_t  *tctx;
757
758
0
  sbuff = fr_log_request_oid_buff;
759
0
  if (unlikely(!sbuff)) {
760
0
    sbuff = talloc(NULL, fr_sbuff_t);
761
0
    if (!sbuff) {
762
0
      fr_perror("Failed allocating memory for fr_log_request_oid_buff");
763
0
      return NULL;
764
0
    }
765
0
    tctx = talloc(sbuff, fr_sbuff_uctx_talloc_t);
766
0
    if (!tctx) {
767
0
      fr_perror("Failed allocating memory for fr_sbuff_uctx_talloc_t");
768
0
      talloc_free(sbuff);
769
0
      return NULL;
770
0
    }
771
772
0
    fr_sbuff_init_talloc(sbuff, sbuff, tctx, 1024, (FR_DICT_ATTR_MAX_NAME_LEN + 1) * FR_DICT_MAX_TLV_STACK);
773
774
0
    fr_atexit_thread_local(fr_log_request_oid_buff, _fr_log_request_oid_buff_free, sbuff);
775
0
  } else {
776
0
    fr_sbuff_set(sbuff, fr_sbuff_start(sbuff)); /* Reset position */
777
0
  }
778
779
0
  return sbuff;
780
0
}
781
782
/** Print a #fr_pair_t.
783
 *
784
 * @param[in] lvl Debug lvl (1-4).
785
 * @param[in] request to read logging params from.
786
 * @param[in] parent  of pair to print, may be NULL.
787
 * @param[in] vp  to print.
788
 * @param[in] prefix  (optional).
789
 */
790
void log_request_pair(fr_log_lvl_t lvl, request_t *request,
791
          fr_pair_t const *parent, fr_pair_t const *vp, char const *prefix)
792
0
{
793
0
  fr_sbuff_t    *oid_buff;
794
795
0
  if (!request->log.dst) return;
796
797
0
  if (!log_rdebug_enabled(lvl, request)) return;
798
799
0
  PAIR_VERIFY(vp);
800
801
0
  oid_buff = log_request_oid_buff();
802
803
0
  if (fr_pair_print_name(oid_buff, parent ? parent->da : NULL, &vp) <= 0) return;
804
805
  /*
806
   *  Recursively print grouped attributes.
807
   */
808
0
  switch (vp->vp_type) {
809
0
  case FR_TYPE_STRUCTURAL:
810
0
    RDEBUGX(lvl, "%s%s{", prefix ? prefix : "", fr_sbuff_start(oid_buff));
811
0
    log_request_pair_list(lvl, request, vp, &vp->vp_group, NULL);
812
0
    RDEBUGX(lvl, "}");
813
0
    break;
814
815
0
  default:
816
0
    fr_assert(fr_type_is_leaf(vp->vp_type));
817
818
0
    RDEBUGX(lvl, "%s%s%pV", prefix ? prefix : "", fr_sbuff_start(oid_buff), &vp->data);
819
0
    break;
820
0
  }
821
0
}
822
823
/** Print a #fr_pair_list_t
824
 *
825
 * @param[in] lvl Debug lvl (1-4).
826
 * @param[in] request to read logging params from.
827
 * @param[in] parent  of vps to print, may be NULL.
828
 * @param[in] vps to print.
829
 * @param[in] prefix  (optional).
830
 */
831
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request,
832
         fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
833
0
{
834
0
  if (fr_pair_list_empty(vps) || !request->log.dst) return;
835
836
0
  if (!log_rdebug_enabled(lvl, request)) return;
837
838
0
  RINDENT();
839
0
  fr_pair_list_foreach(vps, vp) {
840
0
    PAIR_VERIFY(vp);
841
842
0
    log_request_pair(lvl, request, parent, vp, prefix);
843
0
  }
844
0
  REXDENT();
845
0
}
846
847
/** Print a packet which we received or sent
848
 *
849
 * @param[in] request to read logging params from.
850
 * @param[in] packet  the packet in question
851
 * @param[in] list  to print.
852
 * @param[in] type_da of Packet-Type
853
 * @param[in] received  is this packet being received or being sent
854
 * @param[in] id  do we log IDs, too
855
 */
856
void log_request_packet(request_t *request, fr_packet_t *packet, fr_pair_list_t *list, fr_dict_attr_t const *type_da, bool received, bool id)
857
0
{
858
0
  ssize_t slen;
859
0
  fr_dict_enum_value_t const *enumv;
860
0
  char const *name;
861
0
  char name_buffer[32];
862
0
  char id_buffer[32];
863
0
  char buffer[256];
864
865
0
  if (!RDEBUG_ENABLED) return;
866
867
0
  slen = fr_socket_to_str(buffer, sizeof(buffer), &packet->socket, received);
868
0
  if (slen <= 0) buffer[0] = '\0';
869
870
0
  if (id) {
871
0
    (void) snprintf(id_buffer, sizeof(id_buffer), " ID %u", (unsigned int)packet->id);
872
0
  } else {
873
0
    id_buffer[0] = '\0';
874
0
  }
875
876
0
  enumv = fr_dict_enum_by_value(type_da, fr_box_uint32((uint32_t) packet->code));
877
0
  if (enumv) {
878
0
    name = enumv->name;
879
0
  } else {
880
0
    (void) snprintf(name_buffer, sizeof(name_buffer), "%u", packet->code);
881
0
    name = name_buffer;
882
0
  }
883
884
0
  log_request(L_DBG, L_DBG_LVL_1, request, __FILE__, __LINE__, "%s %s%s %s",
885
0
        received ? "Received" : "Sending",
886
0
        name,
887
0
        id_buffer,
888
0
        buffer);
889
890
0
  if (received || request->parent) {
891
0
    log_request_pair_list(L_DBG_LVL_1, request, NULL, list, NULL);
892
0
  } else {
893
0
    log_request_proto_pair_list(L_DBG_LVL_1, request, NULL, list, NULL);
894
0
  }
895
0
}
896
897
/** Print a list of protocol fr_pair_ts.
898
 *
899
 * @param[in] lvl Debug lvl (1-4).
900
 * @param[in] request to read logging params from.
901
 * @param[in] parent  of vps to print, may be NULL.
902
 * @param[in] vps to print.
903
 * @param[in] prefix  (optional).
904
 */
905
void log_request_proto_pair_list(fr_log_lvl_t lvl, request_t *request,
906
         fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
907
0
{
908
0
  if (fr_pair_list_empty(vps) || !request->log.dst) return;
909
910
0
  if (!log_rdebug_enabled(lvl, request)) return;
911
912
0
  RINDENT();
913
0
  fr_pair_list_foreach(vps, vp) {
914
0
    PAIR_VERIFY(vp);
915
916
0
    if (!fr_dict_attr_common_parent(fr_dict_root(request->proto_dict), vp->da, true)) continue;
917
918
0
    log_request_pair(lvl, request, parent, vp, prefix);
919
0
  }
920
0
  REXDENT();
921
0
}
922
923
/** Write the string being parsed, and a marker showing where the parse error occurred
924
 *
925
 * @param[in] type    the log category.
926
 * @param[in] lvl   of debugging this message should be logged at.
927
 * @param[in] request   The current request.
928
 * @param[in] file    src file the log message was generated in.
929
 * @param[in] line    number the log message was generated on.
930
 * @param[in] str   Subject string we're printing a marker for.
931
 * @param[in] str_len   Subject string length.  Use SIZE_MAX for the
932
 *        length of the string.
933
 * @param[in] marker_idx  The position of the marker relative to the string.
934
 * @param[in] marker_fmt  What the parse error was.
935
 * @param[in] ...   Arguments for fmt string.
936
 */
937
void log_request_marker(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request,
938
      char const *file, int line,
939
      char const *str, size_t str_len,
940
      ssize_t marker_idx, char const *marker_fmt, ...)
941
0
{
942
0
  char const    *ellipses = "";
943
0
  rindent_t   indent;
944
0
  va_list     ap;
945
0
  char      *error;
946
0
  static char const marker_spaces[] = "                                                            "; /* 60 */
947
948
0
  if (str_len == SIZE_MAX) str_len = strlen(str);
949
950
0
  if (marker_idx < 0) marker_idx = (marker_idx == SSIZE_MIN) ? SSIZE_MAX : -marker_idx;
951
952
0
  if ((size_t) marker_idx >= sizeof(marker_spaces)) {
953
0
    size_t offset = (marker_idx - (sizeof(marker_spaces) - 1)) + ((sizeof(marker_spaces) * 3) / 4);
954
955
0
    marker_idx -= offset;
956
957
0
    if (offset >= str_len) {
958
0
      str += str_len;
959
0
      str_len = 0;
960
0
    } else {
961
0
      str += offset;
962
0
      str_len -= offset;
963
0
    }
964
965
0
    ellipses = "... ";
966
0
  }
967
968
  /*
969
   *  Don't want format markers being indented
970
   */
971
0
  indent = request->log.indent;
972
0
  request->log.indent.module = 0;
973
0
  request->log.indent.unlang = 0;
974
975
0
  va_start(ap, marker_fmt);
976
0
  error = fr_vasprintf(request, marker_fmt, ap);
977
0
  va_end(ap);
978
979
0
  log_request(type, lvl, request, file, line, "%s%.*s", ellipses, (int) str_len, str);
980
0
  log_request(type, lvl, request, file, line, "%s%.*s^ %s", ellipses, (int) marker_idx, marker_spaces, error);
981
0
  talloc_free(error);
982
983
0
  request->log.indent = indent;
984
0
}
985
986
void log_request_hex(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request,
987
         char const *file, int line,
988
         uint8_t const *data, size_t data_len)
989
0
{
990
0
  size_t i, j, len;
991
0
  char buffer[(0x10 * 3) + 1];
992
0
  char *p, *end = buffer + sizeof(buffer);
993
994
0
  for (i = 0; i < data_len; i += 0x10) {
995
0
    len = 0x10;
996
0
    if ((i + len) > data_len) len = data_len - i;
997
998
0
    for (p = buffer, j = 0; j < len; j++, p += 3) snprintf(p, end - p, "%02x ", data[i + j]);
999
0
    log_request(type, lvl, request, file, line, "%04x: %s", (unsigned int)i, buffer);
1000
0
  }
1001
0
}
1002
1003
/** Function to provide as the readable callback to the event loop
1004
 *
1005
 * Writes any data read from a file descriptor to the request log,
1006
 * tries very hard not to chop lines in the middle, but will split
1007
 * at 1024 byte boundaries if forced to.
1008
 *
1009
 * @param[in] el  UNUSED
1010
 * @param[in] fd  UNUSED
1011
 * @param[in] flags UNUSED
1012
 * @param[in] uctx  Pointer to a log_fd_event_ctx_t
1013
 */
1014
void log_request_fd_event(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1015
0
{
1016
0
  char      buffer[1024];
1017
0
  log_fd_event_ctx_t  *log_info = uctx;
1018
0
  request_t   *request = log_info->request;
1019
0
  fr_sbuff_t    sbuff;
1020
0
  fr_sbuff_marker_t m_start, m_end;
1021
1022
0
  fr_sbuff_term_t const   line_endings = FR_SBUFF_TERMS(L("\n"), L("\r"));
1023
1024
0
  if (!RDEBUG_ENABLEDX(log_info->lvl)) {
1025
0
    while (read(fd, buffer, sizeof(buffer)) > 0); /* discard all input */
1026
0
    return;
1027
0
  }
1028
1029
0
  fr_sbuff_init_out(&sbuff, buffer, sizeof(buffer));
1030
0
  fr_sbuff_marker(&m_start, &sbuff);
1031
0
  fr_sbuff_marker(&m_end, &sbuff);
1032
1033
0
#ifndef NDEBUG
1034
0
  memset(buffer, 0x42, sizeof(buffer));
1035
0
#endif
1036
1037
0
  for (;;) {
1038
0
    ssize_t   slen;
1039
1040
0
    slen = read(fd, fr_sbuff_current(&m_end), fr_sbuff_remaining(&m_end));
1041
0
    if (slen < 0) {
1042
0
      if (errno == EINTR) continue;
1043
1044
      /*
1045
       *  This can happen if the I/O handler is
1046
       *  manually called to clear out any pending
1047
       *  data, and we're using a nonblocking FD.
1048
       *
1049
       *  This can happen with the exec code if
1050
       *  the EVFILT_PROC event gets passed before
1051
       *  the EVFILT_READ event.
1052
       */
1053
0
      if (errno == EWOULDBLOCK) slen = 0;
1054
0
    }
1055
0
    if ((slen < 0) && (errno == EINTR)) continue;
1056
1057
0
    if (slen > 0) fr_sbuff_advance(&m_end, slen);
1058
1059
0
    while (fr_sbuff_ahead(&m_end) > 0) {
1060
0
      fr_sbuff_adv_until(&sbuff, fr_sbuff_ahead(&m_end), &line_endings, '\0');
1061
1062
      /*
1063
       *  Incomplete line, try and read the rest.
1064
       */
1065
0
      if ((slen > 0) && (fr_sbuff_used(&m_start) > 0) &&
1066
0
          !fr_sbuff_is_terminal(&sbuff, &line_endings)) {
1067
0
        break;
1068
0
      }
1069
1070
0
      log_request(log_info->type, log_info->lvl, log_info->request,
1071
0
            __FILE__, __LINE__,
1072
0
            "%s%s%pV",
1073
0
            log_info->prefix ? log_info->prefix : "",
1074
0
            log_info->prefix ? " - " : "",
1075
0
            fr_box_strvalue_len(fr_sbuff_current(&m_start),
1076
0
              fr_sbuff_behind(&m_start)));
1077
1078
0
      fr_sbuff_advance(&sbuff, 1);  /* Skip the whitespace */
1079
0
      fr_sbuff_set(&m_start, &sbuff);
1080
0
    }
1081
1082
    /*
1083
     *  Error or done
1084
     */
1085
0
    if (slen <= 0) break;
1086
1087
    /*
1088
     *  Clear out the existing data
1089
     */
1090
0
    fr_sbuff_shift(&sbuff, fr_sbuff_used(&m_start), false);
1091
0
  }
1092
0
}
1093
1094
/** Log a fatal error, then exit
1095
 *
1096
 */
1097
void log_fatal(fr_log_t const *log, char const *file, int line, char const *fmt, ...)
1098
0
{
1099
0
  va_list ap;
1100
1101
0
  va_start(ap, fmt);
1102
0
  fr_vlog(log, L_ERR, file, line, fmt, ap);
1103
0
  va_end(ap);
1104
1105
0
  fr_exit_now(EXIT_FAILURE);
1106
0
}
1107
1108
/** Register a logging destination.
1109
 *
1110
 */
1111
static void log_register_dst(char const *name, fr_log_t *log, CONF_SECTION *cs)
1112
0
{
1113
0
  fr_log_track_t *dst;
1114
1115
0
  MEM(dst = talloc_zero(dst_tree, fr_log_track_t));
1116
0
  dst->name = name;
1117
0
  dst->log = log;
1118
0
  dst->cs = cs;
1119
1120
0
  fr_rb_insert(dst_tree, dst);
1121
1122
0
  if (log->dst != L_DST_FILES) return;
1123
1124
0
  fr_assert(log->file != NULL);
1125
1126
0
  fr_rb_insert(filename_tree, dst);
1127
0
}
1128
1129
/** Get a logging destination by name.
1130
 *
1131
 */
1132
fr_log_t *log_dst_by_name(char const *name)
1133
0
{
1134
0
  fr_log_track_t find, *found;
1135
1136
0
  memset(&find, 0, sizeof(find));
1137
0
  find.name = name;
1138
1139
0
  fr_rb_find((void **)&found, dst_tree, &find);
1140
0
  return (found) ? found->log : NULL;
1141
0
}
1142
1143
static int _log_free(fr_log_t *log)
1144
0
{
1145
0
  fr_assert(log->dst == L_DST_FILES);
1146
1147
0
  fclose(log->handle);
1148
0
  return 0;
1149
0
}
1150
1151
static char const *log_destination = NULL;
1152
static bool log_timestamp;
1153
static bool log_timestamp_is_set;
1154
1155
/*
1156
 *  Parse an fr_log_t configuration.
1157
 */
1158
static const conf_parser_t log_config[] = {
1159
  { FR_CONF_POINTER("destination", FR_TYPE_STRING, 0, &log_destination), .dflt = "file" },
1160
#if 0
1161
  /*
1162
   *  @todo - once we allow this, also check that there's only _one_ destination
1163
   *  which uses syslog_facility.
1164
   */
1165
  { FR_CONF_OFFSET("syslog_facility", main_config_t, syslog_facility), .dflt = "daemon",
1166
    .func = cf_table_parse_int,
1167
    .uctx = &(cf_table_parse_ctx_t){
1168
      .table = syslog_facility_table,
1169
      .len = &syslog_facility_table_len
1170
    }
1171
  },
1172
#endif
1173
  { FR_CONF_POINTER_IS_SET("timestamp", FR_TYPE_BOOL, 0, &log_timestamp) },
1174
  { FR_CONF_OFFSET("file", fr_log_t, file), },
1175
  { FR_CONF_OFFSET("colourise", fr_log_t, colourise) },
1176
  { FR_CONF_OFFSET("line_number", fr_log_t, line_number) },
1177
  { FR_CONF_OFFSET("use_utc", fr_log_t, dates_utc) },
1178
  { FR_CONF_OFFSET("print_level", fr_log_t, print_level) },
1179
  { FR_CONF_OFFSET("suppress_secrets", fr_log_t, suppress_secrets) },
1180
  CONF_PARSER_TERMINATOR
1181
};
1182
1183
/** Parse a named logging section.
1184
 *
1185
 *  @todo - we should probably allow for TCP sockets, too.  But then
1186
 *  those can block.  So we then also need a way to buffer outbound
1187
 *  log messages, and discard log messages if the buffer is full.
1188
 *
1189
 *  This should probably be done with a FILE*, and L_DST_FUNC.
1190
 */
1191
int log_parse_section(CONF_SECTION *cs)
1192
0
{
1193
0
  fr_log_track_t *dst;
1194
0
  fr_log_t *log;
1195
0
  char const *name;
1196
1197
0
  name = cf_section_name2(cs);
1198
0
  if (!name) name = "DEFAULT";
1199
1200
0
  fr_rb_find((void **)&dst, dst_tree, &(fr_log_track_t) {
1201
0
      .name = name,
1202
0
    });
1203
0
  if (dst) {
1204
0
    fr_strerror_printf("Cannot add duplicate log destination '%s'", name);
1205
0
    return -1;
1206
0
  }
1207
1208
0
  MEM(log = talloc_zero(dst_tree, fr_log_t));
1209
1210
0
  if (cf_section_rules_push(cs, log_config) < 0) {
1211
0
  error:
1212
0
    talloc_free(log);
1213
0
    return -1;
1214
0
  }
1215
1216
0
  if (cf_section_parse(log, log, cs) < 0) goto error;
1217
1218
0
  log->dst = fr_table_value_by_str(log_destination_table, log_destination, L_DST_NUM_DEST);
1219
0
  switch (log->dst) {
1220
0
  case L_DST_NUM_DEST:
1221
0
    fr_strerror_printf("Unknown log_destination '%s'", log_destination);
1222
0
    talloc_const_free(log_destination);
1223
0
    log_destination = NULL;
1224
0
    goto error;
1225
1226
0
#ifdef HAVE_SYSLOG_H
1227
0
  case L_DST_SYSLOG:
1228
0
    talloc_const_free(log_destination);
1229
0
    log_destination = NULL;
1230
1231
0
    if (fr_log_init_syslog(log) < 0) goto error;
1232
0
    break;
1233
0
#endif
1234
1235
0
  case L_DST_STDOUT:
1236
0
    talloc_const_free(log_destination);
1237
0
    log_destination = NULL;
1238
1239
0
    if (fr_log_init_std(log, L_DST_STDOUT) < 0) goto error;
1240
0
    break;
1241
1242
0
  case L_DST_STDERR:
1243
0
    talloc_const_free(log_destination);
1244
0
    log_destination = NULL;
1245
1246
0
    if (fr_log_init_std(log, L_DST_STDERR) < 0) goto error;
1247
0
    break;
1248
1249
0
  case L_DST_FILES:
1250
0
    talloc_const_free(log_destination);
1251
0
    log_destination = NULL;
1252
1253
0
    if (!log->file) {
1254
0
      fr_strerror_const("Specified \"files\" as a log destination, but no log filename was given!");
1255
0
      goto error;
1256
0
    }
1257
1258
0
    fr_rb_find((void **)&dst, filename_tree, &(fr_log_track_t) {
1259
0
        .log = log,
1260
0
      });
1261
0
    if (dst) {
1262
0
      fr_strerror_printf("The log destination '%s' is already logging to file %s",
1263
0
             dst->name, log->file);
1264
0
      goto error;
1265
0
    }
1266
1267
0
    if (fr_log_init_file(log, log->file) < 0) goto error;
1268
1269
0
    talloc_set_destructor(log, _log_free);
1270
0
    break;
1271
1272
0
  case L_DST_NULL:
1273
0
    break;
1274
1275
0
  default:
1276
0
    talloc_const_free(log_destination);
1277
0
    log_destination = NULL;
1278
1279
0
    fr_assert(0);
1280
0
    break;
1281
0
  }
1282
1283
0
  if (log_timestamp_is_set) {
1284
0
    log->timestamp = log->timestamp ? L_TIMESTAMP_ON : L_TIMESTAMP_OFF;
1285
0
  } else {
1286
0
    log->timestamp = L_TIMESTAMP_AUTO;
1287
0
  }
1288
1289
0
  log_register_dst(name, log, cs);
1290
1291
0
  return 0;
1292
0
}
1293
1294
static fr_cmp_ret_t _log_track_name_cmp(void const *two, void const *one)
1295
0
{
1296
0
  fr_log_track_t const *a = one;
1297
0
  fr_log_track_t const *b = two;
1298
1299
0
  return CMP(strcmp(a->name, b->name), 0);
1300
0
}
1301
1302
static fr_cmp_ret_t _log_track_filename_cmp(void const *two, void const *one)
1303
0
{
1304
0
  fr_log_track_t const *a = one;
1305
0
  fr_log_track_t const *b = two;
1306
1307
0
  fr_assert(a->log);
1308
0
  fr_assert(a->log->dst == L_DST_FILES);
1309
1310
0
  fr_assert(b->log);
1311
0
  fr_assert(b->log->dst == L_DST_FILES);
1312
1313
0
  return CMP(strcmp(a->log->file, b->log->file), 0);
1314
0
}
1315
1316
/** Initialises the server logging functionality, and the underlying libfreeradius log
1317
 *
1318
 * @note Call log free when the server is done to fix any spurious memory leaks.
1319
 *
1320
 * @param[in] log Logging parameters.
1321
 * @param[in] daemonize Changes what we do with stdout/stderr.
1322
 * @return
1323
 *  - 0 on success.
1324
 *  - -1 on failure.
1325
 */
1326
int log_global_init(fr_log_t *log, bool daemonize)
1327
0
{
1328
0
  int ret;
1329
1330
0
  ret = fr_log_init_legacy(log, daemonize);
1331
0
  if (ret < 0) return ret;
1332
1333
0
  if (fr_dict_autoload(log_dict) < 0) {
1334
0
    fr_perror("log_init");
1335
0
    return -1;
1336
0
  }
1337
1338
0
  if (fr_dict_attr_autoload(log_dict_attr) < 0) {
1339
0
    fr_perror("log_init");
1340
0
    return -1;
1341
0
  }
1342
1343
0
  dst_tree = fr_rb_inline_alloc(NULL, fr_log_track_t, name_node,
1344
0
              _log_track_name_cmp, NULL);
1345
0
  if (!dst_tree) {
1346
0
    fr_perror("log_init");
1347
0
    return -1;
1348
0
  }
1349
1350
0
  filename_tree = fr_rb_inline_alloc(NULL, fr_log_track_t, filename_node,
1351
0
             _log_track_filename_cmp, NULL);
1352
0
  if (!filename_tree) {
1353
0
    fr_perror("log_init");
1354
0
    return -1;
1355
0
  }
1356
1357
0
  log_register_dst("default", log, NULL);
1358
1359
0
  return ret;
1360
0
}
1361
1362
void log_global_free(void)
1363
0
{
1364
0
  fr_dict_autofree(log_dict);
1365
0
  TALLOC_FREE(src_tree);
1366
0
  TALLOC_FREE(dst_tree);
1367
  TALLOC_FREE(filename_tree);
1368
0
}