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