Coverage Report

Created: 2026-11-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/strerror.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 to allow libraries to provide errors to their callers
18
 *
19
 * @file src/lib/util/strerror.c
20
 *
21
 * @copyright 2017-2020 The FreeRADIUS server project
22
 * @copyright 2017-2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23
 */
24
RCSID("$Id: deadb14c8a21d252a969260fcd3702d0fe12c506 $")
25
26
#include <freeradius-devel/util/dlist.h>
27
#include <freeradius-devel/util/strerror.h>
28
#include <freeradius-devel/util/atexit.h>
29
30
31
120
#define FR_STRERROR_BUFSIZE (2048)
32
33
typedef struct fr_log_entry_s fr_log_entry_t;
34
struct fr_log_entry_s {
35
  fr_dlist_t  list;
36
  char const  *msg;   //!< Log message.
37
38
  char const  *subject; //!< Subject for error markers.
39
  size_t    offset;   //!< Where to place the msg marker relative to the subject.
40
41
  char const  *file;    //!< File where the error was created.
42
  int   line;   //!< Line where the error occurred.
43
};
44
45
/** Holds data used by the logging stack
46
 *
47
 * pool_a and pool_b allow #fr_strerror and #fr_strerror_pop during
48
 * a call to #fr_strerror_printf or #fr_strerror_printf_push.
49
 */
50
typedef struct {
51
  TALLOC_CTX  *pool_a;  //!< Pool to avoid memory allocations.
52
  TALLOC_CTX  *pool_b;  //!< Pool to avoid memory allocations.
53
  TALLOC_CTX  *pool;    //!< Current pool in use.
54
55
  fr_dlist_head_t entries;
56
} fr_log_buffer_t;
57
58
static _Thread_local fr_log_buffer_t *fr_strerror_buffer;
59
static _Thread_local bool logging_stop; //!< Due to ordering issues we may get errors being
60
          ///< logged from within other thread local destructors
61
          ///< which cause a crash on exit if the logging buffer
62
          ///< has already been freed.
63
64
/*
65
 *  Explicitly cleanup the memory allocated to the error buffer,
66
 *  just in case valgrind complains about it.
67
 */
68
static int _fr_logging_free(void *arg)
69
60
{
70
  /*
71
   *  Free arg instead of thread local storage
72
   *  as address sanitizer does a better job
73
   *  of tracking and doesn't report a leak.
74
   */
75
60
  if (talloc_free(arg) < 0) return -1;
76
60
  fr_strerror_buffer = NULL;
77
78
60
  logging_stop = true;
79
80
60
  return 0;
81
60
}
82
83
/** Initialise thread local storage
84
 *
85
 * @return fr_buffer_t containing log messages.
86
 */
87
static fr_log_buffer_t *fr_strerror_init(void)
88
1.03M
{
89
1.03M
  fr_log_buffer_t *buffer;
90
91
1.03M
  if (unlikely(logging_stop)) return NULL; /* No more logging */
92
93
1.03M
  buffer = fr_strerror_buffer;
94
1.03M
  if (!buffer) {
95
60
    buffer = talloc(NULL, fr_log_buffer_t);  /* One byte extra for status */
96
60
    if (!buffer) {
97
0
    oom:
98
0
      fr_perror("Failed allocating memory for libradius error buffer");
99
0
      return NULL;
100
0
    }
101
60
    buffer->pool_a = talloc_pool(buffer, FR_STRERROR_BUFSIZE);
102
60
    if (!buffer->pool_a) goto oom;
103
104
60
    buffer->pool_b = talloc_pool(buffer, FR_STRERROR_BUFSIZE);
105
60
    if (!buffer->pool_b) goto oom;
106
107
60
    buffer->pool = buffer->pool_a;
108
109
60
    fr_atexit_thread_local(fr_strerror_buffer, _fr_logging_free, buffer);
110
111
60
    fr_dlist_talloc_init(&buffer->entries, fr_log_entry_t, list);
112
60
  }
113
114
1.03M
  return buffer;
115
1.03M
}
116
117
/*
118
 *  If last pool was pool_a, allocate from pool_b
119
 *  ...and vice versa.  This prevents the pools
120
 *  from leaking due to non-contiguous allocations
121
 *  when we're using fr_strerror as an argument
122
 *  for another message.
123
 */
