The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
base.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 (at
5 * 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: 754f123abea5e4ea94c9b5892637ed204302b5e0 $
19 * @file kafka/base.c
20 * @brief Kafka global structures
21 *
22 * @copyright 2022 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24
25#include <freeradius-devel/kafka/base.h>
26#include <freeradius-devel/server/tmpl.h>
27#include <freeradius-devel/util/size.h>
28
29/* fr_kafka_conf_ctx_t definition lives in base.h so the KAFKA_BASE_CONFIG
30 * macro can construct struct literals of it from caller TUs. */
31
32/** @name Shared helpers
33 *
34 * Used by both the base-level and topic-level parse/dflt paths below.
35 *
36 * @{
37 */
38
39/** Common parse path for a single CONF_PAIR's value
40 *
41 * Handles librdkafka's preferred unit conventions (ms-integer for time
42 * deltas, byte-integer for sizes, string "true"/"false" for bools) and
43 * the optional kctx->mapping translation. Caller hands the resulting
44 * string to either rd_kafka_conf_set or rd_kafka_topic_conf_set.
45 */
46static int kafka_config_parse_single(char const **out, CONF_PAIR *cp, conf_parser_t const *rule)
47{
49 fr_kafka_conf_ctx_t const *kctx = rule->uctx;
50 fr_type_t type = rule->type;
51 static _Thread_local char buff[sizeof("18446744073709551615")];
52 static _Thread_local fr_sbuff_t sbuff;
53
54 /*
55 * Map string values if possible, and if there's
56 * no match then just pass the original through.
57 *
58 * We count this as validation...
59 */
60 if (kctx->mapping) {
61 fr_table_ptr_sorted_t *mapping = kctx->mapping;
62 size_t mapping_len = *kctx->mapping_len;
63
65 return 0;
66 } else if (fr_type_is_string(type)) {
67 *out = cf_pair_value(cp);
68 return 0;
69 }
70
71 /*
72 * Parse as a box for basic validation
73 */
74 if (cf_pair_to_value_box(NULL, &vb, cp, rule) < 0) return -1;
75
76 /*
77 * In kafka all the time deltas are in ms
78 * resolution, so we need to take the parsed value,
79 * scale it, and print it back to a string.
80 */
81 switch (type) {
83 {
84 uint64_t delta;
85
86 sbuff = FR_SBUFF_OUT(buff, sizeof(buff));
87 delta = fr_time_delta_to_msec(vb.vb_time_delta);
88 if (fr_sbuff_in_sprintf(&sbuff, "%" PRIu64, delta) < 0) {
89 error:
91 return -1;
92 }
93 *out = fr_sbuff_start(&sbuff);
94 }
95 break;
96
97 case FR_TYPE_SIZE:
98 {
99 size_t size = vb.vb_size;
100
101 sbuff = FR_SBUFF_OUT(buff, sizeof(buff));
102
103 /*
104 * Most options are in bytes, but some are in kilobytes
105 */
106 if (kctx->size_scale) size /= kctx->size_scale;
107
108 /*
109 * Kafka doesn't want units...
110 */
111 if (fr_sbuff_in_sprintf(&sbuff, "%zu", size) < 0) goto error;
112 *out = fr_sbuff_start(&sbuff);
113 }
114 break;
115
116 /*
117 * Ensure bool is always mapped to the string constants
118 * "true" or "false".
119 */
120 case FR_TYPE_BOOL:
121 *out = vb.vb_bool ? "true" : "false";
122 break;
123
124 default:
125 *out = cf_pair_value(cp);
126 break;
127 }
128
130
131 return 0;
132}
133
134/** Common dflt path: take a librdkafka-native value string and materialise
135 * it as a CONF_PAIR in the caller's units (time deltas as "Ns", sizes
136 * with unit suffixes, etc.). Invoked by the base and topic dflt funcs.
137 *
138 * @param[out] out Where to write the pair.
139 * @param[in] parent being populated.
140 * @param[in] cs to allocate the pair in.
141 * @param[in] value to convert.
142 * @param[in] quote to use when allocing the pair.
143 * @param[in] rule UNUSED.
144 * @return
145 * - 0 on success.
146 * - -1 on failure.
147 */
149 fr_token_t quote, conf_parser_t const *rule)
150{
151 char tmp[sizeof("18446744073709551615b")];
152 fr_kafka_conf_ctx_t const *kctx = rule->uctx;
153 fr_type_t type = rule->type;
154
155 /*
156 * Apply any mappings available, but default back
157 * to the raw value if we don't have a match.
158 */
159 if (kctx->mapping) {
160 fr_table_ptr_sorted_t *mapping = kctx->mapping;
161 size_t mapping_len = *kctx->mapping_len;
162
164 }
165 /*
166 * Convert time delta as an integer with ms precision
167 */
168 switch (type) {
170 {
171 fr_sbuff_t value_elem = FR_SBUFF_IN(tmp, sizeof(tmp));
172 fr_time_delta_t delta;
173
174 if (fr_time_delta_from_str(&delta, value, strlen(value), FR_TIME_RES_MSEC) < 0) {
175 cf_log_perr(cs, "Failed parsing default \"%s\"", value);
176 return -1;
177 }
178
179 if (fr_time_delta_to_str(&value_elem, delta, FR_TIME_RES_SEC, true) < 0) {
180 cf_log_perr(cs, "Failed formatting \"%s\"", value);
181 return -1;
182 }
183 value = fr_sbuff_start(&value_elem);
184 }
185 break;
186
187 case FR_TYPE_SIZE:
188 {
189 fr_sbuff_t value_elem = FR_SBUFF_IN(tmp, sizeof(tmp));
190 size_t size;
191
192 if (fr_size_from_str(&size, &FR_SBUFF_IN_STR(value)) < 0) {
193 cf_log_perr(cs, "Failed parsing default \"%s\"", value);
194 return -1;
195 }
196
197 /*
198 * Some options are in kbytes *sigh*
199 */
200 if (kctx->size_scale) size *= kctx->size_scale;
201
202 /*
203 * reprint the size with an appropriate unit
204 */
205 if (fr_size_to_str(&value_elem, size) < 0) {
206 cf_log_perr(cs, "Failed size reprint");
207 return -1;
208 }
209 value = fr_sbuff_start(&value_elem);
210 }
211 break;
212
213 default:
214 break;
215 }
216
217 MEM(*out = cf_pair_alloc(cs, rule->name1, value, T_OP_EQ, T_BARE_WORD, quote));
218 cf_item_mark_parsed(*out); /* Don't re-parse this */
219
220 return 0;
221}
222
223/** No-op parser used to reserve CONF_PAIR names inside a topic subsection
224 * that the module reads separately (via call_env), so they aren't caught
225 * by the trailing raw-passthrough catch-all and fed to librdkafka.
226 */
227static int kafka_noop_parse(UNUSED TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *base,
228 UNUSED CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
229{
230 return 0;
231}
232
233/** @} */
234
235/** @name Base conf (`fr_kafka_conf_t`)
236 *
237 * Lifecycle, lazy-init + talloc sentinel, and the FR_CONF_PAIR_GLOBAL parsers for
238 * the top-level `kafka { ... }` section.
239 *
240 * @{
241 */
242
243/** Destructor on the talloc sentinel that owns the rd_kafka_conf_t handle
244 *
245 * The sentinel is just a talloced `rd_kafka_conf_t *` attached to the
246 * caller's parse ctx - when talloc unwinds the instance, this fires and
247 * releases the librdkafka handle.
248 */
249static int _kafka_conf_free(rd_kafka_conf_t **pconf)
250{
251 if (*pconf) rd_kafka_conf_destroy(*pconf);
252 return 0;
253}
254
255/** Fetch the `fr_kafka_conf_t` currently being populated by the parser
256 *
257 * The parser contract is that `base` points at the caller's instance
258 * struct and `fr_kafka_conf_t` is its first member, so a reinterpret
259 * cast of `base` is the `fr_kafka_conf_t`.
260 *
261 * Also lazy-initialises the underlying librdkafka conf the first time
262 * we see it, attaching a talloc sentinel under the parse ctx so the
263 * handle is released when the caller's instance tree unwinds.
264 */
265static fr_kafka_conf_t *kafka_conf_get(TALLOC_CTX *ctx, void *base)
266{
267 fr_kafka_conf_t *kc = base;
268
269 if (!kc) return NULL;
270 if (!kc->conf) {
271 rd_kafka_conf_t **s;
272
273 MEM(kc->conf = rd_kafka_conf_new());
274
275 /*
276 * Attach a sentinel under the parse ctx so teardown
277 * of the caller's instance data automatically releases
278 * the librdkafka handle.
279 */
280 MEM(s = talloc(ctx, rd_kafka_conf_t *));
281 *s = kc->conf;
282 talloc_set_destructor(s, _kafka_conf_free);
283 }
284 return kc;
285}
286
287/** Translate config items directly to settings in a kafka config struct
288 *
289 * @param[in] ctx to allocate fr_kafka_conf_t in.
290 * @param[out] out Unused.
291 * @param[in] base Unused.
292 * @param[in] ci To parse.
293 * @param[in] rule describing how to parse the item.
294 * @return
295 * - 0 on success.
296 * - -1 on failure
297 */
298int kafka_config_parse(TALLOC_CTX *ctx, UNUSED void *out, void *base,
299 CONF_ITEM *ci, conf_parser_t const *rule)
300{
301 fr_kafka_conf_ctx_t const *kctx = rule->uctx;
304 CONF_PAIR *cp = cf_item_to_pair(ci);
305
306 fr_kafka_conf_t *kc;
307 char const *value;
308
309 kc = kafka_conf_get(ctx, base);
310 fr_assert_msg(kc, "kafka base struct missing - caller must embed fr_kafka_conf_t as first member");
311
312 /*
313 * Multi rules require us to concat the values together before handing them off
314 */
315 if (fr_rule_multi(rule)) {
316 unsigned int i;
317 CONF_PAIR *cp_p;
318 size_t count;
319 char const **array;
320 fr_sbuff_t *agg;
321 fr_slen_t slen;
322
323 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
324
325 count = cf_pair_count(cs, rule->name1);
326 if (count <= 1) goto do_single;
327
328 MEM(array = talloc_array(ctx, char const *, count));
329 for (cp_p = cp, i = 0;
330 cp_p;
331 cp_p = cf_pair_find_next(cs, cp_p, rule->name1), i++) {
332 if (kafka_config_parse_single(&array[i], cp_p, rule) < 0) return -1;
334 }
335
336 slen = fr_sbuff_array_concat(agg, array, kctx->string_sep);
337 talloc_free(array);
338 if (slen < 0) return -1;
339
340 value = fr_sbuff_start(agg);
341 } else {
342 do_single:
343 if (kafka_config_parse_single(&value, cp, rule) < 0) return -1;
344 }
345
346 {
347 char errstr[512];
348
349 if (rd_kafka_conf_set(kc->conf, kctx->property,
350 value, errstr, sizeof(errstr)) != RD_KAFKA_CONF_OK) {
351 cf_log_perr(cp, "%s", errstr);
352 return -1;
353 }
354 }
355
356 return 0;
357}
358
359/** Return the default value from the kafka client library
360 *
361 * @param[out] out Where to write the pair.
362 * @param[in] parent being populated.
363 * @param[in] cs to allocate the pair in.
364 * @param[in] quote to use when allocing the pair.
365 * @param[in] rule UNUSED.
366 * @return
367 * - 0 on success.
368 * - -1 on failure.
369 */
371{
372 char buff[1024];
373 size_t buff_len = sizeof(buff);
374 char const *value;
375
376 fr_kafka_conf_t *kc;
377 fr_kafka_conf_ctx_t const *kctx = rule->uctx;
378 rd_kafka_conf_res_t ret;
379
380 kc = kafka_conf_get(cs, parent);
381 fr_assert_msg(kc, "kafka base struct missing during default generation");
382
383 if ((ret = rd_kafka_conf_get(kc->conf, kctx->property, buff, &buff_len)) != RD_KAFKA_CONF_OK) {
384 if (ret == RD_KAFKA_CONF_UNKNOWN) {
385 if (kctx->empty_default) return 0;
386
387 cf_log_debug(cs, "No default available for \"%s\" - \"%s\"", rule->name1, kctx->property);
388 return 0; /* Not an error */
389 }
390
391 cf_log_err(cs, "Failed retrieving kafka property \"%s\"", kctx->property);
392 return -1;
393 }
394#if 0
395 cf_log_debug(cs, "Retrieved dflt \"%s\" for \"%s\" - \"%s\"", buff, rule->name1, kctx->property);
396#endif
397 value = buff;
398
399 /*
400 * If it's multi we need to break the string apart on the string separator
401 * and potentially unescape the separator.
402 */
403 if (fr_rule_multi(rule)) {
404 fr_sbuff_t value_in = FR_SBUFF_IN(value, buff_len);
405 char tmp[256];
406 fr_sbuff_t value_elem = FR_SBUFF_OUT(tmp, sizeof(tmp));
407 /*
408 * FR_SBUFF_TERM() uses sizeof() on its argument, which
409 * produces the wrong length for a runtime pointer. Build
410 * the terminator list by hand so the length is correct.
411 */
412 fr_sbuff_term_elem_t tt_elem = { .str = kctx->string_sep, .len = strlen(kctx->string_sep) };
413 fr_sbuff_term_t tt = { .len = 1, .elem = &tt_elem };
414 fr_sbuff_unescape_rules_t ue_rules = {
415 .name = __FUNCTION__,
416 .chr = '\\'
417 };
418 /*
419 * Convert escaped separators back
420 */
421 ue_rules.subs[(uint8_t)kctx->string_sep[0]] = kctx->string_sep[0];
422
423 while (fr_sbuff_out_unescape_until(&value_elem, &value_in, SIZE_MAX, &tt, &ue_rules) > 0) {
424 if (kafka_config_dflt_single(out, parent, cs, fr_sbuff_start(&value_elem), quote, rule) < 0) return -1;
425
426 /*
427 * Skip past the string separator
428 */
429 fr_sbuff_advance(&value_in, strlen(kctx->string_sep));
430
431 /*
432 * Reset
433 */
434 fr_sbuff_set_to_start(&value_elem);
435 }
436 return 0;
437 }
438
439 /*
440 * Parse a single value
441 */
442 if (kafka_config_dflt_single(out, parent, cs, value, quote, rule) < 0) return -1;
443
444 return 0;
445}
446
447/** Untyped passthrough: hand a CONF_PAIR's attr/value straight to rd_kafka_conf_set
448 *
449 * Used by the `CF_IDENT_ANY` entry in the base `properties { }` subsection
450 * to accept arbitrary librdkafka properties that don't have a typed entry
451 * in `KAFKA_BASE_CONFIG` / `KAFKA_PRODUCER_CONFIG` / `KAFKA_CONSUMER_CONFIG`.
452 * No unit scaling, no bool mapping - the user writes what librdkafka
453 * expects (e.g. "500" for a ms value, "1048576" for a byte count).
454 */
455int kafka_config_raw_parse(TALLOC_CTX *ctx, UNUSED void *out, void *base,
456 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
457{
458 CONF_PAIR *cp = cf_item_to_pair(ci);
459 fr_kafka_conf_t *kc;
460 char errstr[512];
461
462 kc = kafka_conf_get(ctx, base);
463 fr_assert_msg(kc, "kafka base struct missing - caller must embed fr_kafka_conf_t as first member");
464
465 if (rd_kafka_conf_set(kc->conf, cf_pair_attr(cp), cf_pair_value(cp),
466 errstr, sizeof(errstr)) != RD_KAFKA_CONF_OK) {
467 cf_log_perr(cp, "%s", errstr);
468 return -1;
469 }
470 return 0;
471}
472
473/** @} */
474
475/** @name Topic conf (`fr_kafka_topic_conf_t` + `fr_kafka_topic_t`)
476 *
477 * Per-topic lifecycle, FR_CONF_PAIR_GLOBAL parsers for entries inside a declared
478 * topic subsection, and the subsection hook that indexes each declared
479 * topic onto `fr_kafka_conf_t.topics`.
480 *
481 * @{
482 */
483
484/** Destructor on a per-topic conf - releases the librdkafka handle. */
486{
487 if (ktc->rdtc) rd_kafka_topic_conf_destroy(ktc->rdtc);
488 return 0;
489}
490
491/** Allocate a per-topic conf parented under `ctx`
492 *
493 * Used by the subsection hook to build each declared topic's
494 * `fr_kafka_topic_conf_t`. The destructor releases the librdkafka
495 * handle when the owning `fr_kafka_topic_t` is freed.
496 */
498{
500
501 MEM(ktc = talloc(ctx, fr_kafka_topic_conf_t));
502 MEM(ktc->rdtc = rd_kafka_topic_conf_new());
503 talloc_set_destructor(ktc, _kafka_topic_conf_free);
504 return ktc;
505}
506
507/** Translate config items directly to settings in a kafka topic config struct
508 *
509 * `base` is the `fr_kafka_topic_conf_t` the per-topic subsection hook
510 * handed down, so we write directly through it instead of re-fetching
511 * via cf_data. Falls back to cf_data lookup if a caller runs this
512 * parser outside `kafka_topic_subsection_parse`.
513 *
514 * @param[in] ctx UNUSED.
515 * @param[out] out UNUSED.
516 * @param[in] base topic-level conf (`fr_kafka_topic_conf_t *`).
517 * @param[in] ci To parse.
518 * @param[in] rule describing how to parse the item.
519 * @return
520 * - 0 on success.
521 * - -1 on failure
522 */
523static int kafka_topic_config_parse(UNUSED TALLOC_CTX *ctx, UNUSED void *out, void *base,
524 CONF_ITEM *ci, conf_parser_t const *rule)
525{
526 fr_kafka_conf_ctx_t const *kctx = rule->uctx;
527 CONF_PAIR *cp = cf_item_to_pair(ci);
528
530 char const *value;
531
532 fr_assert_msg(ktc, "kafka topic conf missing - topic parser invoked without subsection hook");
533 if (kafka_config_parse_single(&value, cp, rule) < 0) return -1;
534
535 {
536 char errstr[512];
537
538 if (rd_kafka_topic_conf_set(ktc->rdtc, kctx->property,
539 value, errstr, sizeof(errstr)) != RD_KAFKA_CONF_OK) {
540 cf_log_perr(cp, "%s", errstr);
541 return -1;
542 }
543 }
544
545 return 0;
546}
547
548/** Return the default value for a topic from the kafka client library
549 *
550 * @param[out] out Where to write the pair.
551 * @param[in] parent being populated.
552 * @param[in] cs to allocate the pair in.
553 * @param[in] quote to use when allocing the pair.
554 * @param[in] rule UNUSED.
555 * @return
556 * - 0 on success.
557 * - -1 on failure.
558 */
560{
561 char buff[1024];
562 size_t buff_len = sizeof(buff);
563 char const *value;
564
566 fr_kafka_conf_ctx_t const *kctx = rule->uctx;
567 rd_kafka_conf_res_t ret;
568
569 fr_assert_msg(ktc, "kafka topic conf missing during default generation");
570
571 if ((ret = rd_kafka_topic_conf_get(ktc->rdtc, kctx->property, buff, &buff_len)) != RD_KAFKA_CONF_OK) {
572 if (ret == RD_KAFKA_CONF_UNKNOWN) {
573 if (kctx->empty_default) return 0;
574
575 cf_log_debug(cs, "No default available for \"%s\" - \"%s\"", rule->name1, kctx->property);
576 return 0; /* Not an error */
577 }
578
579 cf_log_err(cs, "Failed retrieving kafka property '%s'", kctx->property);
580 return -1;
581 }
582#if 0
583 cf_log_debug(cs, "Retrieved dflt \"%s\" for \"%s\" - \"%s\"", buff, rule->name1, kctx->property);
584#endif
585 value = buff;
586
587 /*
588 * Parse a single value
589 */
590 if (kafka_config_dflt_single(out, parent, cs, value, quote, rule) < 0) return -1;
591
592 return 0;
593}
594
595/** Topic-level counterpart to `kafka_config_raw_parse`
596 *
597 * Used inside a declared topic's `properties { }` subsection to accept
598 * arbitrary `rd_kafka_topic_conf_set` properties. `base` is the enclosing
599 * topic's `fr_kafka_topic_conf_t`, handed down by the subsection hook.
600 */
601int kafka_topic_config_raw_parse(UNUSED TALLOC_CTX *ctx, UNUSED void *out, void *base,
602 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
603{
604 CONF_PAIR *cp = cf_item_to_pair(ci);
606 char errstr[512];
607
608 fr_assert_msg(ktc, "kafka topic conf missing - raw parser invoked without subsection hook");
609
610 if (rd_kafka_topic_conf_set(ktc->rdtc, cf_pair_attr(cp), cf_pair_value(cp),
611 errstr, sizeof(errstr)) != RD_KAFKA_CONF_OK) {
612 cf_log_perr(cp, "%s", errstr);
613 return -1;
614 }
615 return 0;
616}
617
618/** Order-by-name comparator for the `fr_kafka_conf_t.topics` tree. */
619static fr_cmp_ret_t _kafka_topic_cmp(void const *one, void const *two)
620{
621 fr_kafka_topic_t const *a = one;
622 fr_kafka_topic_t const *b = two;
623 return CMP(strcmp(a->name, b->name), 0);
624}
625
627{
629 fr_kafka_topic_t *found = NULL;
630
631 if (!kc || !kc->topics || !name) return NULL;
632 key.name = name;
633 fr_rb_find((void **)&found, kc->topics, &key);
634 return found;
635}
636
637/** Per-topic subsection hook. Runs the inner rules against the topic's
638 * section, then inserts a record into the parent's topics tree.
639 *
640 * Invoked by the framework for each `<name> { ... }` inside `topic { }`.
641 * `ci` is the topic's CONF_SECTION, `base` points at the caller's instance
642 * struct (with `fr_kafka_conf_t` as its first member).
643 */
644int kafka_topic_subsection_parse(TALLOC_CTX *ctx, void *out, void *base,
645 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
646{
647 CONF_SECTION *subcs = cf_item_to_section(ci);
648 fr_kafka_conf_t *kc;
649 fr_kafka_topic_t *topic;
650 char const *name = cf_section_name1(subcs);
651
652 fr_assert_msg(base, "kafka base struct missing");
653
654 kc = kafka_conf_get(ctx, base);
655 if (!kc->topics) {
657 }
658
659 /*
660 * Allocate eagerly so the inner parsers can write into
661 * topic->conf via `base` instead of round-tripping through
662 * cf_data.
663 */
664 MEM(topic = talloc_zero(kc->topics, fr_kafka_topic_t));
665 topic->name = talloc_strdup(topic, name);
666 topic->conf = kafka_topic_conf_alloc(topic);
667 topic->cs = subcs;
668
669 /*
670 * Inner rules (acks, compression, properties, ...) have been
671 * pushed on the subsection by the framework. Run them with
672 * topic->conf as base so they write directly into our struct.
673 */
674 if (cf_section_parse(ctx, topic->conf, subcs) < 0) {
675 talloc_free(topic);
676 return -1;
677 }
678
679 if (fr_rb_insert(kc->topics, topic) != 0) {
680 cf_log_err(ci, "Duplicate kafka topic '%s'", name);
681 talloc_free(topic);
682 return -1;
683 }
684
685 /*
686 * If the caller wired an output target on the subsection
687 * rule, hand back the topic pointer so it lands in their
688 * array. The tree on kc->topics is the primary index;
689 * this is just a convenience for direct-access patterns.
690 */
691 if (out) *((fr_kafka_topic_t **)out) = topic;
692
693 return 0;
694}
695/** @} */
696
697/** @name `conf_parser_t` arrays
698 *
699 * Nested subsections referenced by the `KAFKA_BASE_CONFIG` /
700 * `KAFKA_PRODUCER_CONFIG` / `KAFKA_CONSUMER_CONFIG` macros in base.h.
701 * Base-level surfaces first, then producer-specific, then consumer.
702 *
703 * @{
704 */
705
706/** `properties { ... }` escape-hatch contents
707 *
708 * Accepts any `key = value` pair and hands it straight to
709 * `rd_kafka_conf_set`. See `kafka_config_raw_parse`.
710 */
715
716/** Per-topic `properties { ... }` escape-hatch contents
717 *
718 * Same idea as `kafka_base_properties_config`, but dispatches to
719 * `rd_kafka_topic_conf_set` against the enclosing topic's conf.
720 */
725
728 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.oauthbearer.config", .empty_default = true }},
729
731 .uctx = &(fr_kafka_conf_ctx_t){ .property = "enable.sasl.oauthbearer.unsecure.jwt" }},
732
734};
735
737 /*
738 * Service principal
739 */
741 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.kerberos.service.name" }},
742
743 /*
744 * Principal
745 */
747 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.kerberos.principal" }},
748
749 /*
750 * knit cmd
751 */
753 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.kerberos.kinit.cmd" }},
754
755 /*
756 * keytab
757 */
759 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.kerberos.kinit.keytab", .empty_default = true }},
760
761 /*
762 * How long between key refreshes
763 */
765 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.kerberos.min.time.before.relogin" }},
766
768};
769
771 /*
772 * SASL mechanism
773 */
775 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.mechanism" }},
776
777 /*
778 * Static SASL username
779 */
781 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.username", .empty_default = true }},
782
783 /*
784 * Static SASL password
785 */
787 .uctx = &(fr_kafka_conf_ctx_t){ .property = "sasl.password", .empty_default = true }},
788
790
792
794};
795
797 { L("false"), "none" },
798 { L("no"), "none" },
799 { L("true"), "https" },
800 { L("yes"), "https" }
801};
803
805 /*
806 * Cipher suite list in OpenSSL's format
807 */
809 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.cipher.suites", .empty_default = true }},
810
811 /*
812 * Curves list in OpenSSL's format
813 */
815 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.curves.list", .empty_default = true }},
816
817 /*
818 * Curves list in OpenSSL's format
819 */
821 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.sigalgs.list", .empty_default = true }},
822
823 /*
824 * Sets the full path to a CA certificate (used to validate
825 * the certificate the server presents).
826 */
828 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.ca.location", .empty_default = true }},
829
830 /*
831 * Location of the CRL file.
832 */
834 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.crl.location", .empty_default = true }},
835
836 /*
837 * Sets the path to the public certificate file we present
838 * to the servers.
839 */
841 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.certificate.location", .empty_default = true }},
842
843 /*
844 * Sets the path to the private key for our public
845 * certificate.
846 */
848 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.key.location", .empty_default = true }},
849
850 /*
851 * Enable or disable certificate validation
852 */
854 .uctx = &(fr_kafka_conf_ctx_t){ .property = "enable.ssl.certificate.verification" }},
855
857 .uctx = &(fr_kafka_conf_ctx_t){ .property = "ssl.endpoint.identification.algorithm",
858 .mapping = kafka_check_cert_cn_table,
859 .mapping_len = &kafka_check_cert_cn_table_len }},
861};
862
864 /*
865 * Socket timeout
866 */
868 .uctx = &(fr_kafka_conf_ctx_t){ .property = "socket.timeout.ms" }},
869
870 /*
871 * Close broker connections after this period.
872 */
874 .uctx = &(fr_kafka_conf_ctx_t){ .property = "connections.max.idle.ms" }},
875
876 /*
877 * Maximum requests in flight (per connection).
878 */
880 .uctx = &(fr_kafka_conf_ctx_t){ .property = "max.in.flight.requests.per.connection" }},
881
882 /*
883 * Socket send buffer.
884 */
886 .uctx = &(fr_kafka_conf_ctx_t){ .property = "socket.send.buffer.bytes" }},
887
888 /*
889 * Socket recv buffer.
890 */
892 .uctx = &(fr_kafka_conf_ctx_t){ .property = "socket.receive.buffer.bytes" }},
893
894 /*
895 * If true, send TCP keepalives
896 */
898 .uctx = &(fr_kafka_conf_ctx_t){ .property = "socket.keepalive.enable" }},
899
900 /*
901 * If true, disable nagle algorithm
902 */
904 .uctx = &(fr_kafka_conf_ctx_t){ .property = "socket.nagle.disable" }},
905
906 /*
907 * How long the DNS resolver cache is valid for
908 */
910 .uctx = &(fr_kafka_conf_ctx_t){ .property = "broker.address.ttl" }},
911
912 /*
913 * Should we use A records, AAAA records or either
914 * when resolving broker addresses
915 */
917 .uctx = &(fr_kafka_conf_ctx_t){ .property = "broker.address.family" }},
918
919 /*
920 * How many failures before we reconnect the connection
921 */
922 { FR_CONF_PAIR_GLOBAL("reconnection_failure_count", FR_TYPE_UINT32, 0, kafka_config_parse, kafka_config_dflt),
923 .uctx = &(fr_kafka_conf_ctx_t){ .property = "socket.max.fails" }},
924
925 /*
926 * Initial time to wait before reconnecting.
927 */
929 .uctx = &(fr_kafka_conf_ctx_t){ .property = "reconnect.backoff.ms" }},
930
931 /*
932 * Max time to wait before reconnecting.
933 */
935 .uctx = &(fr_kafka_conf_ctx_t){ .property = "reconnect.backoff.max.ms" }},
936
938};
939
941 /*
942 * Request the API version from connected brokers
943 */
945 .uctx = &(fr_kafka_conf_ctx_t){ .property = "api.version.request" }},
946
947 /*
948 * How long to wait for a version response.
949 */
951 .uctx = &(fr_kafka_conf_ctx_t){ .property = "api.version.request.timeout.ms" }},
952
953 /*
954 * How long to wait before retrying a version request.
955 */
957 .uctx = &(fr_kafka_conf_ctx_t){ .property = "api.version.fallback.ms" }},
958
959 /*
960 * Default version to use if the version request fails.
961 */
963 .uctx = &(fr_kafka_conf_ctx_t){ .property = "broker.version.fallback" }},
964
966};
967
969 /*
970 * Interval between attempts to refresh metadata from brokers
971 */
973 .uctx = &(fr_kafka_conf_ctx_t){ .property = "topic.metadata.refresh.interval.ms" }},
974
975 /*
976 * Interval between attempts to refresh metadata from brokers
977 */
979 .uctx = &(fr_kafka_conf_ctx_t){ .property = "metadata.max.age.ms" }},
980
981 /*
982 * Used when a topic loses its leader
983 */
985 .uctx = &(fr_kafka_conf_ctx_t){ .property = "topic.metadata.refresh.fast.interval.ms" }},
986
987 /*
988 * Used when a topic loses its leader to prevent spurious metadata changes
989 */
991 .uctx = &(fr_kafka_conf_ctx_t){ .property = "topic.metadata.propagation.max.ms" }},
992
993 /*
994 * Use sparse metadata requests which use less bandwidth maps
995 */
997 .uctx = &(fr_kafka_conf_ctx_t){ .property = "topic.metadata.refresh.sparse" }},
998
999 /*
1000 * List of topics to ignore
1001 */
1003 .uctx = &(fr_kafka_conf_ctx_t){ .property = "topic.blacklist", .string_sep = ",", .empty_default = true }},
1004
1006};
1007
1008/** @name Producer-specific topic config
1009 * @{
1010 */
1011
1013 /*
1014 * Payload and key templates for `kafka.produce.<topic>`
1015 * invocations. Parsed at call_env time, but we reserve
1016 * the names here so the raw-passthrough catch-all below
1017 * doesn't try to hand them to rd_kafka_topic_conf_set.
1018 */
1019 { FR_CONF_PAIR_GLOBAL("value", FR_TYPE_STRING, 0, kafka_noop_parse, NULL) },
1021
1022 /*
1023 * This field indicates the number of acknowledgements the leader
1024 * broker must receive from ISR brokers before responding to the request.
1025 */
1027 .uctx = &(fr_kafka_conf_ctx_t){ .property = "request.required.acks" }},
1028
1029 /*
1030 * medium The ack timeout of the producer request in milliseconds
1031 */
1033 .uctx = &(fr_kafka_conf_ctx_t){ .property = "request.timeout.ms" }},
1034
1035 /*
1036 * Local message timeout
1037 */
1039 .uctx = &(fr_kafka_conf_ctx_t){ .property = "message.timeout.ms" }},
1040
1041 /*
1042 * Partitioning strategy
1043 */
1045 .uctx = &(fr_kafka_conf_ctx_t){ .property = "partitioner" }},
1046
1047 /*
1048 * compression codec to use for compressing message sets.
1049 */
1051 .uctx = &(fr_kafka_conf_ctx_t){ .property = "compression.type" }},
1052
1053 /*
1054 * compression level to use
1055 */
1057 .uctx = &(fr_kafka_conf_ctx_t){ .property = "compression.level" }},
1058
1059 /*
1060 * Escape hatch for rd_kafka_topic_conf_set properties not
1061 * covered above. Same shape as the top-level properties
1062 * block but writes to the per-topic conf.
1063 */
1065
1067};
1068
1069/*
1070 * Allows topic configurations in the format:
1071 *
1072 * topic {
1073 * <name> {
1074 * request_required_acks = ...
1075 * }
1076 * }
1077 *
1078 */
1086
1087/* The producer config now lives entirely in the `KAFKA_PRODUCER_CONFIG`
1088 * macro in base.h so callers can compose it with their own config entries.
1089 * See that macro for the full set of librdkafka pass-through properties. */
1090
1091/** @} */
1092
1093/** @name Consumer-specific topic + group config
1094 * @{
1095 */
1096
1098 /*
1099 * Group consumer is a member of
1100 */
1102 .uctx = &(fr_kafka_conf_ctx_t){ .property = "group.id" }},
1103
1104 /*
1105 * A unique identifier of the consumer instance provided by the end user
1106 */
1108 .uctx = &(fr_kafka_conf_ctx_t){ .property = "group.instance.id" }},
1109
1110 /*
1111 * Range or roundrobin
1112 */
1113 { FR_CONF_PAIR_GLOBAL("partition_assignment_strategy", FR_TYPE_STRING, 0, kafka_config_parse, kafka_config_dflt),
1114 .uctx = &(fr_kafka_conf_ctx_t){ .property = "partition.assignment.strategy" }},
1115
1116 /*
1117 * Client group session and failure detection timeout.
1118 */
1120 .uctx = &(fr_kafka_conf_ctx_t){ .property = "session.timeout.ms" }},
1121
1122 /*
1123 * Group session keepalive heartbeat interval.
1124 */
1126 .uctx = &(fr_kafka_conf_ctx_t){ .property = "heartbeat.interval.ms" }},
1127
1128 /*
1129 * How often to query for the current client group coordinator
1130 */
1131 { FR_CONF_PAIR_GLOBAL("coordinator_query_interval", FR_TYPE_TIME_DELTA, 0, kafka_config_parse, kafka_config_dflt),
1132 .uctx = &(fr_kafka_conf_ctx_t){ .property = "coordinator.query.interval.ms" }},
1133
1134
1136};
1137
1139 /*
1140 * How many messages we process at a time
1141 *
1142 * High numbers may starve the worker thread
1143 */
1145 .uctx = &(fr_kafka_conf_ctx_t){ .property = "consume.callback.max.messages" }},
1146
1147 /*
1148 * Action to take when there is no initial offset
1149 * in offset store or the desired offset is out of range.
1150 */
1152 .uctx = &(fr_kafka_conf_ctx_t){ .property = "auto.offset.reset" }},
1153
1154 /*
1155 * Escape hatch for rd_kafka_topic_conf_set properties not
1156 * covered above.
1157 */
1159
1161};
1162
1163/*
1164 * Allows topic configurations in the format:
1165 *
1166 * topic {
1167 * <name> {
1168 * request_required_acks = ...
1169 * }
1170 * }
1171 *
1172 */
1180
1181/* The consumer config now lives in the `KAFKA_CONSUMER_CONFIG` macro in
1182 * base.h so callers can compose it with their own entries. */
1183
1184/** @} */
1185
1186/** @name Library init
1187 *
1188 * librdkafka defers SSL / SASL / internal-refcount setup until the first
1189 * `rd_kafka_new()`. Doing that lazily in a worker thread races the
1190 * server's own OpenSSL init and leaves the ordering non-deterministic,
1191 * so we kick it once at module load via `fr_kafka_init()`. The counter
1192 * mirrors `fr_openssl_init()` in src/lib/tls/base.c.
1193 *
1194 * @{
1195 */
1197
1198static void _kafka_null_log_cb(UNUSED rd_kafka_t const *rk, UNUSED int level,
1199 UNUSED char const *fac, UNUSED char const *buf)
1200{
1201 /* swallow the "no bootstrap brokers" warning from the dummy producer */
1202}
1203
1204/** Drive librdkafka's lazy global init deterministically
1205 *
1206 * First call creates and immediately destroys a throwaway producer, which
1207 * walks all of librdkafka's one-shot init paths (SSL lock callbacks on
1208 * OpenSSL 1.0.2, SASL global init if compiled in, etc.). Subsequent
1209 * calls just bump the refcount so multiple kafka-using modules can share
1210 * the init.
1211 */
1213{
1214 rd_kafka_conf_t *conf;
1215 rd_kafka_t *rk;
1216 char errstr[512];
1217
1218 if (kafka_instance_count > 0) {
1220 return 0;
1221 }
1222
1223 conf = rd_kafka_conf_new();
1224 rd_kafka_conf_set_log_cb(conf, _kafka_null_log_cb);
1225
1226 rk = rd_kafka_new(RD_KAFKA_PRODUCER, conf, errstr, sizeof(errstr));
1227 if (!rk) {
1228 fr_strerror_printf("Failed priming librdkafka globals: %s", errstr);
1229 return -1;
1230 }
1231 rd_kafka_destroy(rk);
1232
1234 return 0;
1235}
1236
1237/** Drop one ref to librdkafka's global init
1238 *
1239 * librdkafka refcounts its own globals internally; our counter just
1240 * pairs fr_kafka_init() calls so re-entrant module load/unload in test
1241 * harnesses does the right thing.
1242 */
1244{
1245 if (kafka_instance_count == 0) return;
1247}
1248
1249/** @} */
1250/** @} */
#define _Thread_local
Definition atexit.h:213
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#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:113
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition cf_parse.c:1289
int cf_pair_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, CONF_PAIR *cp, conf_parser_t const *rule)
Parses a CONF_PAIR into a boxed value.
Definition cf_parse.c:128
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
void const * uctx
User data accessible by the cf_parse_t func.
Definition cf_parse.h:629
#define FR_CONF_PAIR_GLOBAL(_name, _type, _flags, _func, _dflt_func)
conf_parser_t entry which doesn't fill in a pointer or offset, but relies on functions to record valu...
Definition cf_parse.h:385
#define FR_CONF_SUBSECTION_GLOBAL(_name, _flags, _subcs)
conf_parser_t entry which runs conf_parser_t entries for a subsection without any output
Definition cf_parse.h:398
fr_type_t type
An fr_type_t value, controls the output type.
Definition cf_parse.h:610
#define fr_rule_multi(_rule)
Definition cf_parse.h:494
char const * name1
Name of the CONF_ITEM to parse.
Definition cf_parse.h:607
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:446
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
Definition cf_parse.h:433
@ CONF_FLAG_FILE_READABLE
File matching value must exist, and must be readable.
Definition cf_parse.h:435
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
Common header for all CONF_* types.
Definition cf_priv.h:54
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *cs, CONF_PAIR const *prev, char const *attr)
Find a pair with a name matching attr, after specified pair.
Definition cf_util.c:1611
unsigned int cf_pair_count(CONF_SECTION const *cs, char const *attr)
Count the number of times an attribute occurs in a parent section.
Definition cf_util.c:1678
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t op, fr_token_t lhs_quote, fr_token_t rhs_quote)
Allocate a CONF_PAIR.
Definition cf_util.c:1444
char const * cf_section_name1(CONF_SECTION const *cs)
Return the first identifier of a CONF_SECTION.
Definition cf_util.c:1348
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:695
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:675
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1756
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1740
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:345
#define cf_parent(_cf)
Definition cf_util.h:118
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:352
#define cf_log_debug(_cf, _fmt,...)
Definition cf_util.h:348
#define cf_item_mark_parsed(_cf)
Definition cf_util.h:191
#define CF_IDENT_ANY
Definition cf_util.h:80
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:243
#define MEM(x)
Definition debug.h:38
Test enumeration values.
Definition dict_test.h:92
talloc_free(hp)
size_t size_scale
Divide/multiply FR_TYPE_SIZE by this amount.
Definition base.h:84
rd_kafka_conf_t * conf
Definition base.h:49
CONF_SECTION * cs
topic's CONF_SECTION (for call_env lookups of per-topic pairs like value / key)
Definition base.h:69
fr_rb_tree_t * topics
Declared topics, keyed by name.
Definition base.h:51
rd_kafka_topic_conf_t * rdtc
Definition base.h:57
char const * property
Kafka configuration property.
Definition base.h:85
char const * name
as it appeared in config
Definition base.h:67
fr_kafka_topic_conf_t * conf
parsed per-topic librdkafka conf
Definition base.h:68
char const * string_sep
Used for multi-value configuration items.
Definition base.h:86
bool empty_default
Don't produce messages saying the default is missing.
Definition base.h:83
size_t * mapping_len
Length of the mapping tables.
Definition base.h:82
fr_table_ptr_sorted_t * mapping
Mapping table between string constant.
Definition base.h:81
uctx attached to each entry in KAFKA_BASE_PRODUCER_CONFIG
Definition base.h:80
Declared topic record - one per topic { <name> { ... } } subsection.
Definition base.h:66
static fr_cmp_ret_t _kafka_topic_cmp(void const *one, void const *two)
Order-by-name comparator for the fr_kafka_conf_t.topics tree.
Definition base.c:619
void fr_kafka_free(void)
Drop one ref to librdkafka's global init.
Definition base.c:1243
conf_parser_t const kafka_connection_config[]
Definition base.c:863
conf_parser_t const kafka_base_consumer_topics_config[]
Definition base.c:1173
static int kafka_topic_config_dflt(CONF_PAIR **out, void *parent, CONF_SECTION *cs, fr_token_t quote, conf_parser_t const *rule)
Return the default value for a topic from the kafka client library.
Definition base.c:559
conf_parser_t const kafka_base_topic_properties_config[]
Per-topic properties { ... } escape-hatch contents.
Definition base.c:721
static size_t kafka_check_cert_cn_table_len
Definition base.c:802
fr_kafka_topic_t * kafka_topic_conf_find(fr_kafka_conf_t const *kc, char const *name)
Look up a declared topic by name on an fr_kafka_conf_t
Definition base.c:626
static uint32_t kafka_instance_count
Definition base.c:1196
int kafka_config_parse(TALLOC_CTX *ctx, UNUSED void *out, void *base, CONF_ITEM *ci, conf_parser_t const *rule)
Translate config items directly to settings in a kafka config struct.
Definition base.c:298
conf_parser_t const kafka_base_consumer_topic_config[]
Definition base.c:1138
int kafka_config_raw_parse(TALLOC_CTX *ctx, UNUSED void *out, void *base, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
Untyped passthrough: hand a CONF_PAIR's attr/value straight to rd_kafka_conf_set.
Definition base.c:455
int kafka_config_dflt(CONF_PAIR **out, void *parent, CONF_SECTION *cs, fr_token_t quote, conf_parser_t const *rule)
Return the default value from the kafka client library.
Definition base.c:370
static fr_kafka_conf_t * kafka_conf_get(TALLOC_CTX *ctx, void *base)
Fetch the fr_kafka_conf_t currently being populated by the parser.
Definition base.c:265
static int kafka_topic_config_parse(UNUSED TALLOC_CTX *ctx, UNUSED void *out, void *base, CONF_ITEM *ci, conf_parser_t const *rule)
Translate config items directly to settings in a kafka topic config struct.
Definition base.c:523
int fr_kafka_init(void)
Drive librdkafka's lazy global init deterministically.
Definition base.c:1212
int kafka_topic_config_raw_parse(UNUSED TALLOC_CTX *ctx, UNUSED void *out, void *base, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
Topic-level counterpart to kafka_config_raw_parse
Definition base.c:601
conf_parser_t const kafka_base_properties_config[]
properties { ... } escape-hatch contents
Definition base.c:711
static int _kafka_topic_conf_free(fr_kafka_topic_conf_t *ktc)
Destructor on a per-topic conf - releases the librdkafka handle.
Definition base.c:485
static conf_parser_t const kafka_sasl_oauth_config[]
Definition base.c:726
static fr_table_ptr_sorted_t kafka_check_cert_cn_table[]
Definition base.c:796
conf_parser_t const kafka_version_config[]
Definition base.c:940
int kafka_topic_subsection_parse(TALLOC_CTX *ctx, void *out, void *base, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
Per-topic subsection hook.
Definition base.c:644
static int kafka_noop_parse(UNUSED TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *base, UNUSED CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
No-op parser used to reserve CONF_PAIR names inside a topic subsection that the module reads separate...
Definition base.c:227
conf_parser_t const kafka_consumer_group_config[]
Definition base.c:1097
conf_parser_t const kafka_tls_config[]
Definition base.c:804
conf_parser_t const kafka_base_producer_topics_config[]
Definition base.c:1079
static fr_kafka_topic_conf_t * kafka_topic_conf_alloc(TALLOC_CTX *ctx)
Allocate a per-topic conf parented under ctx
Definition base.c:497
static int _kafka_conf_free(rd_kafka_conf_t **pconf)
Destructor on the talloc sentinel that owns the rd_kafka_conf_t handle.
Definition base.c:249
static void _kafka_null_log_cb(UNUSED rd_kafka_t const *rk, UNUSED int level, UNUSED char const *fac, UNUSED char const *buf)
Definition base.c:1198
conf_parser_t const kafka_metadata_config[]
Definition base.c:968
static conf_parser_t const kafka_sasl_kerberos_config[]
Definition base.c:736
static conf_parser_t const kafka_base_producer_topic_config[]
Definition base.c:1012
static int kafka_config_parse_single(char const **out, CONF_PAIR *cp, conf_parser_t const *rule)
Common parse path for a single CONF_PAIR's value.
Definition base.c:46
static int kafka_config_dflt_single(CONF_PAIR **out, UNUSED void *parent, CONF_SECTION *cs, char const *value, fr_token_t quote, conf_parser_t const *rule)
Common dflt path: take a librdkafka-native value string and materialise it as a CONF_PAIR in the call...
Definition base.c:148
conf_parser_t const kafka_sasl_config[]
Definition base.c:770
size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
unsigned int uint32_t
unsigned char uint8_t
ssize_t fr_slen_t
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
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
static char const * name
fr_slen_t fr_sbuff_array_concat(fr_sbuff_t *out, char const *const *array, char const *sep)
Concat an array of strings (not NULL terminated), with a string separator.
Definition sbuff.c:2379
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1611
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_IN(_start, _len_or_end)
char const * str
Terminal string.
Definition sbuff.h:160
char const * name
Name for rule set to aid we debugging.
Definition sbuff.h:209
size_t len
Length of the list.
Definition sbuff.h:170
#define FR_SBUFF_IN_STR(_start)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define FR_SBUFF_OUT(_start, _len_or_end)
char subs[SBUFF_CHAR_CLASS]
Special characters and their substitutions.
Definition sbuff.h:212
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Terminal element with pre-calculated lengths.
Definition sbuff.h:159
Set of terminal elements.
Set of parsing rules for *unescape_until functions.
fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
Parse a size string with optional unit.
Definition size.c:40
fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in)
Print a size string with unit.
Definition size.c:155
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
fr_aka_sim_id_type_t type
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
#define fr_table_str_by_str_value(_table, _str_value, _def)
Brute force search a sorted or ordered ptr table, assuming the pointers are strings.
Definition table.h:657
An element in a lexicographically sorted array of name to ptr mappings.
Definition table.h:65
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
const char * base(const char *p)
Definition testlib.c:55
fr_slen_t fr_time_delta_from_str(fr_time_delta_t *out, char const *in, size_t inlen, fr_time_res_t hint)
Create fr_time_delta_t from a string.
Definition time.c:419
fr_slen_t fr_time_delta_to_str(fr_sbuff_t *out, fr_time_delta_t delta, fr_time_res_t res, bool is_unsigned)
Print fr_time_delta_t to a string with an appropriate suffix.
Definition time.c:447
@ FR_TIME_RES_MSEC
Definition time.h:58
@ FR_TIME_RES_SEC
Definition time.h:50
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition time.h:637
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
enum fr_token fr_token_t
@ T_BARE_WORD
Definition token.h:118
@ T_OP_EQ
Definition token.h:81
static unsigned count
Definition unittest.c:47
static fr_slen_t parent
Definition pair.h:858
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_type_is_string(_x)
Definition types.h:348
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4399
#define FR_VALUE_BOX_INITIALISER_NULL(_vb)
A static initialiser for stack/globally allocated boxes.
Definition value.h:511
static size_t char ** out
Definition value.h:1030