The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 421ff39cb7022cac8029aae645eb5bec9663d6bb $
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/base.h>
28 #include <freeradius-devel/server/exfile.h>
29 
30 #include <freeradius-devel/util/debug.h>
31 #include <freeradius-devel/util/misc.h>
32 #include <freeradius-devel/util/perm.h>
33 #include <freeradius-devel/util/syserror.h>
34 
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 
38 typedef 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 
48 struct 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  */
69 static inline void exfile_trigger_exec(exfile_t *ef, exfile_entry_t *entry, char const *name_suffix)
70 {
71  char name[128];
72  fr_pair_t *vp;
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 
90  fr_pair_list_prepend_by_da_len(NULL, vp, &args, da, entry->filename,
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 
119 static 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  */
147 exfile_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  */
195 void 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  */
214 static 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  */
268 static 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;
272  uint32_t hash;
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 
380 reopen:
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 
386 try_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  */
505 int 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  */
525 static 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  */
561 int 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:89
#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:1997
fr_dict_t const * fr_dict_internal(void)
Definition: dict_util.c:4204
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:2925
ino_t st_ino
inode number
Definition: exfile.c:43
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.
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
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 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:200
uint32_t fr_hash_string(char const *p)
Definition: hash.c:859
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition: interpret.c:1768
talloc_free(reap)
unsigned int uint32_t
Definition: merged_model.c:33
unsigned int mode_t
Definition: merged_model.c:21
long long int off_t
Definition: merged_model.c:22
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:2316
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_oct(char out[static 5], mode_t mode)
Definition: perm.c:51
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
#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_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:333
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
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition: talloc.h:212
#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:588
#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:280
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:375
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223