124
static inline CC_HINT(always_inline) TALLOC_CTX *pool_alt(fr_log_buffer_t *buffer)
125
1.03M
{
126
1.03M
  if (buffer->pool == buffer->pool_a) {
127
518k
    buffer->pool = buffer->pool_b;
128
518k
    return buffer->pool;
129
518k
  }
130
518k
  return buffer->pool = buffer->pool_a;
131
1.03M
}
132
133
static inline CC_HINT(always_inline) void pool_alt_free_children(fr_log_buffer_t *buffer)
134
1.01M
{
135
1.01M
  if (buffer->pool == buffer->pool_a) {
136
509k
    talloc_free_children(buffer->pool_b);
137
509k
    return;
138
509k
  }
139
509k
  talloc_free_children(buffer->pool_a);
140
509k
}
141
142
/** Create an entry in the thread local logging stack, clearing all other entries
143
 *
144
 * @note Can't be inlined.
145
 *
146
 * @hidecallergraph
147
 */
148
static fr_log_entry_t *strerror_vprintf(char const *file, int line, char const *fmt, va_list ap)
149
380k
{
150
380k
  va_list   ap_p;
151
380k
  fr_log_entry_t  *entry;
152
380k
  fr_log_buffer_t *buffer;
153
154
380k
  buffer = fr_strerror_init();
155
380k
  if (unlikely(buffer == NULL)) return NULL;
156
157
  /*
158
   *  Clear any existing log messages
159
   */
160
380k
  if (!fmt) {
161
0
    fr_strerror_clear();
162
0
    return NULL;
163
0
  }
164
165
380k
  entry = talloc(pool_alt(buffer), fr_log_entry_t);
166
380k
  if (unlikely(!entry)) {
167
0
  oom:
168
0
    fr_perror("Failed allocating memory for libradius error buffer");
169
0
    return NULL;
170
0
  }
171
172
380k
  va_copy(ap_p, ap);
173
380k
  entry->msg = fr_vasprintf(entry, fmt, ap_p);
174
380k
  va_end(ap_p);
175
380k
  if (unlikely(!entry->msg)) goto oom;
176
380k
  entry->subject = NULL;
177
380k
  entry->offset = 0;
178
380k
  entry->file = file;
179
380k
  entry->line = line;
180
181
380k
  pool_alt_free_children(buffer);
182
380k
  fr_dlist_clear(&buffer->entries);
183
380k
  fr_dlist_insert_tail(&buffer->entries, entry);
184
185
380k
  return entry;
186
380k
}
187
188
/** Add a message to an existing stack of messages
189
 *
190
 * @param[in] file  the error occurred in.
191
 * @param[in] line  the error occurred on.
192
 * @param[in] buffer  The log buffer to allocate memory from.
193
 * @param[in] fmt printf style format string.
194
 * @param[in] ap  Arguments for the error string.
195
 *
196
 * @note Can't be inline.
197
 *
198
 * @hidecallergraph
199
 */
200
static fr_log_entry_t *strerror_vprintf_push(char const *file, int line,
201
               fr_log_buffer_t *buffer, char const *fmt, va_list ap)
