The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
exfile.c
Go to the documentation of this file.
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: 542d466b51843c4340d5d7a6e9c0e4e762fab12b $
19 *
20 * @file exfile.c
21 * @brief Allow multiple threads to write to the same set of files.
22 *
23 * @author Alan DeKok (aland@freeradius.org)
24 * @copyright 2014 The FreeRADIUS server project
25 */
26#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
27#include <freeradius-devel/server/exfile.h>
28#include <freeradius-devel/server/trigger.h>
29
30#include <freeradius-devel/util/debug.h>
31#include <freeradius-devel/util/file.h>
32#include <freeradius-devel/util/misc.h>
33#include <freeradius-devel/util/perm.h>
34#include <freeradius-devel/util/syserror.h>
35
36#include <fcntl.h>
37
38typedef struct {
39 int fd; //!< File descriptor associated with an entry.
40 uint32_t hash; //!< Hash for cheap comparison.
41 fr_time_t last_used; //!< Last time the entry was used.
42 dev_t st_dev; //!< device inode
43 ino_t st_ino; //!< inode number
44 char *filename; //!< Filename.
46
47
48struct exfile_s {
49 uint32_t max_entries; //!< How many file descriptors we keep track of.
50 fr_time_delta_t max_idle; //!< Maximum idle time for a descriptor.
54 bool locking;
55 CONF_SECTION *conf; //!< Conf section to search for triggers.
56 char const *trigger_prefix; //!< Trigger path in the global trigger section.
57 fr_pair_list_t trigger_args; //!< Arguments to pass to trigger.
58};
59
60#define MAX_TRY_LOCK 4 //!< How many times we attempt to acquire a lock
61 //!< before giving up.
62
63/** Send an exfile trigger.
64 *
65 * @param[in] ef to send trigger for.
66 * @param[in] entry for the file that the event occurred on.
67 * @param[in] name_suffix trigger name suffix.
68 */
69static inline void exfile_trigger_exec(exfile_t *ef, exfile_entry_t *entry, char const *name_suffix)
70{
71 char name[128];
74 fr_dict_attr_t const *da;
75
77 fr_assert(ef != NULL);
78 fr_assert(name_suffix != NULL);
79
80 if (!ef->trigger_prefix) return;
81
83 if (!da) {
84 ERROR("Incomplete internal dictionary: Missing definition for \"Exfile-Name\"");
85 return;
86 }
87
88 (void) fr_pair_list_copy(NULL, &args, &ef->trigger_args);
89
91 talloc_array_length(entry->filename) - 1, false);
92
93 snprintf(name, sizeof(name), "%s.%s", ef->trigger_prefix, name_suffix);
95
97}
98
99
101{
102 if (entry->fd >= 0) close(entry->fd);
103
104 entry->hash = 0;
105 entry->fd = -1;
106
107 /*
108 * Issue close trigger *after* we've closed the fd
109 */
110 exfile_trigger_exec(ef, entry, "close");
111
112 /*
113 * Trigger still needs access to filename to populate Exfile-Name
114 */
115 TALLOC_FREE(entry->filename);
116}
117
118
119static int _exfile_free(exfile_t *ef)
120{
121 uint32_t i;
122
123 pthread_mutex_lock(&ef->mutex);
124
125 for (i = 0; i < ef->max_entries; i++) {
126 if (!ef->entries[i].filename) continue;
127
128 exfile_cleanup_entry(ef, &ef->entries[i]);
129 }
130
131 pthread_mutex_unlock(&ef->mutex);
132 pthread_mutex_destroy(&ef->mutex);
133
134 return 0;
135}
136
137/** Initialize a way for multiple threads to log to one or more files.
138 *
139 * @param ctx The talloc context
140 * @param max_entries Max file descriptors to cache, and manage locks for.
141 * @param max_idle Maximum time a file descriptor can be idle before it's closed.
142 * @param locking whether or not to lock the files.
143 * @return
144 * - new context.
145 * - NULL on error.
146 */
147exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t max_entries, fr_time_delta_t max_idle, bool locking)
148{
149 exfile_t *ef;
150
151 ef = talloc_zero(NULL, exfile_t);
152 if (!ef) return NULL;
154
155 talloc_link_ctx(ctx, ef);
156
157 ef->max_entries = max_entries;
158 ef->max_idle = max_idle;
159 ef->locking = locking;
160
161 /*
162 * If we're not locking the files, just return the
163 * handle. Each call to exfile_open() will just open a
164 * new file descriptor.
165 */
166 if (!ef->locking) return ef;
167
168 ef->entries = talloc_zero_array(ef, exfile_entry_t, max_entries);
169 if (!ef->entries) {
170 talloc_free(ef);
171 return NULL;
172 }
173
174 if (pthread_mutex_init(&ef->mutex, NULL) != 0) {
175 talloc_free(ef);
176 return NULL;
177 }
178
179 talloc_set_destructor(ef, _exfile_free);
180
181 return ef;
182}
183
184/** Enable triggers for an exfiles handle
185 *
186 * @param[in] ef to enable triggers for.
187 * @param[in] conf section to search for triggers in.
188 * @param[in] trigger_prefix prefix to prepend to all trigger names. Usually a path
189 * to the module's trigger configuration .e.g.
190 * @verbatim modules.<name>.file @endverbatim
191 * @verbatim <trigger name> @endverbatim is appended to form the complete path.
192 * @param[in] trigger_args to make available in any triggers executed by the exfile api.
193 * Exfile-File is automatically added to this list.
194 */
195void exfile_enable_triggers(exfile_t *ef, CONF_SECTION *conf, char const *trigger_prefix, fr_pair_list_t *trigger_args)
196{
198 MEM(ef->trigger_prefix = trigger_prefix ? talloc_typed_strdup(ef, trigger_prefix) : "");
199
201
202 ef->conf = conf;
203
204 if (!trigger_args) return;
205
206 (void) fr_pair_list_copy(ef, &ef->trigger_args, trigger_args);
207}
208
209
210/*
211 * Try to open the file. It it doesn't exist, try to
212 * create it's parent directories.
213 */
214static int exfile_open_mkdir(exfile_t *ef, char const *filename, mode_t permissions)
215{
216 int fd;
217
218 fd = open(filename, O_RDWR | O_CREAT, permissions);
219 if (fd < 0) {
220 mode_t dirperm;
221 char *p, *dir;
222
223 /*
224 * Maybe the directory doesn't exist. Try to
225 * create it.
226 */
227 dir = talloc_typed_strdup(ef, filename);
228 if (!dir) return -1;
229 p = strrchr(dir, FR_DIR_SEP);
230 if (!p) {
231 fr_strerror_printf("No '/' in '%s'", filename);
232 talloc_free(dir);
233 return -1;
234 }
235 *p = '\0';
236
237 /*
238 * Ensure that the 'x' bit is set, so that we can
239 * read the directory.
240 */
241 dirperm = permissions;
242 if ((dirperm & 0600) != 0) dirperm |= 0100;
243 if ((dirperm & 0060) != 0) dirperm |= 0010;
244 if ((dirperm & 0006) != 0) dirperm |= 0001;
245
246 if (fr_mkdir(NULL, dir, -1, dirperm, NULL, NULL) < 0) {
247 fr_strerror_printf("Failed to create directory %s: %s", dir, fr_syserror(errno));
248 talloc_free(dir);
249 return -1;
250 }
251 talloc_free(dir);
252
253 fd = open(filename, O_RDWR | O_CREAT, permissions);
254 if (fd < 0) {
255 fr_strerror_printf("Failed to open file %s: %s", filename, fr_syserror(errno));
256 return -1;
257 }
258 }
259
260 return fd;
261}
262
263/*
264 * Experience appears to show that coverity models of functions can't use incoming parameters
265 * to influence whether and which __coverity*__() functions are called. We therefore create a
266 * separate function for the locking case which we *can* model.
267 */
268static int exfile_open_lock(exfile_t *ef, char const *filename, mode_t permissions, off_t *offset)
269{
270 int i, tries, unused = -1, found = -1, oldest = -1;
271 bool do_cleanup = false;
273 fr_time_t now;
274 struct stat st;
275 off_t real_offset;
276
277 /*
278 * It's faster to do hash comparisons of a string than
279 * full string comparisons.
280 */
281 hash = fr_hash_string(filename);
282 now = fr_time();
283 unused = -1;
284
285 pthread_mutex_lock(&ef->mutex);
286
287 if (fr_time_gt(now, fr_time_add(ef->last_cleaned, fr_time_delta_from_sec(1)))) do_cleanup = true;
288
289 /*
290 * Find the matching entry, or an unused one.
291 *
292 * Also track which entry is the oldest, in case there
293 * are no unused entries.
294 */
295 for (i = 0; i < (int) ef->max_entries; i++) {
296 if (!ef->entries[i].filename) {
297 if (unused < 0) unused = i;
298 continue;
299 }
300
301 if ((oldest < 0) ||
302 (fr_time_lt(ef->entries[i].last_used, ef->entries[oldest].last_used))) {
303 oldest = i;
304 }
305
306 /*
307 * Hash comparisons are fast. String comparisons are slow.
308 *
309 * But we still need to do string comparisons if
310 * the hash matches, because 1/2^16 filenames
311 * will result in a hash collision. And that's
312 * enough filenames in a long-running server to
313 * ensure that it happens.
314 */
315 if ((found < 0) &&
316 (ef->entries[i].hash == hash) &&
317 (strcmp(ef->entries[i].filename, filename) == 0)) {
318 found = i;
319
320 /*
321 * If we're not cleaning up, stop now.
322 */
323 if (!do_cleanup) break;
324
325 /*
326 * If we are cleaning up, then clean up
327 * entries OTHER than the one we found,
328 * do so now.
329 */
330 } else if (do_cleanup) {
331 if (fr_time_gteq(fr_time_add(ef->entries[i].last_used, ef->max_idle), now)) continue;
332
333 exfile_cleanup_entry(ef, &ef->entries[i]);
334 }
335 }
336
337 if (do_cleanup) ef->last_cleaned = now;
338
339 /*
340 * We found an existing entry, return that.
341 */
342 if (found >= 0) {
343 i = found;
344
345 /*
346 * Stat the *filename*, not the file we opened.
347 * If that's not the file we opened, then go back
348 * and re-open the file.
349 */
350 if (stat(ef->entries[i].filename, &st) < 0) {
351 goto reopen;
352 }
353
354 if ((st.st_dev != ef->entries[i].st_dev) ||
355 (st.st_ino != ef->entries[i].st_ino)) {
356 close(ef->entries[i].fd);
357 goto reopen;
358 }
359
360 goto try_lock;
361 }
362
363 /*
364 * There are no unused entries, free the oldest one.
365 */
366 if (unused < 0) {
367 exfile_cleanup_entry(ef, &ef->entries[oldest]);
368 unused = oldest;
369 }
370
371 /*
372 * Create a new entry.
373 */
374 i = unused;
375
376 ef->entries[i].hash = hash;
377 ef->entries[i].filename = talloc_typed_strdup(ef->entries, filename);
378 ef->entries[i].fd = -1;
379
380reopen:
381 ef->entries[i].fd = exfile_open_mkdir(ef, filename, permissions);
382 if (ef->entries[i].fd < 0) goto error;
383
384 exfile_trigger_exec(ef, &ef->entries[i], "open");
385
386try_lock:
387 /*
388 * Lock from the start of the file.
389 */
390 if (lseek(ef->entries[i].fd, 0, SEEK_SET) < 0) {
391 fr_strerror_printf("Failed to seek in file %s: %s", filename, fr_syserror(errno));
392
393 error:
394 exfile_cleanup_entry(ef, &ef->entries[i]);
395 pthread_mutex_unlock(&(ef->mutex));
396 return -1;
397 }
398
399 /*
400 * Try to lock it. If we can't lock it, it's because
401 * some reader has re-named the file to "foo.work" and
402 * locked it. So, we close the current file, re-open it,
403 * and try again/
404 */
405 for (tries = 0; tries < MAX_TRY_LOCK; tries++) {
406 if (rad_lockfd_nonblock(ef->entries[i].fd, 0) >= 0) break;
407
408 if (errno != EAGAIN) {
409 fr_strerror_printf("Failed to lock file %s: %s", filename, fr_syserror(errno));
410 goto error;
411 }
412
413 close(ef->entries[i].fd);
414 ef->entries[i].fd = open(filename, O_RDWR | O_CREAT, permissions);
415 if (ef->entries[i].fd < 0) {
416 fr_strerror_printf("Failed to open file %s: %s", filename, fr_syserror(errno));
417 goto error;
418 }
419 }
420
421 if (tries >= MAX_TRY_LOCK) {
422 fr_strerror_printf("Failed to lock file %s: too many tries", filename);
423 goto error;
424 }
425
426 /*
427 * Maybe someone deleted the file while we were waiting
428 * for the lock. If so, re-open it.
429 */
430 if (fstat(ef->entries[i].fd, &st) < 0) {
431 fr_strerror_printf("Failed to stat file %s: %s", filename, fr_syserror(errno));
432 goto reopen;
433 }
434
435 if (st.st_nlink == 0) {
436 close(ef->entries[i].fd);
437 goto reopen;
438 }
439
440 /*
441 * Remember which device and inode this file is
442 * for.
443 */
444 ef->entries[i].st_dev = st.st_dev;
445 ef->entries[i].st_ino = st.st_ino;
446
447 /*
448 * Sometimes the file permissions are changed externally.
449 * just be sure to update the permission if necessary.
450 */
451 if ((st.st_mode & ~S_IFMT) != permissions) {
452 char str_need[10], oct_need[5];
453 char str_have[10], oct_have[5];
454
455 fr_perm_mode_to_oct(oct_need, permissions);
456 fr_perm_mode_to_str(str_need, permissions);
457
458 fr_perm_mode_to_oct(oct_have, st.st_mode & ~S_IFMT);
459 fr_perm_mode_to_str(str_have, st.st_mode & ~S_IFMT);
460
461 WARN("File %s permissions are %s (%s) not %s (%s))", filename,
462 oct_have, str_have, oct_need, str_need);
463
464 if (((st.st_mode | permissions) != st.st_mode) &&
465 (fchmod(ef->entries[i].fd, (st.st_mode & ~S_IFMT) | permissions) < 0)) {
466 fr_perm_mode_to_oct(oct_need, (st.st_mode & ~S_IFMT) | permissions);
467 fr_perm_mode_to_str(str_need, (st.st_mode & ~S_IFMT) | permissions);
468
469 WARN("Failed resetting file %s permissions to %s (%s): %s",
470 filename, oct_need, str_need, fr_syserror(errno));
471 }
472 }
473
474 /*
475 * Seek to the end of the file before returning the FD to
476 * the caller.
477 */
478 real_offset = lseek(ef->entries[i].fd, 0, SEEK_END);
479 if (offset) *offset = real_offset;
480
481 /*
482 * Return holding the mutex for the entry.
483 */
484 ef->entries[i].last_used = now;
485
486 exfile_trigger_exec(ef, &ef->entries[i], "reserve");
487
488 /* coverity[missing_unlock] */
489 return ef->entries[i].fd;
490}
491
492/** Open a new log file, or maybe an existing one.
493 *
494 * When multithreaded, the FD is locked via a mutex. This way we're
495 * sure that no other thread is writing to the file.
496 *
497 * @param ef The logfile context returned from exfile_init().
498 * @param filename the file to open.
499 * @param permissions to use.
500 * @param offset Optional pointer to store offset in when seeking the end of file.
501 * @return
502 * - FD used to write to the file.
503 * - -1 on failure.
504 */
505int exfile_open(exfile_t *ef, char const *filename, mode_t permissions, off_t *offset)
506{
507 if (!ef || !filename) return -1;
508
509 if (!ef->locking) {
510 int found = exfile_open_mkdir(ef, filename, permissions);
511 off_t real_offset;
512
513 if (found < 0) return -1;
514 real_offset = lseek(found, 0, SEEK_END);
515 if (offset) *offset = real_offset;
516 return found;
517 }
518
519 return exfile_open_lock(ef, filename, permissions, offset);
520}
521
522/*
523 * Same split for exfile_close().
524 */
525static int exfile_close_lock(exfile_t *ef, int fd)
526{
527 uint32_t i;
528
529 /*
530 * Unlock the bytes that we had previously locked.
531 */
532 for (i = 0; i < ef->max_entries; i++) {
533 if (ef->entries[i].fd != fd) continue;
534
535 (void) lseek(ef->entries[i].fd, 0, SEEK_SET);
536 (void) rad_unlockfd(ef->entries[i].fd, 0);
537 pthread_mutex_unlock(&(ef->mutex));
538
539 exfile_trigger_exec(ef, &ef->entries[i], "release");
540 return 0;
541 }
542
543 pthread_mutex_unlock(&(ef->mutex));
544
545 fr_strerror_const("Attempt to unlock file which is not tracked");
546 return -1;
547}
548
549/** Close the log file. Really just return it to the pool.
550 *
551 * When multithreaded, the FD is locked via a mutex. This way we're sure that no other thread is
552 * writing to the file. This function will unlock the mutex, so that other threads can write to
553 * the file.
554 *
555 * @param ef The logfile context returned from #exfile_init.
556 * @param fd the FD to close (i.e. return to the pool).
557 * @return
558 * - 0 on success.
559 * - -1 on failure.
560 */
561int exfile_close(exfile_t *ef, int fd)
562{
563 if (!ef->locking) {
564 /*
565 * No locking: just close the file.
566 */
567 close(fd);
568 return 0;
569 }
570 return exfile_close_lock(ef, fd);
571}
va_list args
Definition acutest.h:770
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2402
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4612
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3330
ino_t st_ino
inode number
Definition exfile.c:43
exfile_t * exfile_init(TALLOC_CTX *ctx, uint32_t max_entries, fr_time_delta_t max_idle, bool locking)
Initialize a way for multiple threads to log to one or more files.
Definition exfile.c:147
static void exfile_cleanup_entry(exfile_t *ef, exfile_entry_t *entry)
Definition exfile.c:100
pthread_mutex_t mutex
Definition exfile.c:52
#define MAX_TRY_LOCK
How many times we attempt to acquire a lock before giving up.
Definition exfile.c:60
uint32_t max_entries
How many file descriptors we keep track of.
Definition exfile.c:49
static int exfile_open_lock(exfile_t *ef, char const *filename, mode_t permissions, off_t *offset)
Definition exfile.c:268
bool locking
Definition exfile.c:54
static int exfile_open_mkdir(exfile_t *ef, char const *filename, mode_t permissions)
Definition exfile.c:214
static void exfile_trigger_exec(exfile_t *ef, exfile_entry_t *entry, char const *name_suffix)
Send an exfile trigger.
Definition exfile.c:69
char const * trigger_prefix
Trigger path in the global trigger section.
Definition exfile.c:56
dev_t st_dev
device inode
Definition exfile.c:42
fr_time_t last_cleaned
Definition exfile.c:51
uint32_t hash
Hash for cheap comparison.
Definition exfile.c:40
char * filename
Filename.
Definition exfile.c:44
void exfile_enable_triggers(exfile_t *ef, CONF_SECTION *conf, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Enable triggers for an exfiles handle.
Definition exfile.c:195
exfile_entry_t * entries
Definition exfile.c:53
static int _exfile_free(exfile_t *ef)
Definition exfile.c:119
fr_time_t last_used
Last time the entry was used.
Definition exfile.c:41
fr_pair_list_t trigger_args
Arguments to pass to trigger.
Definition exfile.c:57
CONF_SECTION * conf
Conf section to search for triggers.
Definition exfile.c:55
fr_time_delta_t max_idle
Maximum idle time for a descriptor.
Definition exfile.c:50
int exfile_open(exfile_t *ef, char const *filename, mode_t permissions, off_t *offset)
Open a new log file, or maybe an existing one.
Definition exfile.c:505
int fd
File descriptor associated with an entry.
Definition exfile.c:39
static int exfile_close_lock(exfile_t *ef, int fd)
Definition exfile.c:525
int exfile_close(exfile_t *ef, int fd)
Close the log file.
Definition exfile.c:561
Definition exfile.c:38
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
Definition file.c:219
uint32_t fr_hash_string(char const *p)
Definition hash.c:865
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:1845
talloc_free(reap)
unsigned int uint32_t
unsigned int mode_t
long long int off_t
int rad_unlockfd(int fd, int lock_len)
Definition misc.c:141
int rad_lockfd_nonblock(int fd, int lock_len)
Definition misc.c:129
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2321
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
char const * fr_perm_mode_to_str(char out[static 10], mode_t mode)
Convert mode_t into humanly readable permissions flags.
Definition perm.c:36
char const * fr_perm_mode_to_oct(char out[static 5], mode_t mode)
Definition perm.c:51
#define fr_assert(_expr)
Definition rad_assert.h:38
#define WARN(fmt,...)
Definition radclient.h:47
static rs_t * conf
Definition radsniff.c:53
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
static char const * name
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition talloc.c:167
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:441
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
#define fr_time_gteq(_a, _b)
Definition time.h:238
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_gt(_a, _b)
Definition time.h:237
#define fr_time_lt(_a, _b)
Definition time.h:239
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
int trigger_exec(unlang_interpret_t *intp, CONF_SECTION const *cs, char const *name, bool rate_limit, fr_pair_list_t *args)
Execute a trigger - call an executable to process an event.
Definition trigger.c:228
close(uq->fd)
#define fr_pair_list_prepend_by_da_len(_ctx, _vp, _list, _attr, _val, _len, _tainted)
Prepend a pair to a list, assigning its value.
Definition pair.h:376
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223