The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
proto_detail_file.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: 29172408413a11ca38822d0c84833e284007f060 $
19  * @file proto_detail_file.c
20  * @brief Detail handler for files
21  *
22  * @copyright 2017 The FreeRADIUS server project.
23  * @copyright 2017 Alan DeKok (aland@deployingradius.com)
24  */
25 
26 #include <freeradius-devel/io/application.h>
27 #include <freeradius-devel/io/listen.h>
28 #include <freeradius-devel/io/schedule.h>
29 
30 #include <freeradius-devel/server/main_config.h>
31 #include <freeradius-devel/server/protocol.h>
32 
33 #include <freeradius-devel/util/syserror.h>
34 #include <freeradius-devel/util/misc.h>
35 
36 #include "proto_detail.h"
37 
38 #include <netdb.h>
39 
40 #include <fcntl.h>
41 #include <sys/stat.h>
42 
43 #ifdef HAVE_GLOB_H
44 #include <glob.h>
45 #else
46 #error proto_detail_file requires <glob.h>
47 #endif
48 
49 DIAG_OFF(unused-macros)
50 #if 0
51 /*
52  * When we want detailed debugging here, without detailed server
53  * debugging.
54  */
55 #define MPRINT DEBUG
56 #else
57 #define MPRINT DEBUG3
58 #endif
59 DIAG_ON(unused-macros)
60 
61 /*
62  * For talloc names, ".name = detail_file", and dl.c prepends "proto_", and appends "_t".
63  */
65 
66 /*
67  * @todo - this should really now be a different data structure
68  */
70 
71 static void work_init(proto_detail_file_thread_t *thread, bool triggered_by_delete);
72 static void mod_vnode_delete(fr_event_list_t *el, int fd, UNUSED int fflags, void *ctx);
73 
75  { FR_CONF_OFFSET_FLAGS("filename", CONF_FLAG_REQUIRED, proto_detail_file_t, filename ) },
76 
77  { FR_CONF_OFFSET("filename_work", proto_detail_file_t, filename_work ) },
78 
79  { FR_CONF_OFFSET("poll_interval", proto_detail_file_t, poll_interval), .dflt = "5" },
80 
81  { FR_CONF_OFFSET("immediate", proto_detail_file_t, immediate) },
82 
84 };
85 
86 
87 /*
88  * All of the decoding is done by proto_detail and proto_detail_work
89  */
90 static int mod_decode(void const *instance, request_t *request, uint8_t *const data, size_t data_len)
91 {
93 
94  return inst->parent->work_io->decode(inst->parent->work_io_instance, request, data, data_len);
95 }
96 
97 #if 0
98 static ssize_t mod_write(UNUSED fr_listen_t *li, UNUSED void *packet_ctx, UNUSED fr_time_t request_time,
99  UNUSED uint8_t *buffer, UNUSED size_t buffer_len, UNUSED size_t written)
100 {
101 #if 1
102  fr_assert(0);
103 
104  return -1;
105 #else
106  proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
107 
108  return thread->listen->app_io->write(thread->listen, packet_ctx, request_time, buffer, buffer_len, written);
109 #endif
110 }
111 #endif
112 
113 static void mod_vnode_extend(fr_listen_t *li, UNUSED uint32_t fflags)
114 {
115  proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
116 
117  bool has_worker = false;
118 
119  pthread_mutex_lock(&thread->worker_mutex);
120  has_worker = (thread->num_workers != 0);
121  pthread_mutex_unlock(&thread->worker_mutex);
122 
123  if (has_worker) return;
124 
125  if (thread->ev) fr_event_timer_delete(&thread->ev);
126 
127  work_init(thread, false);
128 }
129 
130 /** Open a detail listener
131  *
132  */
133 static int mod_open(fr_listen_t *li)
134 {
136  proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
137 
138  if (inst->poll_interval == 0) {
139  int oflag;
140 
141 #ifdef O_EVTONLY
142  oflag = O_EVTONLY;
143 #else
144  oflag = O_RDONLY;
145 #endif
146  li->fd = thread->fd = open(inst->directory, oflag);
147  if (thread->fd < 0) {
148  cf_log_err(inst->cs, "Failed opening %s: %s", inst->directory, fr_syserror(errno));
149  return -1;
150  }
151  } else {
152  li->fd = thread->fd = -1;
153  li->non_socket_listener = true;
154  li->needs_full_setup = true;
155  }
156  thread->inst = inst;
157  thread->name = talloc_typed_asprintf(thread, "detail_file which will read files matching %s", inst->filename);
158  thread->vnode_fd = -1;
159  pthread_mutex_init(&thread->worker_mutex, NULL);
160 
161  li->no_write_callback = true;
162 
163  return 0;
164 }
165 
166 /*
167  * The "detail.work" file doesn't exist. Let's see if we can rename one.
168  */
170 {
171  proto_detail_file_t const *inst = thread->inst;
172  unsigned int i;
173  int found;
174  time_t chtime;
175  char const *filename;
176  glob_t files;
177  struct stat st;
178 
179  DEBUG3("proto_detail (%s): polling for detail files in %s",
180  thread->name, inst->directory);
181 
182  memset(&files, 0, sizeof(files));
183  if (glob(inst->filename, 0, NULL, &files) != 0) {
184  noop:
185  DEBUG3("proto_detail (%s): no matching files for %s",
186  thread->name, inst->filename);
187  globfree(&files);
188  return -1;
189  }
190 
191  /*
192  * Loop over the glob'd files, looking for the
193  * oldest one.
194  */
195  chtime = 0;
196  found = -1;
197  for (i = 0; i < files.gl_pathc; i++) {
198  if (stat(files.gl_pathv[i], &st) < 0) continue;
199 
200  if ((i == 0) || (st.st_ctime < chtime)) {
201  chtime = st.st_ctime;
202  found = i;
203  }
204  }
205 
206  /*
207  * No matching files, reset the timer and continue.
208  */
209  if (found < 0) goto noop;
210 
211  /*
212  * Rename detail to detail.work
213  */
214  filename = files.gl_pathv[found];
215 
216  DEBUG("proto_detail (%s): Renaming %s -> %s", thread->name, filename, inst->filename_work);
217  if (rename(filename, inst->filename_work) < 0) {
218  ERROR("detail (%s): Failed renaming %s to %s: %s",
219  thread->name, filename, inst->filename_work, fr_syserror(errno));
220  goto noop;
221  }
222 
223  globfree(&files); /* Shouldn't be using anything in files now */
224 
225  /*
226  * The file should now exist, return the open'd FD.
227  */
228  return open(inst->filename_work, inst->mode);
229 }
230 
231 /*
232  * Start polling again after a timeout.
233  */
235 {
236  proto_detail_file_thread_t *thread = talloc_get_type_abort(uctx, proto_detail_file_thread_t);
237 
238  work_init(thread, false);
239 }
240 
241 /*
242  * The "detail.work" file exists, and is open in the 'fd'.
243  */
244 static int work_exists(proto_detail_file_thread_t *thread, int fd)
245 {
246  proto_detail_file_t const *inst = thread->inst;
247  bool opened = false;
249  fr_listen_t *li = NULL;
250  struct stat st;
251 
253 
254  DEBUG3("proto_detail (%s): Trying to lock %s", thread->name, inst->filename_work);
255 
256  /*
257  * "detail.work" exists, try to lock it.
258  */
259  if (rad_lockfd_nonblock(fd, 0) < 0) {
260  fr_time_delta_t delay;
261 
262  DEBUG3("proto_detail (%s): Failed locking %s: %s",
263  thread->name, inst->filename_work, fr_syserror(errno));
264 
265  close(fd);
266 
267  delay = thread->lock_interval;
268 
269  /*
270  * Set the next interval, and ensure that we
271  * don't do massive busy-polling.
272  */
275  fr_time_delta_wrap(2)));
278  }
279 
280  DEBUG3("proto_detail (%s): Waiting %.6fs for lock on file %s",
281  thread->name, fr_time_delta_unwrap(delay) / (double)NSEC, inst->filename_work);
282 
283  if (fr_event_timer_in(thread, thread->el, &thread->ev,
284  delay, work_retry_timer, thread) < 0) {
285  ERROR("Failed inserting retry timer for %s", inst->filename_work);
286  }
287  return 0;
288  }
289 
290  DEBUG3("proto_detail (%s): Obtained lock and starting to process file %s",
291  thread->name, inst->filename_work);
292 
293  /*
294  * Ignore empty files.
295  */
296  if (fstat(fd, &st) < 0) {
297  ERROR("Failed opening %s: %s", inst->filename_work,
298  fr_syserror(errno));
299  unlink(inst->filename_work);
300  close(fd);
301  return 1;
302  }
303 
304  if (!st.st_size) {
305  DEBUG3("proto_detail (%s): %s file is empty, ignoring it.",
306  thread->name, inst->filename_work);
307  unlink(inst->filename_work);
308  close(fd);
309  return 1;
310  }
311 
312  /*
313  * This listener is allocated in a thread-specific
314  * context, so it doesn't need a destructor,
315  */
316  MEM(li = talloc_zero(NULL, fr_listen_t));
317 
318  /*
319  * Create a new listener, and insert it into the
320  * scheduler. Shamelessly copied from proto_detail.c
321  * mod_open(), with changes.
322  *
323  * This listener is parented from the worker. So that
324  * when the worker goes away, so does the listener.
325  */
326  li->app_io = inst->parent->work_io;
327 
328  li->app = inst->parent->self;
329  li->app_instance = inst->parent;
330  li->server_cs = inst->parent->server_cs;
331 
332  /*
333  * The worker may be in a different thread, so avoid
334  * talloc threading issues by using a NULL TALLOC_CTX.
335  */
336  MEM(li->thread_instance = work = talloc_zero(li, proto_detail_work_thread_t));
337 
338  li->app_io_instance = inst->parent->work_io_instance;
339  work->inst = li->app_io_instance;
340  work->file_parent = thread;
341  work->ev = NULL;
342 
343  li->fd = work->fd = dup(fd);
344  if (work->fd < 0) {
345  DEBUG("proto_detail (%s): Failed opening %s: %s",
346  thread->name, inst->filename_work, fr_syserror(errno));
347 
348  close(fd);
349  talloc_free(li);
350  return -1;
351  }
352 
353  /*
354  * Don't do anything until the file has been deleted.
355  *
356  * @todo - ensure that proto_detail_work is done the file...
357  * maybe by creating a new instance?
358  */
359  if (fr_event_filter_insert(thread, NULL, thread->el, fd, FR_EVENT_FILTER_VNODE,
360  &funcs, NULL, thread) < 0) {
361  PERROR("Failed adding work socket to event loop");
362  close(fd);
363  talloc_free(li);
364  return -1;
365  }
366 
367  /*
368  * Remember this for later.
369  */
370  thread->vnode_fd = fd;
371 
372  /*
373  * For us, this is the worker listener.
374  * For the worker, this is it's own parent
375  */
376  thread->listen = li;
377 
378  work->filename_work = talloc_strdup(work, inst->filename_work);
379 
380  /*
381  * Set configurable parameters for message ring buffer.
382  */
383  li->default_message_size = inst->parent->max_packet_size;
384  li->num_messages = inst->parent->num_messages;
385 
386  pthread_mutex_lock(&thread->worker_mutex);
387  thread->num_workers++;
388  pthread_mutex_unlock(&thread->worker_mutex);
389 
390  /*
391  * Open the detail.work file.
392  */
393  if (li->app_io->open(li) < 0) {
394  ERROR("Failed opening %s", li->app_io->common.name);
395  goto error;
396  }
397  opened = true;
398 
399  fr_assert(li->app_io->get_name);
400  li->name = li->app_io->get_name(li);
401 
402  if (!fr_schedule_listen_add(inst->parent->sc, li)) {
403  error:
404  if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) {
405  PERROR("Failed removing DELETE callback when opening work file");
406  }
407  close(thread->vnode_fd);
408  thread->vnode_fd = -1;
409 
410  if (opened) {
411  (void) li->app_io->close(li);
412  thread->listen = NULL;
413  li = NULL;
414  }
415 
416  talloc_free(li);
417  return -1;
418  }
419 
420  /*
421  * Tell the worker to clean itself up.
422  */
423  work->listen = li;
424 
425  return 0;
426 }
427 
428 
429 static void mod_vnode_delete(fr_event_list_t *el, int fd, UNUSED int fflags, void *ctx)
430 {
431  proto_detail_file_thread_t *thread = talloc_get_type_abort(ctx, proto_detail_file_thread_t);
432  proto_detail_file_t const *inst = thread->inst;
433 
434  DEBUG("proto_detail (%s): Deleted %s", thread->name, inst->filename_work);
435 
436  /*
437  * Silently ignore notifications from the directory. We
438  * didn't ask for them, but libkqueue delivers them to
439  * us.
440  */
441  if (fd == thread->fd) return;
442 
443  if (fd != thread->vnode_fd) {
444  ERROR("Received DELETE for FD %d, when we were expecting one on FD %d - ignoring it",
445  fd, thread->vnode_fd);
446  return;
447  }
448 
450  PERROR("Failed removing DELETE callback after deletion");
451  }
452  close(fd);
453  thread->vnode_fd = -1;
454 
455  /*
456  * Re-initialize the state machine.
457  *
458  * Note that a "delete" may be the result of an atomic
459  * "move", which both deletes the old file, and creates
460  * the new one.
461  */
462  work_init(thread, true);
463 }
464 
465 
466 /** Start processing a new work file
467  *
468  * @param[in] thread the thread instance.
469  * @param[in] triggered_by_delete true if this was triggered by a vnode_delete.
470  * When a new file is moved over a workfile
471  * vnode delete can serve as an indication
472  * that new data is available.
473  * It can also mean however, that the file
474  * has just been deleted, so we shouldn't
475  * treat this failure to open the new file
476  * as a fatal error.
477  */
478 static void work_init(proto_detail_file_thread_t *thread, bool triggered_by_delete)
479 {
480  proto_detail_file_t const *inst = thread->inst;
481  int fd, rcode;
482  bool has_worker;
483 
484  pthread_mutex_lock(&thread->worker_mutex);
485  has_worker = (thread->num_workers != 0);
486  pthread_mutex_unlock(&thread->worker_mutex);
487 
488  /*
489  * The worker is still processing the file, poll until
490  * it's done.
491  */
492  if (has_worker) {
493  DEBUG3("proto_detail (%s): worker %s is still alive, waiting for it to finish.",
494  thread->name, inst->filename_work);
495  goto delay;
496  }
497 
498  fr_assert(thread->vnode_fd < 0);
499 
500  /*
501  * See if there is a "detail.work" file. If not, try to
502  * rename an existing file to "detail.work".
503  */
504  DEBUG3("Trying to open %s", inst->filename_work);
505  fd = open(inst->filename_work, inst->mode);
506 
507  /*
508  * If the work file didn't exist, try to rename detail* ->
509  * detail.work, and return the newly opened file.
510  */
511  if (fd < 0) {
512  if (errno != ENOENT) {
513  DEBUG("proto_detail (%s): Failed opening %s: %s",
514  thread->name, inst->filename_work,
515  fr_syserror(errno));
516  goto delay;
517  }
518 
519 retry:
520  fd = work_rename(thread);
521  }
522 
523  /*
524  * The work file still doesn't exist. Go set up timers,
525  * or wait for an event which signals us that something
526  * in the directory changed.
527  */
528  if (fd < 0) {
529  /*
530  * Wait for the directory to change before
531  * looking for another "detail" file.
532  */
533  if (!inst->poll_interval) return;
534 
535  /*
536  * File really was deleted, and didn't have
537  * anything moved over the top.
538  */
539  if (inst->immediate && triggered_by_delete) return;
540 
541 delay:
542  /*
543  * If we're processing one file and exiting, then the input file must exist.
544  */
545  if (inst->immediate && inst->parent->exit_when_done) {
546  ERROR("Input file does not exist");
547  fr_exit(EXIT_FAILURE);
548  }
549 
550  /*
551  * Check every N seconds.
552  */
553  DEBUG3("Waiting %d.000000s for new files in %s", inst->poll_interval, thread->name);
554 
555  if (fr_event_timer_in(thread, thread->el, &thread->ev,
556  fr_time_delta_from_sec(inst->poll_interval), work_retry_timer, thread) < 0) {
557  ERROR("Failed inserting poll timer for %s", inst->filename_work);
558  }
559  return;
560  }
561 
562  thread->lock_interval = fr_time_delta_from_msec(100);
563 
564  /*
565  * It exists, go process it!
566  *
567  * We will get back to the main loop when the
568  * "detail.work" file is deleted.
569  */
570  rcode = work_exists(thread, fd);
571  if (rcode < 0) goto delay;
572 
573  /*
574  * The file was empty, so we try to get another one.
575  */
576  if (rcode == 1) goto retry;
577 
578  /*
579  * Otherwise the child is successfully processing the
580  * file.
581  */
582 }
583 
584 
585 /** Set the event list for a new IO instance
586  *
587  * @param[in] li the listener
588  * @param[in] el the event list
589  * @param[in] nr context from the network side
590  */
592 {
594  proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
595 
596  thread->el = el;
597 
598  if (inst->immediate) {
599  work_init(thread, false);
600  return;
601  }
602 
603  /*
604  * Delay for a bit, before reading the detail files.
605  * This gives the server time to call
606  * rad_suid_down_permanent(), and for /proc/PID to
607  * therefore change permissions, so that libkqueue can
608  * read it.
609  */
610  if (fr_event_timer_in(thread, thread->el, &thread->ev,
611  fr_time_delta_from_sec(1), work_retry_timer, thread) < 0) {
612  ERROR("Failed inserting poll timer for %s", thread->filename_work);
613  }
614 }
615 
616 
617 static char const *mod_name(fr_listen_t *li)
618 {
619  proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
620 
621  return thread->name;
622 }
623 
624 
625 static int mod_bootstrap(module_inst_ctx_t const *mctx)
626 {
627  proto_detail_file_t *inst = talloc_get_type_abort(mctx->inst->data, proto_detail_file_t);
628  CONF_SECTION *conf = mctx->inst->conf;
629  dl_module_inst_t const *dl_inst;
630  char *p;
631 
632 #ifdef __linux__
633  /*
634  * The kqueue API takes an FD, but inotify requires a filename.
635  * libkqueue uses /proc/PID/fd/# to look up the FD -> filename mapping.
636  *
637  * However, if you start the server as "root", and then swap to "radiusd",
638  * /proc/PID will be owned by "root" for security reasons. The only way
639  * to make /proc/PID owned by "radiusd" is to set the DUMPABLE flag.
640  *
641  * Instead of making the poor sysadmin figure this out,
642  * we check for this situation, and give them a
643  * descriptive message telling them what to do.
644  */
646  main_config->uid_is_set &&
647  main_config->server_uid != 0) {
648  cf_log_err(conf, "Cannot start detail file reader due to Linux limitations.");
649  cf_log_err(conf, "Please set 'allow_core_dumps = true' in the main configuration file.");
650  return -1;
651  }
652 #endif
653 
654  /*
655  * Find the dl_module_inst_t holding our instance data
656  * so we can find out what the parent of our instance
657  * was.
658  */
659  dl_inst = dl_module_instance_by_data(mctx->inst->data);
660  fr_assert(dl_inst);
661 
662 #ifndef __linux__
663  /*
664  * Linux inotify works. So we allow poll_interval==0
665  */
666  FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, >=, 1);
667 #endif
668  FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, <=, 3600);
669 
670  inst->parent = talloc_get_type_abort(dl_inst->parent->data, proto_detail_t);
671  inst->cs = conf;
672 
673  inst->directory = p = talloc_strdup(inst, inst->filename);
674 
675  p = strrchr(p, '/');
676  if (!p) {
677  cf_log_err(conf, "Filename must contain '/'");
678  return -1;
679  }
680 
681  *p = '\0';
682 
683  if (!inst->filename_work) {
684  inst->filename_work = talloc_typed_asprintf(inst, "%s/detail.work", inst->directory);
685  }
686 
687  /*
688  * We need this for the lock.
689  */
690  inst->mode = O_RDWR;
691 
692  if (inst->parent->exit_when_done && !inst->immediate) {
693  cf_log_warn(conf, "Ignoring 'exit_when_done' due to 'immediate' flag not being set");
694  inst->parent->exit_when_done = false;
695  }
696 
697  return 0;
698 }
699 
701 static pthread_mutex_t detail_file_mutex = PTHREAD_MUTEX_INITIALIZER;
702 
703 /** Compare two thread instances based on node pointer
704  *
705  * @param[in] one First thread specific xlat expansion instance.
706  * @param[in] two Second thread specific xlat expansion instance.
707  * @return CMP(one, two)
708  */
709 static int8_t _detail_file_cmp(void const *one, void const *two)
710 {
711  proto_detail_file_t const *a = one, *b = two;
712 
713  return CMP(strcmp(a->filename, b->filename), 0);
714 }
715 
716 /*
717  * Check for multiple readers on the same set of files.
718  */
719 static int mod_instantiate(module_inst_ctx_t const *mctx)
720 {
721  proto_detail_file_t *inst = talloc_get_type_abort(mctx->inst->data, proto_detail_file_t);
722  CONF_SECTION *conf = mctx->inst->conf;
723 
724  pthread_mutex_lock(&detail_file_mutex);
725  if (!detail_file_tree) {
727  if (!detail_file_tree) {
728  pthread_mutex_unlock(&detail_file_mutex);
729  cf_log_err(conf, "Failed initializing detail_file");
730  return -1;
731  }
732  }
733 
735  proto_detail_file_t const *old;
736 
738  fr_assert(old);
739 
740  pthread_mutex_unlock(&detail_file_mutex);
741  cf_log_err(conf, "Duplicate detail listener %s", inst->filename);
742  cf_log_err(conf, "Original detail listener is in virtual server %s", cf_section_name2(old->parent->server_cs));
743  return -1;
744  }
745  pthread_mutex_unlock(&detail_file_mutex);
746 
747  return 0;
748 }
749 
750 
751 static int mod_close(fr_listen_t *li)
752 {
754  proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
755 
756  if (thread->nr) (void) fr_network_listen_delete(thread->nr, inst->parent->listen);
757 
758  /*
759  * @todo - have our OWN event loop for timers, and a
760  * "copy timer from -> to, which means we only have to
761  * delete our child event loop from the parent on close.
762  */
763  if (thread->fd >= 0) close(thread->fd);
764 
765  if (thread->vnode_fd >= 0) {
766  if (thread->nr) {
767  (void) fr_network_listen_delete(thread->nr, inst->parent->listen);
768  } else {
769  if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) {
770  PERROR("Failed removing DELETE callback on detach");
771  }
772  }
773  close(thread->vnode_fd);
774  thread->vnode_fd = -1;
775 
776  pthread_mutex_destroy(&thread->worker_mutex);
777  }
778 
779  return 0;
780 }
781 
782 
783 /** Private interface for use by proto_detail_file
784  *
785  */
788  .common = {
789  .magic = MODULE_MAGIC_INIT,
790  .name = "detail_file",
791  .config = file_listen_config,
792  .inst_size = sizeof(proto_detail_file_t),
793  .thread_inst_size = sizeof(proto_detail_file_thread_t),
794  .bootstrap = mod_bootstrap,
795  .instantiate = mod_instantiate,
796  },
797  .default_message_size = 65536,
798  .default_reply_size = 32,
799 
800  .open = mod_open,
801  .close = mod_close,
802  .vnode = mod_vnode_extend,
803  .decode = mod_decode,
804 // .write = mod_write,
805  .event_list_set = mod_event_list_set,
806  .get_name = mod_name,
807 };
static int const char char buffer[256]
Definition: acutest.h:574
fr_io_close_t close
Close the transport.
Definition: app_io.h:60
fr_io_open_t open
Open a new socket for listening, or accept/connect a new connection.
Definition: app_io.h:43
module_t common
Common fields to all loadable modules.
Definition: app_io.h:34
fr_io_data_write_t write
Write from a data buffer to a socket.
Definition: app_io.h:48
fr_io_name_t get_name
get the socket name
Definition: app_io.h:70
Public structure describing an I/O path for a protocol.
Definition: app_io.h:33
#define DIAG_ON(_x)
Definition: build.h:419
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition: build.h:110
#define UNUSED
Definition: build.h:313
#define DIAG_OFF(_x)
Definition: build.h:418
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:486
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: cf_parse.h:406
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1126
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define cf_log_warn(_cf, _fmt,...)
Definition: cf_util.h:266
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition: debug.h:226
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
dl_module_inst_t const * dl_module_instance_by_data(void const *data)
Lookup a dl_module_inst_t via instance data.
Definition: dl_module.c:215
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
dl_module_inst_t const *_CONST parent
Parent module's instance (if any).
Definition: dl_module.h:167
A module/inst tuple.
Definition: dl_module.h:162
@ FR_EVENT_FILTER_VNODE
Filter for vnode subfilters.
Definition: event.h:63
#define fr_event_filter_insert(...)
Definition: event.h:219
fr_event_fd_cb_t delete
The file was deleted.
Definition: event.h:181
#define fr_event_timer_in(...)
Definition: event.h:255
Callbacks for the FR_EVENT_FILTER_VNODE filter.
Definition: event.h:180
size_t num_messages
for the message ring buffer
Definition: listen.h:52
bool non_socket_listener
special internal listener that does not use sockets.
Definition: listen.h:45
char const * name
printable name for this socket - set by open
Definition: listen.h:29
void const * app_instance
Definition: listen.h:38
size_t default_message_size
copied from app_io, but may be changed
Definition: listen.h:51
fr_app_t const * app
Definition: listen.h:37
void const * app_io_instance
I/O path configuration context.
Definition: listen.h:32
CONF_SECTION * server_cs
CONF_SECTION of the server.
Definition: listen.h:40
void * thread_instance
thread / socket context
Definition: listen.h:33
bool no_write_callback
sometimes we don't need to do writes
Definition: listen.h:44
int fd
file descriptor for this socket - set by open
Definition: listen.h:28
bool needs_full_setup
Set to true to avoid the short cut when adding the listener.
Definition: listen.h:46
fr_app_io_t const * app_io
I/O path functions.
Definition: listen.h:31
int fr_network_listen_delete(fr_network_t *nr, fr_listen_t *li)
Delete a socket from a network.
Definition: network.c:259
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG3(_fmt,...)
Definition: log.h:266
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition: event.c:1604
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition: event.c:1253
Stores all information relating to an event list.
Definition: event.c:411
main_config_t const * main_config
Main server configuration.
Definition: main_config.c:69
bool allow_core_dumps
Whether the server is allowed to drop a core when receiving a fatal signal.
Definition: main_config.h:132
static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, fr_time_t request_time, uint8_t *buffer, size_t buffer_len, size_t written)
Definition: master.c:2202
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
int rad_lockfd_nonblock(int fd, int lock_len)
Definition: misc.c:129
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Detail master protocol handler.
proto_detail_t * parent
The module that spawned us!
Definition: proto_detail.h:84
proto_detail_work_thread_t * file_parent
thread instance of the directory reader that spawned us
Definition: proto_detail.h:117
fr_time_delta_t lock_interval
interval between trying the locks.
Definition: proto_detail.h:123
fr_listen_t * listen
talloc_parent() is slow
Definition: proto_detail.h:116
char const * filename
file name, usually with wildcards
Definition: proto_detail.h:86
CONF_SECTION * server_cs
server CS for this listener
Definition: proto_detail.h:38
char const * name
debug name for printing
Definition: proto_detail.h:108
fr_event_timer_t const * ev
for detail file timers.
Definition: proto_detail.h:141
int num_workers
number of workers
Definition: proto_detail.h:144
fr_event_list_t * el
for various timers
Definition: proto_detail.h:114
pthread_mutex_t worker_mutex
for the workers
Definition: proto_detail.h:143
char const * filename_work
work file name
Definition: proto_detail.h:119
proto_detail_work_t const * inst
instance data
Definition: proto_detail.h:109
int fd
file descriptor
Definition: proto_detail.h:111
int vnode_fd
file descriptor for vnode_delete
Definition: proto_detail.h:112
fr_network_t * nr
for Linux-specific callbacks
Definition: proto_detail.h:115
static void mod_vnode_extend(fr_listen_t *li, UNUSED uint32_t fflags)
static const conf_parser_t file_listen_config[]
fr_app_io_t proto_detail_file
Private interface for use by proto_detail_file.
struct proto_detail_work_s proto_detail_file_t
static void work_retry_timer(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
static int8_t _detail_file_cmp(void const *one, void const *two)
Compare two thread instances based on node pointer.
static pthread_mutex_t detail_file_mutex
static int mod_decode(void const *instance, request_t *request, uint8_t *const data, size_t data_len)
static int mod_bootstrap(module_inst_ctx_t const *mctx)
static int work_rename(proto_detail_file_thread_t *thread)
static int mod_open(fr_listen_t *li)
Open a detail listener.
static char const * mod_name(fr_listen_t *li)
static fr_rb_tree_t * detail_file_tree
static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, UNUSED void *nr)
Set the event list for a new IO instance.
static int mod_close(fr_listen_t *li)
static int mod_instantiate(module_inst_ctx_t const *mctx)
static void work_init(proto_detail_file_thread_t *thread, bool triggered_by_delete)
Start processing a new work file.
static void mod_vnode_delete(fr_event_list_t *el, int fd, UNUSED int fflags, void *ctx)
static int work_exists(proto_detail_file_thread_t *thread, int fd)
static rs_t * conf
Definition: radsniff.c:53
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition: rb.c:624
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rb.c:576
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition: rb.h:246
The main red black tree structure.
Definition: rb.h:73
fr_network_t * fr_schedule_listen_add(fr_schedule_t *sc, fr_listen_t *li)
Add a fr_listen_t to a scheduler.
Definition: schedule.c:823
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
#define talloc_get_type_abort_const
Definition: talloc.h:270
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition: time.h:573
static fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b)
Definition: time.h:255
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition: time.h:154
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition: time.h:588
#define fr_time_delta_wrap(_time)
Definition: time.h:152
#define NSEC
Definition: time.h:377
static fr_time_delta_t fr_time_delta_div(fr_time_delta_t a, fr_time_delta_t b)
Definition: time.h:267
#define fr_time_delta_gt(_a, _b)
Definition: time.h:281
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
"server local" time.
Definition: time.h:69
close(uq->fd)
static fr_event_list_t * el
static fr_slen_t data
Definition: value.h:1259