202
16.3k
{
203
16.3k
  va_list   ap_p;
204
16.3k
  fr_log_entry_t  *entry;
205
206
16.3k
  if (!fmt) return NULL;
207
208
16.3k
  entry = talloc(pool_alt(buffer), fr_log_entry_t);
209
16.3k
  if (unlikely(!entry)) {
210
0
  oom:
211
0
    fr_perror("Failed allocating memory for libradius error buffer");
212
0
    return NULL;
213
0
  }
214
215
16.3k
  va_copy(ap_p, ap);
216
16.3k
  entry->msg = fr_vasprintf(entry, fmt, ap_p);
217
16.3k
  va_end(ap_p);
218
16.3k
  if (unlikely(!entry->msg)) goto oom;
219
220
  /*
221
   *  Address pathological case where we could leak memory
222
   *  if only a combination of fr_strerror and
223
   *  fr_strerror_printf_push are used.
224
   *
225
   *  Must run AFTER fr_vasprintf, otherwise pointer args
226
   *  referencing the old pool (e.g. from fr_strerror())
227
   *  would be freed before being consumed.
228
   */
229
16.3k
  if (!fr_dlist_num_elements(&buffer->entries)) pool_alt_free_children(buffer);
230
231
16.3k
  entry->subject = NULL;
232
16.3k
  entry->offset = 0;
233
16.3k
  entry->file = file;
234
16.3k
  entry->line = line;
235
236
16.3k
  return entry;
237
16.3k
}
238
239
/** Log to thread local error buffer
240
 *
241
 * @param[in] file  the error occurred in.
242
 * @param[in] line  the error occurred on.
243
 * @param[in] fmt printf style format string.
244
 *      If NULL clears any existing messages.
245
 * @param[in] ap  Arguments for the format string.
246
 *
247
 * @hidecallergraph
248
 */
249
void _fr_strerror_vprintf(char const *file, int line, char const *fmt, va_list ap)
250
380k
{
251
380k
  va_list   our_ap;
252
253
380k
  va_copy(our_ap, ap);
254
380k
  strerror_vprintf(file, line, fmt, our_ap);
255
380k
  va_end(our_ap);
256
380k
}
257
258
/** Add a message to an existing stack of messages at the tail
259
 *
260
 * @param[in] file  the error occurred in.
261
 * @param[in] line  the error occurred on.
262
 * @param[in] fmt printf style format string.
263
 * @param[in] ap  Arguments for the format string.
264
 *
265
 * @hidecallergraph
266
 */
267
void _fr_strerror_vprintf_push(char const *file, int line, char const *fmt, va_list ap)
268
16.3k
{
269
16.3k
  va_list     our_ap;
270
16.3k
  fr_log_buffer_t   *buffer;
271
16.3k
  fr_log_entry_t    *entry;
272
273
16.3k
  buffer = fr_strerror_init();
274
16.3k
  if (unlikely(buffer == NULL)) return;
275
276
16.3k
  va_copy(our_ap, ap);
277
16.3k
  entry = strerror_vprintf_push(file, line, buffer, fmt, our_ap);
278
16.3k
  va_end(our_ap);
279
280
16.3k
  if (unlikely(!entry)) return;
281
282
16.3k
  fr_dlist_insert_tail(&buffer->entries, entry);
283
16.3k
}
284
285
/** Add a message to an existing stack of messages at the head
286
 *
287
 * @param[in] file  the error occurred in.
288
 * @param[in] line  the error occurred on.
289
 * @param[in] fmt printf style format string.
290
 * @param[in] ap  Arguments for the format string.
291
 *
292
 * @hidecallergraph
293
 */
294
void _fr_strerror_vprintf_push_head(char const *file, int line, char const *fmt, va_list ap)
295
0
{
296
0
  va_list     our_ap;
297
0
  fr_log_buffer_t   *buffer;
298
0
  fr_log_entry_t    *entry;
299
300
0
  buffer = fr_strerror_init();
301
0
  if (unlikely(buffer == NULL)) return;
302
303
0
  va_copy(our_ap, ap);
304
0
  entry = strerror_vprintf_push(file, line, buffer, fmt, our_ap);
305
0
  va_end(our_ap);
306
307
0
  if (unlikely(!entry)) return;
308
309
0
  fr_dlist_insert_head(&buffer->entries, entry);
310
0
}
311
312
/** Add an error marker to an existing stack of messages
313
 *
314
 * @param[in] file  the error occurred in.
315
 * @param[in] line  the error occurred on.
316
 * @param[in] subject to mark up.
317
 * @param[in] offset  Positive offset to show where the error
318
 *      should be positioned.
319
 * @param[in] fmt Error string.
320
 * @param[in] ap  Arguments for the error string.
321
 *
322
 * @hidecallergraph
323
 */
