The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 663ba8cb66b7a74d2df732c017d59da4a2575d7e $
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
49DIAG_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
59DIAG_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
71static void work_init(proto_detail_file_thread_t *thread, bool triggered_by_delete);
72static void mod_vnode_delete(fr_event_list_t *el, int fd, UNUSED int fflags, void *ctx);
73
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 */
90static 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
98static 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
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 */
133static 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 */
244static 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 */
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
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
429static 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 */
478static 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
519retry:
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
541delay:
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
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
617static 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
626static pthread_mutex_t detail_file_mutex = PTHREAD_MUTEX_INITIALIZER;
627
628/** Compare two thread instances based on node pointer
629 *
630 * @param[in] one First thread specific xlat expansion instance.
631 * @param[in] two Second thread specific xlat expansion instance.
632 * @return CMP(one, two)
633 */
634static int8_t _detail_file_cmp(void const *one, void const *two)
635{
636 proto_detail_file_t const *a = one, *b = two;
637
638 return CMP(strcmp(a->filename, b->filename), 0);
639}
640
641/*
642 * Check for multiple readers on the same set of files.
643 */
644static int mod_instantiate(module_inst_ctx_t const *mctx)
645{
646 proto_detail_file_t *inst = talloc_get_type_abort(mctx->mi->data, proto_detail_file_t);
647 CONF_SECTION *conf = mctx->mi->conf;
648
649 module_instance_t const *mi;
650 char *p;
651
652#ifdef __linux__
653 /*
654 * The kqueue API takes an FD, but inotify requires a filename.
655 * libkqueue uses /proc/PID/fd/# to look up the FD -> filename mapping.
656 *
657 * However, if you start the server as "root", and then swap to "radiusd",
658 * /proc/PID will be owned by "root" for security reasons. The only way
659 * to make /proc/PID owned by "radiusd" is to set the DUMPABLE flag.
660 *
661 * Instead of making the poor sysadmin figure this out,
662 * we check for this situation, and give them a
663 * descriptive message telling them what to do.
664 */
666 main_config->uid_is_set &&
667 main_config->server_uid != 0) {
668 cf_log_err(conf, "Cannot start detail file reader due to Linux limitations.");
669 cf_log_err(conf, "Please set 'allow_core_dumps = true' in the main configuration file.");
670 return -1;
671 }
672#endif
673
674 /*
675 * Find the module_instance_t holding our instance data
676 * so we can find out what the parent of our instance
677 * was.
678 */
679 mi = mctx->mi;
680
681#ifndef __linux__
682 /*
683 * Linux inotify works. So we allow poll_interval==0
684 */
685 FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, >=, 1);
686#endif
687 FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, <=, 3600);
688
689 inst->parent = talloc_get_type_abort(mi->parent->data, proto_detail_t);
690 inst->cs = conf;
691
692 inst->directory = p = talloc_strdup(inst, inst->filename);
693
694 p = strrchr(p, '/');
695 if (!p) {
696 cf_log_err(conf, "Filename must contain '/'");
697 return -1;
698 }
699
700 *p = '\0';
701
702 if (!inst->filename_work) {
703 inst->filename_work = talloc_typed_asprintf(inst, "%s/detail.work", inst->directory);
704 }
705
706 /*
707 * We need this for the lock.
708 */
709 inst->mode = O_RDWR;
710
711 if (inst->parent->exit_when_done && !inst->immediate) {
712 cf_log_warn(conf, "Ignoring 'exit_when_done' due to 'immediate' flag not being set");
713 inst->parent->exit_when_done = false;
714 }
715
716 pthread_mutex_lock(&detail_file_mutex);
717 if (!detail_file_tree) {
719 if (!detail_file_tree) {
720 pthread_mutex_unlock(&detail_file_mutex);
721 cf_log_err(conf, "Failed initializing detail_file");
722 return -1;
723 }
724 }
725
727 proto_detail_file_t const *old;
728
730 fr_assert(old);
731
732 pthread_mutex_unlock(&detail_file_mutex);
733 cf_log_err(conf, "Duplicate detail listener %s", inst->filename);
734 cf_log_err(conf, "Original detail listener is in virtual server %s", cf_section_name2(old->parent->server_cs));
735 return -1;
736 }
737 pthread_mutex_unlock(&detail_file_mutex);
738
739 return 0;
740}
741
742
743static int mod_close(fr_listen_t *li)
744{
746 proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t);
747
748 if (thread->nr) (void) fr_network_listen_delete(thread->nr, inst->parent->listen);
749
750 /*
751 * @todo - have our OWN event loop for timers, and a
752 * "copy timer from -> to, which means we only have to
753 * delete our child event loop from the parent on close.
754 */
755 if (thread->fd >= 0) close(thread->fd);
756
757 if (thread->vnode_fd >= 0) {
758 if (thread->nr) {
759 (void) fr_network_listen_delete(thread->nr, inst->parent->listen);
760 } else {
761 if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) {
762 PERROR("Failed removing DELETE callback on detach");
763 }
764 }
765 close(thread->vnode_fd);
766 thread->vnode_fd = -1;
767
768 pthread_mutex_destroy(&thread->worker_mutex);
769 }
770
771 return 0;
772}
773
774
775/** Private interface for use by proto_detail_file
776 *
777 */
780 .common = {
781 .magic = MODULE_MAGIC_INIT,
782 .name = "detail_file",
784 .inst_size = sizeof(proto_detail_file_t),
785 .thread_inst_size = sizeof(proto_detail_file_thread_t),
786 .instantiate = mod_instantiate,
787 },
788 .default_message_size = 65536,
789 .default_reply_size = 32,
790
791 .open = mod_open,
792 .close = mod_close,
793 .vnode = mod_vnode_extend,
794 .decode = mod_decode,
795// .write = mod_write,
796 .event_list_set = mod_event_list_set,
797 .get_name = mod_name,
798};
static int const char char buffer[256]
Definition acutest.h:576
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:458
#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:112
#define UNUSED
Definition build.h:315
#define DIAG_OFF(_x)
Definition build.h:457
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:502
#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:418
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1185
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:228
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
@ 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:1611
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:1260
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.
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:2223
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_time_delta_t lock_interval
interval between trying the locks.
fr_listen_t * listen
talloc_parent() is slow
char const * filename
file name, usually with wildcards
CONF_SECTION * server_cs
server CS for this listener
char const * name
debug name for printing
fr_event_timer_t const * ev
for detail file timers.
int num_workers
number of workers
fr_event_list_t * el
for various timers
pthread_mutex_t worker_mutex
for the workers
char const * filename_work
work file name
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 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 work_rename(proto_detail_file_thread_t *thread)
static int mod_open(fr_listen_t *li)
Open a detail listener.
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 char const * mod_name(fr_listen_t *li)
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)
#define fr_assert(_expr)
Definition rad_assert.h:38
static rs_t * conf
Definition radsniff.c:53
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:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
#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:881
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:329
void * data
Module's instance data.
Definition module.h:271
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:337
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:198
Module instance data.
Definition module.h:265
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:492
#define talloc_get_type_abort_const
Definition talloc.h:282
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
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:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define NSEC
Definition time.h:379
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
close(uq->fd)
static fr_event_list_t * el
static fr_slen_t data
Definition value.h:1265