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