324
void _fr_strerror_marker_vprintf(char const *file, int line,
325
         char const *subject, size_t offset, char const *fmt, va_list ap)
326
0
{
327
0
  va_list   our_ap;
328
0
  fr_log_entry_t  *entry;
329
330
0
  va_copy(our_ap, ap);
331
0
  entry = strerror_vprintf(file, line, fmt, our_ap);
332
0
  va_end(our_ap);
333
334
0
  if (unlikely(!entry)) return;
335
336
0
  entry->subject = talloc_strdup(entry, subject);
337
0
  entry->offset = offset;
338
0
}
339
340
/** Add an error marker to an existing stack of messages at the tail
341
 *
342
 * @param[in] file  the error occurred in.
343
 * @param[in] line  the error occurred on.
344
 * @param[in] subject to mark up.
345
 * @param[in] offset  Positive offset to show where the error
346
 *      should be positioned.
347
 * @param[in] fmt Error string.
348
 * @param[in] ap  Arguments for the error string.
349
 *
350
 * @hidecallergraph
351
 */
352
void _fr_strerror_marker_vprintf_push(char const *file, int line,
353
              char const *subject, size_t offset, char const *fmt, va_list ap)
354
0
{
355
0
  va_list     our_ap;
356
0
  fr_log_entry_t    *entry;
357
0
  fr_log_buffer_t   *buffer;
358
359
0
  buffer = fr_strerror_init();
360
0
  if (unlikely(buffer == NULL)) return;
361
362
0
  va_copy(our_ap, ap);
363
0
  entry = strerror_vprintf_push(file, line, buffer, fmt, our_ap);
364
0
  va_end(our_ap);
365
366
0
  if (unlikely(!entry)) return;
367
368
0
  entry->subject = talloc_strdup(entry, subject);
369
0
  entry->offset = offset;
370
371
0
  fr_dlist_insert_tail(&buffer->entries, entry);
372
0
}
373
374
/** Add an error marker to an existing stack of messages at the head
375
 *
376
 * @param[in] file  the error occurred in.
377
 * @param[in] line  the error occurred on.
378
 * @param[in] subject to mark up.
379
 * @param[in] offset  Positive offset to show where the error
380
 *      should be positioned.
381
 * @param[in] fmt Error string.
382
 * @param[in] ap  Arguments for the error string.
383
 *
384
 * @hidecallergraph
385
 */
386
void _fr_strerror_marker_vprintf_push_head(char const *file, int line,
387
             char const *subject, size_t offset, char const *fmt, va_list ap)
388
0
{
389
0
  va_list     our_ap;
390
0
  fr_log_entry_t    *entry;
391
0
  fr_log_buffer_t   *buffer;
392
393
0
  buffer = fr_strerror_init();
394
0
  if (unlikely(buffer == NULL)) return;
395
396
0
  va_copy(our_ap, ap);
397
0
  entry = strerror_vprintf_push(file, line, buffer, fmt, our_ap);
398
0
  va_end(our_ap);
399
400
0
  if (unlikely(!entry)) return;
401
402
0
  entry->subject = talloc_strdup(entry, subject);
403
0
  entry->offset = offset;
404
405
0
  fr_dlist_insert_head(&buffer->entries, entry);
406
0
}
407
408
/** Create an entry in the thread local logging stack using a const string, clearing all other entries
409
 *
410
 * @hidecallergraph
411
 */
