The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
proto_detail_work.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: 028b390c03fe132ea9e5baf19479779904266813 $
19 * @file proto_detail_work.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#include <netdb.h>
26#include <freeradius-devel/server/protocol.h>
27#include <freeradius-devel/server/pair.h>
28#include <freeradius-devel/server/main_loop.h>
29#include <freeradius-devel/io/application.h>
30#include <freeradius-devel/io/listen.h>
31#include <freeradius-devel/util/syserror.h>
32#include "proto_detail.h"
33
34#include <fcntl.h>
35#include <sys/stat.h>
36
37#ifndef NDEBUG
38#if 0
39/*
40 * When we want detailed debugging here, without detailed server
41 * debugging.
42 */
43#define MPRINT DEBUG
44#else
45#define MPRINT DEBUG3
46#endif
47#else
48// No debugging, just remove the mprint entirely
49#define MPRINT(_x, ...)
50#endif
51
52typedef struct {
53 proto_detail_work_thread_t *parent; //!< talloc_parent is SLOW!
54 fr_time_t timestamp; //!< when we read the entry.
55 off_t done_offset; //!< where we're tracking the status
56
57 int id; //!< for retransmission counters
58
59 uint8_t *packet; //!< for retransmissions
60 size_t packet_len; //!< for retransmissions
61
62 fr_retry_t retry; //!< our retry timers
63 fr_timer_t *ev; //!< retransmission timer
64 fr_dlist_t entry; //!< for the retransmission list
66
68 { FR_CONF_OFFSET("initial_rtx_time", proto_detail_work_t, retry_config.irt), .dflt = STRINGIFY(2) },
69 { FR_CONF_OFFSET("max_rtx_time", proto_detail_work_t, retry_config.mrt), .dflt = STRINGIFY(16) },
70
71 /*
72 * Retransmit indefinitely, as v2 and v3 did.
73 */
74 { FR_CONF_OFFSET("max_rtx_count", proto_detail_work_t, retry_config.mrc), .dflt = STRINGIFY(0) },
75 /*
76 * ...again same as v2 and v3.
77 */
78 { FR_CONF_OFFSET("max_rtx_duration", proto_detail_work_t, retry_config.mrd), .dflt = STRINGIFY(0) },
79 { FR_CONF_OFFSET("max_outstanding", proto_detail_work_t, max_outstanding), .dflt = STRINGIFY(1) },
81};
82
83
85 { FR_CONF_OFFSET_FLAGS("filename", CONF_FLAG_REQUIRED, proto_detail_work_t, filename_work ) },
86
87 { FR_CONF_OFFSET("track", proto_detail_work_t, track_progress ) },
88
89 { FR_CONF_OFFSET("retransmit", proto_detail_work_t, retransmit ), .dflt = "yes" },
90
91 { FR_CONF_POINTER("limit", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) limit_config },
93};
94
96
99 { .out = &dict_freeradius, .proto = "freeradius" },
100
102};
103
105
111
112/*
113 * All of the decoding is done by proto_detail.c
114 */
115static int mod_decode(void const *instance, request_t *request, UNUSED uint8_t *const data, UNUSED size_t data_len)
116{
117
119 fr_detail_entry_t const *track = talloc_get_type_abort_const(request->async->packet_ctx, fr_detail_entry_t);
120 fr_pair_t *vp;
121
122 request->client = inst->client;
123
124 request->packet->id = track->id;
125 request->reply->id = track->id;
126 REQUEST_VERIFY(request);
127
129 vp->vp_uint32 = track->retry.count;
130
131 return 0;
132}
133
136 { 0 }
137};
138
141 { 0 }
142};
143
144static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
145{
147 proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
148
149 ssize_t data_size;
150 size_t packet_len;
151 fr_detail_entry_t *track;
152 uint8_t *partial, *end, *next, *p, *record_end;
153 uint8_t *stopped_search;
154 off_t done_offset;
155
156 fr_assert(*leftover < buffer_len);
157 fr_assert(thread->fd >= 0);
158 fr_assert(thread->el);
159
160 MPRINT("AT COUNT %d offset %ld", thread->count, (long) thread->read_offset);
161
162 /*
163 * Process retransmissions before anything else in the
164 * file.
165 */
166 track = fr_dlist_head(&thread->list);
167 if (track) {
168 fr_dlist_remove(&thread->list, track);
169
170 /*
171 * Don't over-write "leftover" bytes!
172 */
173 if (*leftover) {
174 fr_assert(thread->leftover == 0);
175 if (!thread->leftover_buffer) MEM(thread->leftover_buffer = talloc_array(thread, uint8_t, buffer_len));
176
177 memcpy(thread->leftover_buffer, buffer, *leftover);
178 thread->leftover = *leftover;
179 *leftover = 0;
180 }
181
182 fr_assert(buffer_len >= track->packet_len);
183 memcpy(buffer, track->packet, track->packet_len);
184
185 DEBUG("Retrying packet %d (retransmission %u)", track->id, track->retry.count);
186 *packet_ctx = track;
187 *recv_time_p = track->timestamp;
188 return track->packet_len;
189 }
190
191 /*
192 * If we decide that we're closing, ignore everything
193 * else in the file. Someone extended the file on us
194 * without locking it first. So too bad for them.
195 */
196 if (thread->closing) {
197 if (inst->track_progress) thread->read_offset = lseek(thread->fd, 0, SEEK_END);
198 return 0;
199 }
200
201 /*
202 * Once a socket is ready, the network side tries to read
203 * many packets. So if we want to stop it from reading,
204 * we have to check this ourselves.
205 */
206 if (thread->outstanding >= inst->max_outstanding) {
207 fr_assert(thread->paused);
208 return 0;
209 }
210
211 /*
212 * If we've cached leftover data from the ring buffer,
213 * copy it back.
214 */
215 if (thread->leftover) {
216 fr_assert(*leftover == 0);
217 fr_assert(thread->leftover < buffer_len);
218
219 memcpy(buffer, thread->leftover_buffer, thread->leftover);
220 *leftover = thread->leftover;
221 thread->leftover = 0;
222 }
223
224 /*
225 * Seek to the current read offset.
226 */
227 (void) lseek(thread->fd, thread->read_offset, SEEK_SET);
228
229 /*
230 * There will be "leftover" bytes left over in the buffer
231 * from any previous read. At the start of the file,
232 * "leftover" will be zero.
233 */
234 partial = buffer + *leftover;
235
236 MPRINT("READ leftover %zd", *leftover);
237
238 /*
239 * Try to read as much data as possible.
240 */
241 if (!thread->eof) {
242 size_t room;
243
244 room = buffer_len - *leftover;
245
246 data_size = read(thread->fd, partial, room);
247 if (data_size < 0) {
248 ERROR("proto_detail (%s): Failed reading file %s: %s",
249 thread->name, thread->filename_work, fr_syserror(errno));
250 return -1;
251 }
252
253 MPRINT("GOT %zd bytes", data_size);
254
255 /*
256 * Remember the read offset, and whether we got EOF.
257 */
258 thread->read_offset = lseek(thread->fd, 0, SEEK_CUR);
259
260 /*
261 * Only set EOF if there's no more data in the buffer to manage.
262 */
263 thread->eof = (data_size == 0) || (thread->read_offset == thread->file_size) || ((size_t) data_size < room);
264 if (thread->eof) {
265 MPRINT("Set EOF data_size %ld vs room %ld", data_size, room);
266 MPRINT("Set EOF read %ld vs file %ld", (long) thread->read_offset, (long) thread->file_size);
267 }
268 end = partial + data_size;
269
270 } else {
271 MPRINT("READ UNTIL EOF");
272 /*
273 * We didn't read any more data from the file,
274 * but there should be data left in the buffer.
275 */
276 fr_assert(*leftover > 0);
277 end = buffer + *leftover;
278 }
279
280redo:
281 next = NULL;
282 stopped_search = end;
283
284 /*
285 * Look for "end of record" marker, starting from the
286 * beginning of the buffer.
287 *
288 * Note that all of the data MUST be printable, and raw
289 * LFs are forbidden in attribute contents.
290 */
291 fr_assert((buffer + thread->last_search) <= end);
292
293 MPRINT("Starting search from offset %ld", thread->last_search);
294
295 p = buffer + thread->last_search;
296 while (p < end) {
297 if (p[0] != '\n') {
298 p++;
299 continue;
300 }
301 if ((p + 1) == end) {
302 /*
303 * Remember the last LF, so if the next
304 * read starts with a LF, we can find the
305 * end of record marker.
306 */
307 stopped_search = p;
308 break; /* no more data */
309 }
310
311 if (p[1] == '\n') {
312 p[0] = '\0';
313 p[1] = '\0';
314 next = p + 2;
315 stopped_search = next;
316 thread->last_line++;
317 break;
318 }
319
320 /*
321 * If we're not at EOF, and we're not at end of
322 * record, every line MUST have a leading tab.
323 */
324 if (p[1] != '\t') {
325 ERROR("proto_detail (%s): Missing tab indent at %s[%u], offset from start of file %zu",
326 thread->name,
327 thread->filename_work, thread->last_line,
328 (size_t)((p - buffer) + thread->header_offset));
329 return -1;
330 }
331
332 /*
333 * Smash the \n with zero, so that each line can
334 * be parsed individually.
335 */
336 p[0] = '\0';
337
338 /*
339 * Skip the \n\t
340 */
341 if ((p + 2) >= end) {
342 stopped_search = p;
343 break;
344 }
345
346 p += 2;
347
348 /*
349 * Skip attribute name
350 */
351 while ((p < end) && !isspace((uint8_t) *p)) p++;
352
353 /*
354 * Not enough room for " = ", skip this sanity
355 * check, and just search for a \n on the next
356 * round through the loop.
357 */
358 if ((end - p) < 3) {
359 stopped_search = p;
360 break;
361 }
362
363 /*
364 * Check for " = ". If the line doesn't contain
365 * this, it's malformed.
366 */
367 if (memcmp(p, " = ", 3) != 0) {
368 ERROR("proto_detail (%s): Missing pair assignment operator at %s[%u], offset from start of file %zu: %.*s",
369 thread->name,
370 thread->filename_work, thread->last_line,
371 (size_t)((p - buffer) + thread->header_offset), (int) (end - p), p);
372 return -1;
373 }
374
375 /*
376 * Skip the " = ", and go back to the top of the
377 * loop where we check for the next \n.
378 */
379 p += 3;
380 }
381
382 thread->last_search = (stopped_search - buffer);
383
384 /*
385 * If there is a next record, remember how large this
386 * record is, and update "leftover" bytes.
387 */
388 if (next) {
389 packet_len = next - buffer;
390 *leftover = end - next;
391
392 MPRINT("FOUND next at %zd, leftover is %zd", packet_len, *leftover);
393
394 } else if (!thread->eof) {
395 if ((size_t) (end - buffer) == buffer_len) {
396 ERROR("proto_detail (%s): Too large entry (>%d bytes) found at offset %zu: %.*s of file %s",
397 thread->name, (int) buffer_len,
398 (size_t)((p - buffer) + thread->header_offset), (int) (end - p), p,
399 thread->filename_work);
400 return -1;
401 }
402
403 /*
404 * We're not at EOF, and there is no "next"
405 * entry. Remember all of the leftover data in
406 * the buffer, and ask the caller to call us when
407 * there's more data.
408 */
409 *leftover = end - buffer;
410 MPRINT("Not at EOF, and no next. Leftover is %zd", *leftover);
411 return 0;
412
413 } else {
414 /*
415 * Else we're at EOF, it's OK to not have an "end
416 * of record" marker. We just eat all of the
417 * remaining data.
418 */
419 packet_len = end - buffer;
420 *leftover = 0;
421
422 MPRINT("NO end of record, but at EOF, found %zd leftover is 0", packet_len);
423 }
424
425 /*
426 * Too big? Ignore it.
427 *
428 * @todo - skip the record, using memmove() etc.
429 */
430 if (packet_len > inst->parent->max_packet_size) {
431 DEBUG("Ignoring 'too large' entry at offset %zu of %s",
432 (size_t) thread->header_offset, thread->filename_work);
433 DEBUG("Entry size %lu is greater than allowed maximum %u",
434 packet_len, inst->parent->max_packet_size);
435 skip_record:
436 MPRINT("Skipping record");
437 if (next) {
438 memmove(buffer, next, (end - next));
439 data_size = (end - next);
440 *leftover = 0;
441 end = buffer + data_size;
442 thread->last_search = 0;
443
444 /*
445 * No more data, we're done.
446 */
447 if (end == buffer) return 0;
448 goto redo;
449 }
450
451 fr_assert(*leftover == 0);
452 packet_len = 0;
453 goto done;
454 }
455
456 /*
457 * Search for the "Timestamp" attribute. We overload
458 * that to track which entries have been used.
459 */
460 record_end = buffer + packet_len;
461 p = buffer;
462 done_offset = 0;
463
464 while (p < record_end) {
465 if (*p != '\0') {
466 p++;
467 continue;
468 }
469
470 p++;
471 if (p == record_end) break;
472
473 if (((record_end - p) >= 5) &&
474 (memcmp(p, "\tDone", 5) == 0)) {
475 goto skip_record;
476 }
477
478 if (((record_end - p) > 10) &&
479 (memcmp(p, "\tTimestamp", 10) == 0)) {
480 p++;
481 done_offset = thread->header_offset + (p - buffer);
482 }
483 }
484
485 /*
486 * Allocate the tracking entry.
487 */
488 MEM(track = talloc_zero(thread, fr_detail_entry_t));
489 track->parent = thread;
490 track->timestamp = fr_time();
491 track->id = thread->count++;
492
493 track->done_offset = done_offset;
494 if (inst->retransmit) {
495 MEM(track->packet = talloc_memdup(track, buffer, packet_len));
496 track->packet_len = packet_len;
497 }
498
499 /*
500 * We've read one more packet.
501 */
502 thread->header_offset += packet_len;
503
504 *packet_ctx = track;
505 *recv_time_p = track->timestamp;
506
507 thread->outstanding++;
508
509done:
510 /*
511 * If we're at EOF, mark us as "closing".
512 */
513 if (thread->eof) {
514 fr_assert(!thread->closing);
515 thread->closing = (*leftover == 0);
516 MPRINT("AT EOF, BUT CLOSING %d", thread->closing);
517 }
518
519 /*
520 * Pause reading until such time as we need more packets.
521 */
522 if (!thread->paused && (thread->outstanding >= inst->max_outstanding)) {
523 (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, pause_read);
524 thread->paused = true;
525
526 /*
527 * Back up so that read() knows there's more data.
528 */
529 if (*leftover) (void) lseek(thread->fd, thread->read_offset - 1, SEEK_SET);
530 }
531
532 /*
533 * Next time, start searching from the start of the
534 * buffer.
535 */
536 thread->last_search = 0;
537
538 MPRINT("Returning NUM %u - %.*s", thread->outstanding, (int) packet_len, buffer);
539 return packet_len;
540}
541
542
543static void work_retransmit(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
544{
545 fr_detail_entry_t *track = talloc_get_type_abort(uctx, fr_detail_entry_t);
546 proto_detail_work_thread_t *thread = track->parent;
547
548 DEBUG("%s - retransmitting packet %d", thread->name, track->id);
549
550 fr_dlist_insert_tail(&thread->list, track);
551
552 if (thread->paused && (thread->outstanding < thread->inst->max_outstanding)) {
553 (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, resume_read);
554 thread->paused = false;
555 }
556
557 fr_assert(thread->fd >= 0);
558
559 /*
560 * Seek to the START of the file, so that the FD will
561 * always return ready.
562 *
563 * The mod_read() function will take care of seeking to
564 * the correct read offset.
565 */
566 (void) lseek(thread->fd, 0, SEEK_SET);
567
568#ifdef __linux__
569 fr_network_listen_read(thread->nr, thread->listen);
570#endif
571}
572
573static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
574 uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
575{
577 proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
578 fr_detail_entry_t *track = packet_ctx;
579
580 if (buffer_len < 1) return -1;
581
582 fr_assert(thread->outstanding > 0);
583 fr_assert(thread->fd >= 0);
584
585 if (!buffer[0]) {
586 if (!inst->retransmit) goto mark_done;
587
588 if (fr_time_eq(track->retry.start, fr_time_wrap(0))) {
589 fr_retry_init(&track->retry, fr_time(), &inst->retry_config);
590 } else {
591 fr_retry_state_t state;
592
593 state = fr_retry_next(&track->retry, fr_time());
594 if (state == FR_RETRY_MRC) {
595 DEBUG("%s - packet %d failed after %u retransmissions",
596 thread->name, track->id, track->retry.count);
597 goto fail;
598
599 }
600
601 if (state == FR_RETRY_MRD) {
602 DEBUG("%s - packet %d failed after %u seconds",
603 thread->name, track->id,
604 (unsigned int) fr_time_delta_to_sec(inst->retry_config.mrd));
605 goto fail;
606 }
607 }
608
609 DEBUG("%s - packet %d failed during processing. Will retransmit in %.6fs",
610 thread->name, track->id, fr_time_delta_unwrap(track->retry.rt) / (double)NSEC);
611
612 if (fr_timer_at(thread, thread->el->tl, &track->ev,
613 track->retry.next, false, work_retransmit, track) < 0) {
614 ERROR("%s - Failed inserting retransmission timeout", thread->name);
615 fail:
616 if (inst->track_progress && (track->done_offset > 0)) goto mark_done;
617 goto free_track;
618 }
619
620 if (!thread->paused && (thread->outstanding >= inst->max_outstanding)) {
621 (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, pause_read);
622 thread->paused = true;
623 }
624
625 return 1;
626
627 } else if (inst->track_progress && (track->done_offset > 0)) {
628 mark_done:
629 /*
630 * Seek to the entry, mark it as done, and then seek to
631 * the point in the file where we were reading from.
632 */
633 (void) lseek(thread->fd, track->done_offset, SEEK_SET);
634 if (write(thread->fd, "Done", 4) < 0) {
635 ERROR("%s - Failed marking entry as done: %s", thread->name, fr_syserror(errno));
636 }
637 (void) lseek(thread->fd, thread->read_offset, SEEK_SET);
638 }
639
640free_track:
641 thread->outstanding--;
642
643 /*
644 * If we need to read some more packet, let's do so.
645 */
646 if (thread->paused && (thread->outstanding < inst->max_outstanding)) {
647 (void) fr_event_filter_update(thread->el, thread->fd, FR_EVENT_FILTER_IO, resume_read);
648 thread->paused = false;
649
650 /*
651 * And seek to the start of the file, so that the
652 * reader gets activated again. The reader will
653 * lseek() to the read offset, so this seek is fine.
654 */
655 (void) lseek(thread->fd, 0, SEEK_SET);
656 }
657
658 /*
659 * @todo - add a used / free pool for these
660 */
661 talloc_free(track);
662
663 /*
664 * Close the socket if we're at EOF, and there are no
665 * outstanding replies to deal with.
666 */
667 if (thread->closing && !thread->outstanding) {
668 MPRINT("WRITE ASKED TO CLOSE");
669 errno = ECONNRESET;
670 return -1;
671 }
672
673 MPRINT("WRITE RETURN B %ld", buffer_len);
674 return buffer_len;
675}
676
677/** Open a detail listener
678 *
679 */
680static int mod_open(fr_listen_t *li)
681{
683 proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
684
685 fr_dlist_init(&thread->list, fr_detail_entry_t, entry);
686
687 /*
688 * Open the file if we haven't already been given one.
689 */
690 if (thread->fd < 0) {
691 thread->filename_work = talloc_strdup(thread, inst->filename_work);
692
693 li->fd = thread->fd = open(thread->filename_work, inst->mode);
694 if (thread->fd < 0) {
695 cf_log_err(inst->cs, "Failed opening %s: %s", thread->filename_work, fr_syserror(errno));
696 return -1;
697 }
698 }
699
700 /*
701 * If we're tracking progress, learn where the EOF is.
702 */
703 if (inst->track_progress) {
704 struct stat buf;
705
706 if (fstat(thread->fd, &buf) < 0) {
707 cf_log_err(inst->cs, "Failed checking %s: %s", thread->filename_work, fr_syserror(errno));
708 return -1;
709 }
710
711 thread->file_size = buf.st_size;
712 } else {
713 /*
714 * Avoid triggering erroneous EOF.
715 */
716 thread->file_size = 1;
717 }
718
719 fr_assert(thread->name == NULL);
720 fr_assert(thread->filename_work != NULL);
721 thread->name = talloc_typed_asprintf(thread, "detail_work reading file %s", thread->filename_work);
722
723 /*
724 * Linux doesn't like us adding write callbacks for FDs
725 * which reference files. Since the callback is only
726 * used when the FD blocks, and files don't (mostly)
727 * block, we just mark this read-only.
728 *
729 * The code in src/lib/io/network.c will call the
730 * mod_write() callback on any write, even if the
731 * listener is marked "read_only"
732 */
733 li->no_write_callback = true;
734
735 return 0;
736}
737
738
740{
742
743 /*
744 * One less worker... we check for "0" because of the
745 * hacks in proto_detail which let us start up with
746 * "transport = work" for debugging purposes.
747 */
748 if (thread->file_parent) {
749 pthread_mutex_lock(&thread->file_parent->worker_mutex);
750 if (thread->file_parent->num_workers > 0) thread->file_parent->num_workers--;
751 pthread_mutex_unlock(&thread->file_parent->worker_mutex);
752 }
753
754 DEBUG("Closing %sdetail worker file %s", thread->outstanding == 0 ? "and deleting " : "", thread->name);
755
756#ifdef NOTE_REVOKE
758#endif
759 fr_event_fd_delete(thread->el, thread->fd, FR_EVENT_FILTER_IO);
760
761 if (thread->outstanding == 0) unlink(thread->filename_work);
762
763 close(thread->fd);
764 thread->fd = -1;
765
766 if (inst->parent->exit_when_done) {
767 INFO("Done reading detail files, process will now exit");
768
769 /*
770 * The least hacky way of signalling that the server
771 * should exit. This is an almost identical code
772 * path as receiving a SIGTERM.
773 */
775 }
776
777 return 0;
778}
779
780
781/** Close a detail listener
782 *
783 */
784static int mod_close(fr_listen_t *li)
785{
786 proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
787
788 return mod_close_internal(thread);
789}
790
791#ifdef NOTE_REVOKE
792static void mod_revoke(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
793{
794 proto_detail_work_thread_t *thread = talloc_get_type_abort(uctx, proto_detail_work_thread_t);
795
796 /*
797 * The underlying file system is gone. Stop reading the
798 * file, destroy all of the IO handlers, and delete everything.
799 */
800 DEBUG("Detail worker %s had file system unmounted. Stopping.", thread->name);
801 mod_close_internal(thread);
802}
803#endif
804
805
806/** Set the event list for a new IO instance
807 *
808 * @param[in] li the listener
809 * @param[in] el the event list
810 * @param[in] nr context from the network side
811 */
813{
814 proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
815
816#ifdef NOTE_REVOKE
818
819 memset(&funcs, 0, sizeof(funcs));
820 funcs.revoke = mod_revoke;
821
822 if (fr_event_filter_insert(thread, NULL, el, thread->fd, FR_EVENT_FILTER_VNODE, &funcs, NULL, thread) < 0) {
823 WARN("Failed to add event watching for unmounted file system");
824 }
825#endif
826
827 thread->el = el;
828 thread->nr = nr;
829}
830
831
832static char const *mod_name(fr_listen_t *li)
833
834{ proto_detail_work_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_work_thread_t);
835
836 return thread->name;
837}
838
839static int mod_instantiate(module_inst_ctx_t const *mctx)
840{
841 proto_detail_work_t *inst = talloc_get_type_abort(mctx->mi->data, proto_detail_work_t);
842 fr_client_t *client;
843 CONF_SECTION *cs = mctx->mi->conf;
844 module_instance_t const *mi = mctx->mi;
845
846 inst->parent = talloc_get_type_abort(mi->parent->data, proto_detail_t);
847 inst->cs = cs;
848
849 if (inst->track_progress) {
850 inst->mode = O_RDWR;
851 } else {
852 inst->mode = O_RDONLY;
853 }
854
855 if (inst->retransmit) {
856 FR_TIME_DELTA_BOUND_CHECK("limit.initial_rtx_time", inst->retry_config.irt, >=, fr_time_delta_from_sec(1));
857 FR_TIME_DELTA_BOUND_CHECK("limit.initial_rtx_time", inst->retry_config.irt, <=, fr_time_delta_from_sec(60));
858
859 /*
860 * If you need more than this, just set it to
861 * "0", and check Packet-Transmit-Count manually.
862 */
863 FR_INTEGER_BOUND_CHECK("limit.max_rtx_count", inst->retry_config.mrc, <=, 20);
864 FR_TIME_DELTA_BOUND_CHECK("limit.max_rtx_duration", inst->retry_config.mrd, <=, fr_time_delta_from_sec(600));
865
866 /*
867 * This is a reasonable value.
868 */
869 FR_TIME_DELTA_BOUND_CHECK("limit.max_rtx_timer", inst->retry_config.mrt, <=, fr_time_delta_from_sec(30));
870 }
871
872 FR_INTEGER_BOUND_CHECK("limit.max_outstanding", inst->max_outstanding, >=, 1);
873
874 MEM(client = inst->client = talloc_zero(inst, fr_client_t));
875
876 client->ipaddr.af = AF_INET;
877 client->ipaddr.addr.v4.s_addr = htonl(INADDR_NONE);
878 client->src_ipaddr = client->ipaddr;
879
880 client->longname = client->shortname = client->secret = inst->filename_work;
881 client->nas_type = talloc_strdup(client, "other");
882
883 return 0;
884}
885
886/** Private interface for use by proto_detail_file
887 *
888 */
891 .common = {
892 .magic = MODULE_MAGIC_INIT,
893 .name = "detail_work",
895 .inst_size = sizeof(proto_detail_work_t),
896 .thread_inst_size = sizeof(proto_detail_work_thread_t),
897 .instantiate = mod_instantiate
898 },
899 .default_message_size = 65536,
900 .default_reply_size = 32,
901
902 .open = mod_open,
903 .close = mod_close,
904 .read = mod_read,
905 .decode = mod_decode,
906 .write = mod_write,
907 .event_list_set = mod_event_list_set,
908 .get_name = mod_name,
909};
static int const char char buffer[256]
Definition acutest.h:578
module_t common
Common fields to all loadable modules.
Definition app_io.h:34
Public structure describing an I/O path for a protocol.
Definition app_io.h:33
#define STRINGIFY(x)
Definition build.h:197
#define UNUSED
Definition build.h:317
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:660
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:520
#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:283
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition cf_parse.h:337
#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:271
#define FR_TIME_DELTA_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:531
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:432
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:426
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:597
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:288
static int max_outstanding
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:294
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:307
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:313
Specifies an attribute which must be present for the module to function.
Definition dict.h:293
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:306
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
Entry in a doubly linked list.
Definition dlist.h:41
@ FR_EVENT_FILTER_VNODE
Filter for vnode subfilters.
Definition event.h:85
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:84
#define fr_event_filter_update(...)
Definition event.h:240
#define fr_event_filter_insert(...)
Definition event.h:235
#define FR_EVENT_RESUME(_s, _f)
Re-add the filter for a func from kevent.
Definition event.h:132
#define FR_EVENT_SUSPEND(_s, _f)
Temporarily remove the filter for a func from kevent.
Definition event.h:116
Callbacks for the FR_EVENT_FILTER_IO filter.
Definition event.h:189
Structure describing a modification to a filter's state.
Definition event.h:97
Callbacks for the FR_EVENT_FILTER_VNODE filter.
Definition event.h:196
talloc_free(hp)
int af
Address family.
Definition inet.h:64
union fr_ipaddr_t::@137 addr
void const * app_io_instance
I/O path configuration context.
Definition listen.h:33
void * thread_instance
thread / socket context
Definition listen.h:34
bool no_write_callback
sometimes we don't need to do writes
Definition listen.h:46
int fd
file descriptor for this socket - set by open
Definition listen.h:28
void fr_network_listen_read(fr_network_t *nr, fr_listen_t *li)
Signal the network to read from a listener.
Definition network.c:336
fr_ipaddr_t ipaddr
IPv4/IPv6 address of the host.
Definition client.h:83
char const * secret
Secret PSK.
Definition client.h:90
fr_ipaddr_t src_ipaddr
IPv4/IPv6 address to send responses from (family must match ipaddr).
Definition client.h:84
char const * nas_type
Type of client (arbitrary).
Definition client.h:131
char const * longname
Client identifier.
Definition client.h:87
char const * shortname
Client nickname.
Definition client.h:88
Describes a host allowed to send packets to the server.
Definition client.h:80
#define fr_time()
Definition event.c:60
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:1203
Stores all information relating to an event list.
Definition event.c:377
void main_loop_signal_raise(int flag)
Definition main_loop.c:78
@ RADIUS_SIGNAL_SELF_TERM
Definition main_loop.h:37
@ FR_TYPE_UINT32
32 Bit unsigned integer.
long int ssize_t
unsigned char uint8_t
long long int off_t
unsigned long int size_t
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.
struct proto_detail_work_s proto_detail_work_t
size_t last_search
where we last searched in the buffer MUST be offset, as the buffers can change.
proto_detail_work_thread_t * file_parent
thread instance of the directory reader that spawned us
bool paused
Is reading paused?
int count
number of packets we read from this file.
unsigned int last_line
line number of the last record read.
bool eof
are we at EOF on reading?
fr_listen_t * listen
talloc_parent() is slow
char const * name
debug name for printing
int num_workers
number of workers
uint16_t max_outstanding
number of packets to run in parallel
fr_event_list_t * el
for various timers
off_t file_size
size of the file
pthread_mutex_t worker_mutex
for the workers
char const * filename_work
work file name
off_t header_offset
offset of the current header we're reading
proto_detail_work_t const * inst
instance data
fr_dlist_head_t list
for retransmissions
int fd
file descriptor
bool closing
we should be closing the file
off_t read_offset
where we're reading from in filename_work
uint32_t outstanding
number of currently outstanding records;
fr_network_t * nr
for Linux-specific callbacks
fr_dict_attr_autoload_t proto_detail_work_dict_attr[]
static const conf_parser_t file_listen_config[]
static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
#define MPRINT
static int mod_decode(void const *instance, request_t *request, UNUSED uint8_t *const data, UNUSED size_t data_len)
static fr_dict_t const * dict_freeradius
static void work_retransmit(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
proto_detail_work_thread_t * parent
talloc_parent is SLOW!
fr_dlist_t entry
for the retransmission list
static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, void *nr)
Set the event list for a new IO instance.
static int mod_open(fr_listen_t *li)
Open a detail listener.
size_t packet_len
for retransmissions
fr_retry_t retry
our retry timers
uint8_t * packet
for retransmissions
fr_time_t timestamp
when we read the entry.
fr_dict_autoload_t proto_detail_work_dict[]
static fr_event_update_t pause_read[]
static conf_parser_t limit_config[]
fr_timer_t * ev
retransmission timer
fr_app_io_t proto_detail_work
Private interface for use by proto_detail_file.
static int mod_close_internal(proto_detail_work_thread_t *thread)
static char const * mod_name(fr_listen_t *li)
int id
for retransmission counters
static int mod_close(fr_listen_t *li)
Close a detail listener.
off_t done_offset
where we're tracking the status
static fr_dict_attr_t const * attr_packet_transmit_counter
static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
static int mod_instantiate(module_inst_ctx_t const *mctx)
static fr_event_update_t resume_read[]
#define fr_assert(_expr)
Definition rad_assert.h:38
#define pair_update_request(_attr, _da)
#define WARN(fmt,...)
static bool done
Definition radclient.c:83
#define INFO(fmt,...)
Definition radict.c:64
#define REQUEST_VERIFY(_x)
Definition request.h:309
static conf_parser_t retry_config[]
Definition rlm_tacacs.c:39
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
void * data
Module's instance data.
Definition module.h:291
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:357
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
Module instance data.
Definition module.h:285
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:545
#define talloc_get_type_abort_const
Definition talloc.h:113
#define talloc_strdup(_ctx, _str)
Definition talloc.h:145
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_wrap(_time)
Definition time.h:145
#define fr_time_eq(_a, _b)
Definition time.h:241
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647
#define NSEC
Definition time.h:379
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:50
A timer event.
Definition timer.c:84
#define fr_timer_at(...)
Definition timer.h:81
static fr_event_list_t * el
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
Definition retry.c:110
void fr_retry_init(fr_retry_t *r, fr_time_t now, fr_retry_config_t const *config)
Initialize a retransmission counter.
Definition retry.c:36
fr_time_t start
when we started the retransmission
Definition retry.h:53
fr_time_delta_t rt
retransmit interval
Definition retry.h:57
fr_retry_state_t
Definition retry.h:45
@ FR_RETRY_MRC
reached maximum retransmission count
Definition retry.h:47
@ FR_RETRY_MRD
reached maximum retransmission duration
Definition retry.h:48
uint32_t count
number of sent packets
Definition retry.h:58
fr_time_t next
when the next timer should be set
Definition retry.h:55
static fr_slen_t data
Definition value.h:1340