412
static inline CC_HINT(always_inline) fr_log_entry_t *strerror_const(char const *file, int line, char const *msg)
413
638k
{
414
638k
  fr_log_entry_t  *entry;
415
638k
  fr_log_buffer_t *buffer;
416
417
638k
  buffer = fr_strerror_init();
418
638k
  if (unlikely(buffer == NULL)) return NULL;
419
420
638k
  entry = talloc(pool_alt(buffer), fr_log_entry_t);
421
638k
  if (unlikely(!entry)) {
422
0
    fr_perror("Failed allocating memory for libradius error buffer");
423
0
    return NULL;
424
0
  }
425
  /*
426
   *  For some reason this is significantly
427
   *  more efficient than a compound literal
428
   *  even though in the majority of cases
429
   *  compound literals and individual field
430
   *  assignments result in the same byte
431
   *  code.
432
   */
433
638k
  entry->file = file;
434
638k
  entry->line = line;
435
638k
  entry->msg = msg;
436
638k
  entry->subject = NULL;
437
638k
  entry->offset = 0;
438
439
638k
  pool_alt_free_children(buffer);
440
638k
  fr_dlist_clear(&buffer->entries);
441
638k
  fr_dlist_insert_tail(&buffer->entries, entry);
442
443
638k
  return entry;
444
638k
}
445
446
/** Log to thread local error buffer
447
 *
448
 * @param[in] file  the error occurred in.
449
 * @param[in] line  the error occurred on.
450
 * @param[in] msg To add to error stack. Must have a
451
 *      lifetime equal to that of the program.
452
 * @hidecallergraph
453
 */
454
void _fr_strerror_const(char const *file, int line, char const *msg)
455
638k
{
456
638k
  (void)strerror_const(file, line, msg);
457
638k
}
458
459
/** Add a message to an existing stack of messages
460
 *
461
 * @param[in] file  the error occurred in.
462
 * @param[in] line  the error occurred on.
463
 * @param[in] buffer  to add the message to.
464
 * @param[in] msg To add to error stack. Must have a
465
 *      lifetime equal to that of the program.
466
 *
467
 * @hidecallergraph
468
 */
469
static inline CC_HINT(always_inline) fr_log_entry_t *strerror_const_push(char const *file, int line,
470
                   fr_log_buffer_t *buffer, char const *msg)
471
2.87k
{
472
2.87k
  fr_log_entry_t  *entry;
473
474
  /*
475
   *  Address pathological case where we could leak memory
476
   *  if only a combination of fr_strerror and
477
   *  fr_strerror_printf_push are used.
478
   */
479
2.87k
  if (!fr_dlist_num_elements(&buffer->entries)) talloc_free_children(buffer->pool);
480
481
2.87k
  entry = talloc(pool_alt(buffer), fr_log_entry_t);
482
2.87k
  if (unlikely(!entry)) {
483
0
    fr_perror("Failed allocating memory for libradius error buffer");
484
0
    return NULL;
485
0
  }
486
  /*
487
   *  For some reason this is significantly
488
   *  more efficient than a compound literal
489
   *  even though in the majority of cases
490
   *  compound literals and individual field
491
   *  assignments result in the same byte
492
   *  code.
493
   */
494
2.87k
  entry->msg = msg;
495
2.87k
  entry->subject = NULL;
496
2.87k
  entry->offset = 0;
497
2.87k
  entry->file = file;
498
2.87k
  entry->line = line;
499
500
2.87k
  return entry;
501
2.87k
}
502
503
/** Add a message to an existing stack of messages at the tail
504
 *
505
 * @param[in] file  the error occurred in.
506
 * @param[in] line  the error occurred on.
507
 * @param[in] msg To add to error stack. Must have a
508
 *      lifetime equal to that of the program.
509
 *
510
 * @hidecallergraph
511
 */
512
void _fr_strerror_const_push(char const *file, int line, char const *msg)
513
2.87k
{
514
2.87k
  fr_log_buffer_t   *buffer;
515
2.87k
  fr_log_entry_t    *entry;
516
517
2.87k
  buffer = fr_strerror_init();
518
2.87k
  if (unlikely(buffer == NULL)) return;
519
520
2.87k
  entry = strerror_const_push(file, line, buffer, msg);
521
2.87k
  if (unlikely(!entry)) return;
522
523
2.87k
  fr_dlist_insert_tail(&buffer->entries, entry);
524
2.87k
}
525
526
/** Add a message to an existing stack of messages at the head
527
 *
528
 * @param[in] file  the error occurred in.
529
 * @param[in] line  the error occurred on.
530
 * @param[in] msg To add to error stack. Must have a
531
 *      lifetime equal to that of the program.
532
 *
533
 * @hidecallergraph
534
 */
535
void _fr_strerror_const_push_head(char const *file, int line, char const *msg)
536
0
{
537
0
  fr_log_buffer_t   *buffer;
538
0
  fr_log_entry_t    *entry;
539
540
0
  buffer = fr_strerror_init();
541
0
  if (unlikely(buffer == NULL)) return;
542
543
0
  entry = strerror_const_push(file, line, buffer, msg);
544
0
  if (unlikely(!entry)) return;
545
546
0
  fr_dlist_insert_head(&buffer->entries, entry);
547
0
}
548
549
/** Get the last library error
550
 *
551
 * Will only return the last library error once, after which it will return a zero length string.
552
 * If there are additional messages on the log stack they will be discarded.
553
 *
554
 * @return library error or zero length string.
555
 *
556
 * @hidecallergraph
557
 */
558
char const *fr_strerror(void)
559
342
{
560
342
  fr_log_buffer_t   *buffer;
561
342
  fr_log_entry_t    *entry;
562
563
342
  buffer = fr_strerror_buffer;
564
342
  if (unlikely(buffer == NULL)) return "";
565
566
342
  entry = fr_dlist_tail(&buffer->entries);
567
342
  if (!entry) return "";
568
569
  /*
570
   *  Memory gets freed on next call to
571
   *  fr_strerror_printf or fr_strerror_printf_push.
572
   */
573
338
  fr_dlist_clear(&buffer->entries);
574
575
338
  return entry->msg;
576
342
}
577
578
/** Clears all pending messages from the talloc pools
579
 *
580
 */
581
void fr_strerror_clear(void)
582
343k
{
583
343k
  fr_log_buffer_t   *buffer = fr_strerror_buffer;
584
585
343k
  if (unlikely(buffer == NULL)) return;
586
587
343k
  fr_dlist_clear(&buffer->entries);
588
343k
  talloc_free_children(buffer->pool_a);
589
343k
  talloc_free_children(buffer->pool_b);
590
343k
}
591
592
/** Get the last library error marker
593
 *
594
 * @param[out] subject  The subject string the error relates to.
595
 * @param[out] offset Where to place the marker.
596
 * @return
597
 *  - NULL if there are no pending errors.
598
 *  - The error message if there was an error.
599
 *
600
 * @hidecallergraph
601
 */
602
char const *fr_strerror_marker(char const **subject, size_t *offset)
603
0
{
604
0
  fr_log_buffer_t   *buffer;
605
0
  fr_log_entry_t    *entry;
606
607
0
  buffer = fr_strerror_buffer;
608
0
  if (unlikely(buffer == NULL)) return "";
609
610
0
  entry = fr_dlist_head(&buffer->entries);
611
0
  if (!entry) return "";
612
613
  /*
614
   *  Memory gets freed on next call to
615
   *  fr_strerror_printf or fr_strerror_printf_push.
616
   */
617
0
  fr_dlist_clear(&buffer->entries);
618
619
0
  *subject = entry->subject;
620
0
  *offset = entry->offset;
621
622
0
  return entry->msg;
623
0
}
624
625
/** Get the last library error
626
 *
627
 * @return library error or zero length string.
628
 *
629
 * @hidecallergraph
630
 */
631
char const *fr_strerror_peek(void)
632
0
{
633
0
  fr_log_buffer_t   *buffer;
634
0
  fr_log_entry_t    *entry;
635
636
0
  buffer = fr_strerror_buffer;
637
0
  if (unlikely(buffer == NULL)) return "";
638
639
0
  entry = fr_dlist_tail(&buffer->entries);
640
0
  if (!entry) return "";
641
642
0
  return entry->msg;
643
0
}
644
645
/** Get the last library error marker
646
 *
647
 * @param[out] subject  The subject string the error relates to.
648
 * @param[out] offset Where to place the marker.
649
 * @return
650
 *  - NULL if there are no pending errors.
651
 *  - The error message if there was an error.
652
 *
653
 * @hidecallergraph
654
 */
655
char const *fr_strerror_marker_peek(char const **subject, size_t *offset)
656
0
{
657
0
  fr_log_buffer_t   *buffer;
658
0
  fr_log_entry_t    *entry;
659
660
0
  buffer = fr_strerror_buffer;
661
0
  if (unlikely(buffer == NULL)) return "";
662
663
0
  entry = fr_dlist_head(&buffer->entries);
664
0
  if (!entry) return "";
665
666
0
  *subject = entry->subject;
667
0
  *offset = entry->offset;
668
669
0
  return entry->msg;
670
0
}
671
672
/** Pop the last library error
673
 *
674
 * Return the first message added to the error stack using #fr_strerror_printf
675
 * or #fr_strerror_printf_push.
676
 *
677
 * @note  fr_strerror() will return NULL if no messages are pending.
678
 *
679
 * @return
680
 *  - A library error.
681
 *  - NULL if no errors are pending.
682
 *
683
 * @hidecallergraph
684
 */
685
char const *fr_strerror_pop(void)
686
760
{
687
760
  fr_log_buffer_t   *buffer;
688
760
  fr_log_entry_t    *entry;
689
690
760
  buffer = fr_strerror_buffer;
691
760
  if (unlikely(buffer == NULL)) return NULL;
692
693
760
  entry = fr_dlist_head(&buffer->entries);
694
760
  if (!entry) return NULL;
695
696
380
  fr_dlist_remove(&buffer->entries, entry);
697
698
380
  return entry->msg;
699
760
}
700
701
/** Pop the last library error with marker information
702
 *
703
 * Return the first message added to the error stack using #fr_strerror_printf
704
 * or #fr_strerror_printf_push.
705
 *
706
 * @return
707
 *  - A library error.
708
 *  - NULL if no errors are pending.
709
 *
710
 * @hidecallergraph
711
 */
712
char const *fr_strerror_marker_pop(char const **subject, size_t *offset)
713
0
{
714
0
  fr_log_buffer_t   *buffer;
715
0
  fr_log_entry_t    *entry;
716
717
0
  buffer = fr_strerror_buffer;
718
0
  if (unlikely(buffer == NULL)) return NULL;
719
720
0
  entry = fr_dlist_head(&buffer->entries);
721
0
  if (!entry) return NULL;
722
723
0
  fr_dlist_remove(&buffer->entries, entry);
724
725
0
  *subject = entry->subject;
726
0
  *offset = entry->offset;
727
728
0
  return entry->msg;
729
0
}
730
731
/** Print the current error to stderr with a prefix
732
 *
733
 * Used by utility functions lacking their own logging infrastructure
734
 *
735
 * @hidecallergraph
736
 */
737
void fr_perror(char const *fmt, ...)
738
0
{
739
0
  char const  *error;
740
0
  char const  *subject;
741
0
  size_t    offset;
742
0
  char    *prefix = NULL;
743
0
  va_list   ap;
744
745
0
  error = fr_strerror_marker_pop(&subject, &offset);
746
0
  if (fmt) {
747
0
    va_start(ap, fmt);
748
0
    prefix = talloc_vasprintf(NULL, fmt, ap);
749
0
    va_end(ap);
750
751
0
    if (error) {
752
0
      fprintf(stderr, "%s: %s\n", prefix, error);
753
0
    } else {
754
0
      fprintf(stderr, "%s\n", prefix);
755
0
      talloc_free(prefix);
756
0
      return;
757
0
    }
758
0
    talloc_free(prefix);
759
0
  } else {
760
0
    if (!error) return;
761
0
    fprintf(stderr, "%s\n", error);
762
0
  }
763
764
0
  while ((error = fr_strerror_marker_pop(&subject, &offset))) {
765
0
    if (error && (error[0] != '\0')) {
766
0
      fprintf(stderr, "%s\n", error);
767
0
    }
768
0
  }
769
770
0
}
771
772
/** Print the stack of string buffers to a thread local buffer
773
 *
774
 * Used by utility functions lacking their own logging infrastructure
775
 *
776
 * @hidecallergraph
777
 *
778
 * @param[in] line_sep  to insert between the log lines.
779
 * @param[in] fmt to prefix all log messages with.
780
 * @return
781
 *  - A thread local string buffer containing the concatenated messages.
782
 */
783
char const *fr_perror_to_str(char const *line_sep, char const *fmt, ...)
784
0
{
785
0
  char const  *error;
786
0
  char const  *subject;
787
0
  size_t    offset;
788
0
  char    *prefix;
789
0
  va_list   ap;
790
0
  fr_sbuff_t  *agg;
791
792
0
  FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
793
794
0
  va_start(ap, fmt);
795
0
  prefix = talloc_vasprintf(NULL, fmt, ap);
796
0
  if (unlikely(!prefix)) return NULL;
797
0
  va_end(ap);
798
799
0
  error = fr_strerror_marker_pop(&subject, &offset);
800
0
  if (error) {
801
0
    if (fr_sbuff_in_sprintf(agg, "%s: %s%s", prefix, error, line_sep) < 0) return NULL;
802
0
  } else {
803
0
    talloc_free(prefix);
804
0
    return NULL;
805
0
  }
806
807
0
  while ((error = fr_strerror_marker_pop(&subject, &offset))) {
808
0
    if (error && (error[0] != '\0')) {
809
0
      if (fr_sbuff_in_sprintf(agg, "%s: %s%s", prefix, error, line_sep) < 0) return NULL;
810
0
    }
811
0
  }
812
0
  talloc_free(prefix);
813
814
0
  return fr_sbuff_start(agg);
815
0
}
816
817
/** Print the stack of string buffers to a thread local buffer
818
 *
819
 * So that the whole stack of messages ends up on one line.
820
 *
821
 * @hidecallergraph
822
 *
823
 * @param[in] prefix  fixed string at the sart of the line.
824
 * @param[in] line_sep  to insert between the log messages.
825
 * @return
826
 *  - A thread local string buffer containing the concatenated messages.
827
 */
828
char const *fr_strerror_concat(char const *prefix, char const *line_sep)
829
0
{
830
0
  char const  *error;
831
0
  fr_sbuff_t  *agg;
832
833
0
  FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
834
835
0
  if (prefix) (void) fr_sbuff_in_strcpy(agg, prefix);
836
837
0
  while ((error = fr_strerror_pop()) != NULL) {
838
0
    (void) fr_sbuff_in_strcpy(agg, error);
839
0
    if (*fr_strerror_peek()) {
840
0
      (void) fr_sbuff_in_strcpy(agg, line_sep);
841
0
    }
842
0
  }
843
844
0
  return fr_sbuff_start(agg);
845
846
0
}
847