The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
radict.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: d93822134153d8dd3659dd2569a3927e9138d845 $
19 *
20 * @file radict.c
21 * @brief Utility to print attribute data in tab delimited format
22 *
23 * @copyright 2017 The FreeRADIUS server project
24 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 */
26RCSID("$Id: d93822134153d8dd3659dd2569a3927e9138d845 $")
27
28#include <freeradius-devel/util/conf.h>
29#include <freeradius-devel/util/syserror.h>
30#include <freeradius-devel/util/atexit.h>
31#include <freeradius-devel/util/dict_priv.h>
32#include <freeradius-devel/util/hash.h>
33#include <ctype.h>
34#include <dirent.h>
35#include <stdlib.h>
36#include <sys/stat.h>
37#include <unistd.h>
38
39#ifdef HAVE_GETOPT_H
40# include <getopt.h>
41#endif
42
54
55static fr_dict_t *dicts[255];
56static bool print_values = false;
57static bool print_headers = false;
58static bool print_recursive = false;
59static char const *mib = NULL;
60static char const *parent_oid = NULL;
63
64DIAG_OFF(unused-macros)
65#define DEBUG2(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 2)) fr_fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
66#define DEBUG(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 1)) fr_fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
67#define INFO(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 0)) fr_fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
68DIAG_ON(unused-macros)
69
70static void usage(void)
71{
72 fprintf(stderr, "usage: radict [OPTS] [attribute...]\n");
73 fprintf(stderr, " -A Export aliases.\n");
74 fprintf(stderr, " -c Print out in CSV format.\n");
75 fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
76 fprintf(stderr, " -f Export dictionary definitions in the normal dictionary format\n");
77 fprintf(stderr, " -F <format> Set output format. Use 'csv', 'full', or 'dictionary'\n");
78 fprintf(stderr, " -E Export dictionary definitions.\n");
79 fprintf(stderr, " -h Print help text.\n");
80 fprintf(stderr, " -H Show the headers of each field.\n");
81 fprintf(stderr, " -M <name> Mangle names for MIB, and set MIB root.\n");
82 fprintf(stderr, " -O Print the pool size and the bytes used for each protocol dictionary,\n");
83 fprintf(stderr, " and exit with an error if any protocol dictionary overflows its pool.\n");
84 fprintf(stderr, " -p <protocol> Set protocol by name\n");
85 fprintf(stderr, " -P <percent> Measure the memory that each protocol dictionary uses, then set pool= on\n");
86 fprintf(stderr, " the PROTOCOL line of the dictionary to the measured size plus <percent> percent.\n");
87 fprintf(stderr, " -r Write out attributes recursively.\n");
88 fprintf(stderr, " -V Write out all attribute values.\n");
89 fprintf(stderr, " -x Debugging mode.\n");
90 fprintf(stderr, "\n");
91 fprintf(stderr, "Very simple interface to extract attribute definitions from FreeRADIUS dictionaries\n");
92}
93
94static int load_dicts(char const *dict_dir, char const *protocol)
95{
96 int loaded = 0;
97 DIR *dir;
98 struct dirent *dp;
99
100 DEBUG("Reading directory %s", dict_dir);
101
102 dir = opendir(dict_dir);
103 if (!dir) {
104 fr_strerror_printf("Failed opening \"%s\": %s", dict_dir, fr_syserror(errno));
105 return -1;
106 }
107
108 while ((dp = readdir(dir)) != NULL) {
109 struct stat stat_buff;
110 char *file_str;
111
112 if (dp->d_name[0] == '.') continue;
113
114 /*
115 * We only want to load one...
116 */
117 if (protocol && (strcmp(dp->d_name, protocol) != 0)) continue;
118
119 /*
120 * Skip the internal FreeRADIUS dictionary.
121 */
122 if (strcmp(dp->d_name, "freeradius") == 0) continue;
123
124 file_str = talloc_asprintf(NULL, "%s/%s", dict_dir, dp->d_name);
125
126 if (stat(file_str, &stat_buff) == -1) {
127 fr_strerror_printf("Failed stating file \"%s\": %s", file_str, fr_syserror(errno));
128 error:
129 closedir(dir);
130 talloc_free(file_str);
131 return -1;
132 }
133
134 /*
135 * Only process directories
136 */
137 if ((stat_buff.st_mode & S_IFMT) == S_IFDIR) {
138 char *dict_file;
139 struct stat dict_stat_buff;
140 int ret;
141
142 dict_file = talloc_asprintf(NULL, "%s/dictionary", file_str);
143 ret = stat(dict_file, &dict_stat_buff);
144 talloc_free(dict_file);
145
146 /*
147 * If the directory contains a dictionary file,
148 * load it as a dictionary.
149 */
150 if (ret == 0) {
151 if (dict_end >= (dicts + (NUM_ELEMENTS(dicts)))) {
152 fr_strerror_const("Reached maximum number of dictionaries");
153 goto error;
154 }
155
156 DEBUG("Loading dictionary: %s/dictionary", file_str);
157 if (fr_dict_protocol_afrom_file(dict_end, dp->d_name, NULL, __FILE__) < 0) {
158 goto error;
159 }
160 dict_end++;
161 loaded++;
162 }
163
164 /*
165 * For now, don't do sub-protocols.
166 */
167 }
168 talloc_free(file_str);
169 }
170 closedir(dir);
171
172 if (!loaded) {
173 if (!protocol) {
174 fr_strerror_printf("Failed to load any dictionaries");
175 } else {
176 fr_strerror_printf("Failed to load dictionary for protocol %s", protocol);
177 }
178
179 return -1;
180 }
181
182 return 0;
183}
184
185static const char *spaces = " ";
186
187static void da_print_info(fr_dict_t const *dict, fr_dict_attr_t const *da, int depth)
188{
189 char oid_str[512];
190 char flags[256];
191 fr_hash_iter_t iter;
193 fr_sbuff_t old_str_sbuff = FR_SBUFF_OUT(oid_str, sizeof(oid_str));
194 fr_sbuff_t flags_sbuff = FR_SBUFF_OUT(flags, sizeof(flags));
195
196 char const *type;
197 fr_dict_attr_t const *child;
198 fr_hash_table_t *namespace;
199
200 if (fr_dict_attr_oid_print(&old_str_sbuff, NULL, da, false) <= 0) {
201 fr_strerror_printf("OID string too long");
202 fr_exit(EXIT_FAILURE);
203 }
204
205 fr_dict_attr_flags_print(&flags_sbuff, dict, da->type, &da->flags);
206
207 if (!da->flags.is_alias) {
208 type = fr_type_to_str(da->type);
209 } else {
210 fr_assert(da->type == FR_TYPE_VOID);
211 type = "ALIAS";
212 }
213
214 printf("%.*s", depth, spaces);
215
216 /* Protocol Name Type */
217
218 switch(output_format) {
219 case RADICT_OUT_CSV:
220 printf("%s,%s,%s,%u,%s,%s\n",
221 depth == 0 ? fr_dict_root(dict)->name : "",
222 fr_sbuff_start(&old_str_sbuff),
223 da->name,
224 da->attr,
225 type,
226 fr_sbuff_start(&flags_sbuff));
227 break;
228
229 case RADICT_OUT_FANCY:
230 default:
231 printf("%s\t%s\t%s\t%u\t%s\t%s\n",
232 depth == 0 ? fr_dict_root(dict)->name : "",
233 fr_sbuff_start(&old_str_sbuff),
234 da->name,
235 da->attr,
236 type,
237 fr_sbuff_start(&flags_sbuff));
238 }
239
240 if (print_values) {
242
244 if (!ext || !ext->value_by_name) return;
245
246 for (enumv = fr_hash_table_iter_init(ext->value_by_name, &iter);
247 enumv;
248 enumv = fr_hash_table_iter_next(ext->value_by_name, &iter)) {
249 char *str;
250
251 switch(output_format) {
252 case RADICT_OUT_CSV:
253 str = fr_asprintf(NULL, "%s,%s,%s,%u,%s,%s,%s,%pV",
254 depth == 0 ? fr_dict_root(dict)->name : "",
255 fr_sbuff_start(&old_str_sbuff),
256 da->name,
257 da->attr,
258 type,
259 fr_sbuff_start(&flags_sbuff),
260 enumv->name,
261 enumv->value);
262 break;
263
264 case RADICT_OUT_FANCY:
265 default:
266 str = fr_asprintf(NULL, "%s\t%s\t%s\t%u\t%s\t%s\t%s\t%pV",
267 depth == 0 ? fr_dict_root(dict)->name : "",
268 fr_sbuff_start(&old_str_sbuff),
269 da->name,
270 da->attr,
271 type,
272 fr_sbuff_start(&flags_sbuff),
273 enumv->name,
274 enumv->value);
275 }
276
277 printf("%.*s%s\n", depth, spaces, str);
278 talloc_free(str);
279 }
280 }
281
282 /*
283 * Print definitions recursively.
284 */
285 if (!print_recursive || !fr_type_is_structural(da->type)) return;
286
287 namespace = dict_attr_namespace(da);
288 if (!namespace) {
289 fr_assert(0); /* the namespace should always exist */
290 return;
291 }
292
293 for (child = fr_hash_table_iter_init(namespace, &iter);
294 child != NULL;
295 child = fr_hash_table_iter_next(namespace, &iter)) {
296 da_print_info(dict, child, depth + 1);
297 }
298}
299
300static char const *type_to_c_type[] = {
301 [FR_TYPE_STRING] = "char",
302 [FR_TYPE_OCTETS] = "uint8_t",
303
304 [FR_TYPE_IPV4_ADDR] = "struct in_addr",
305 [FR_TYPE_IPV6_ADDR] = "struct in6_addr",
306
307// [FR_TYPE_IFID] = "fr_ifid_t",
308// [FR_TYPE_ETHERNET] = "fr_ethernet_t",
309
310 [FR_TYPE_UINT8] = "uint8_t",
311 [FR_TYPE_UINT16] = "uint16_t",
312 [FR_TYPE_UINT32] = "uint32_t",
313 [FR_TYPE_UINT64] = "uint64_t",
314
315 [FR_TYPE_INT8] = "int8_t",
316 [FR_TYPE_INT16] = "int16_t",
317 [FR_TYPE_INT32] = "int32_t",
318 [FR_TYPE_INT64] = "int64_t",
319
320 [FR_TYPE_DATE] = "fr_time_t",
321 [FR_TYPE_TIME_DELTA] = "fr_time_delta_t",
322
323 [FR_TYPE_MAX] = 0 //!< Ensure array covers all types.
324};
325
326static char const *length_to_c_type[] = {
327 [2] = "uint16_t",
328 [4] = "uint32_t",
329 [8] = "uint64_t",
330};
331
332static void da_normalize_name(fr_dict_attr_t const *da, char buffer[static FR_DICT_ATTR_MAX_NAME_LEN*2 + 1])
333{
334 char const *start = da->name;
335 char const *p;
336 char *q;
337 bool mangle = false;
338
339 /*
340 * The RADIUS MIBs have lots of repetition. So we do some simple mangling of the names to make
341 * them easier to understand.
342 */
343 if (mib && da->parent) {
344 size_t len;
345
346 len = strlen(da->parent->name);
347
348 /*
349 * "radiusAuthServer" and "radiusAuthServTotalAccessRejects"
350 * to "total_access_rejects"
351 *
352 * Otherwise "radiusAuthServer" in the "RADIUS" dictionary, to "auth_server"
353 */
354 mangle = (strncmp(da->parent->name, da->name, len) == 0);
355 if (!mangle) {
356 fr_dict_attr_t const *root = fr_dict_root(da->dict);
357
358 len = strlen(root->name);
359 mangle = (strncasecmp(root->name, da->name, len) == 0);
360 }
361
362 if (mangle) start += len;
363 }
364
365 q = buffer;
366
367 for (p = start; *p != '\0'; p++) {
368 if ((*p >= '0') && (*p <= '9')) {
369 *q++ = *p;
370 continue;
371 }
372
373 if (islower((unsigned int) *p)) {
374 *q++ = *p;
375 continue;
376 }
377
378 if (isupper((unsigned int) *p)) {
379 if (mangle && (p > start)) {
380 *(q++) = '_';
381 }
382
383 *q++ = tolower((unsigned int)*p);
384 continue;
385 }
386
387 *q++ = '_';
388 }
389
390 *q = '\0';
391}
392
393static void da_print_name(FILE *fp, fr_dict_attr_t const *da)
394{
396
398 fprintf(fp, "%s", buffer);
399}
400
401static const bool type_allowed[FR_TYPE_MAX] = {
402 [FR_TYPE_STRING] = true,
403 [FR_TYPE_OCTETS] = true,
404
405 [FR_TYPE_UINT16] = true,
406 [FR_TYPE_UINT32] = true,
407 [FR_TYPE_UINT64] = true,
408
409 [FR_TYPE_IPV4_ADDR] = true,
410 [FR_TYPE_IPV6_ADDR] = true,
411
412 [FR_TYPE_DATE] = true,
413 [FR_TYPE_TIME_DELTA] = true,
414
415};
416
418{
419 int i;
420 fr_dict_attr_t const *da;
421
422 for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
423 if (!type_allowed[da->type]) return false;
424 }
425
426 return true;
427}
428
429#define CHECK_TYPE(_parent) \
430do { \
431 if ((parent->type != FR_TYPE_STRUCT) && (parent->type != FR_TYPE_TLV)) { \
432 fprintf(stderr, "%s is not a struct or tlv\n", parent->name); \
433 return; \
434 } \
435 if (!children_ok(parent)) fr_exit(EXIT_FAILURE); \
436} while (0)
437
438/** Print structures and mappings, mainly for statistics.
439 */
440static void da_print_struct(FILE *fp, fr_dict_attr_t const *parent)
441{
442 int i;
443 fr_dict_attr_t const *da;
444
446
447 /*
448 * @todo - print full OID path and filename?
449 */
450 fprintf(fp, "/*\n *\t%s\n */\n", parent->name);
451 fprintf(fp, "typedef struct {\n");
452
453 for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
454 unsigned int length = 0;
455
456 /*
457 * @todo - if the last field is a union, print out the union definitions first.
458 */
459 fr_assert(!da->flags.array);
460
461 if (da_is_bit_field(da)) {
462 /*
463 * @todo - this is all big endian. for little endian, we print out the bytes in
464 * order, but the bits in each byte are reversed. Likely the easiest way to do
465 * this is via a separate function that we call. But this shouldn't be necessary
466 * for statistics structures, as they shouldn't contain bitfields.
467 */
468 fprintf(fp, "\tunsigned int : %u\t", da->flags.length);
469
470 } else switch (da->type) {
471 case FR_TYPE_STRING:
472 if ((parent->type == FR_TYPE_TLV) && !da->flags.length) {
473 fprintf(fp, "\t%s\t*", type_to_c_type[da->type]);
474 break;
475 }
477
478 case FR_TYPE_OCTETS:
479 fr_assert(da->flags.length > 0);
480 length = da->flags.length;
481 fprintf(fp, "\t%s\t", type_to_c_type[da->type]);
482 break;
483
484 case FR_TYPE_DATE:
485 fr_assert(da->flags.length <= 8);
486 fr_assert(length_to_c_type[da->flags.length] != NULL);
487 fprintf(fp, "\t%s\t", length_to_c_type[da->flags.length]);
488 break;
489
490 default:
491 fr_assert(type_to_c_type[da->type] != NULL);
492 fprintf(fp, "\t%s\t", type_to_c_type[da->type]);
493 break;
494 }
495
496 da_print_name(fp, da);
497
498 if (length) {
499 fprintf(fp, "[%u]", length);
500 }
501
502 fprintf(fp, ";\n");
503 }
504
505 fprintf(fp, "} ");
506
507 fprintf(fp, "fr_stats_");
509 fprintf(fp, "_");
511 fprintf(fp, "_t;\n");
512}
513
514static void da_print_base_c_da_def(FILE *fp, fr_dict_attr_t const *parent)
515{
516 int i;
517 fr_dict_attr_t const *da;
519
521
523
524 fprintf(fp, "static fr_dict_attr_t const *attr_%s;\n", parent_name);
525
526 for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
527 fprintf(fp, "static fr_dict_attr_t const *attr_%s_", parent_name);
528 da_print_name(fp, da);
529 fprintf(fp, ";\n");
530 }
531
532 fprintf(fp, "\n\n");
533}
534
535
536/** Map data types to enum names representing those types
537 */
538#define ENUM_NAME(_x) [_x] = STRINGIFY(_x)
539
584
585static void da_print_stats_link(FILE *fp, fr_dict_attr_t const *parent)
586{
587 int i, num_elements = 0;
588 fr_dict_attr_t const *da;
589 char dict_name[FR_DICT_ATTR_MAX_NAME_LEN*2 + 1];
591
593
594 da_normalize_name(fr_dict_root(parent->dict), dict_name);
596
597 fprintf(fp, "fr_stats_link_t const fr_stats_link_%s_%s = {\n", dict_name, parent_name);
598
599 fprintf(fp, "\t.name = \"fr_stats_%s_%s_t\",\n", dict_name, parent_name);
600 fprintf(fp, "\t.root_p = &attr_%s,\n", parent_name);
601 if (mib) fprintf(fp, "\t.mib = \"%s\",\n", mib);
602 fprintf(fp, "\t.size = sizeof(fr_stats_%s_%s_t),\n", dict_name, parent_name);
603
604 for (i = 1; fr_dict_attr_child_by_num(parent, i) != NULL; i++) {
605 num_elements = i;
606 }
607 fprintf(fp, "\t.num_elements = %d,\n", num_elements);
608
609 fprintf(fp, "\t.entry = {\n");
610
611 /*
612 * For locality, also print out data type and size. That way we _can_ dereference the da, but we
613 * don't _need_ to.
614 */
615 for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
616 fprintf(fp, "\t\t{\n");
617 fprintf(fp, "\t\t\t.da_p = &attr_%s_", parent_name);
618 da_print_name(fp, da);
619 fprintf(fp, ",\n");
620
621 fprintf(fp, "\t\t\t.type = %s,\n", fr_type_to_enum_name[da->type]);
622
623 fprintf(fp, "\t\t\t.offset = offsetof(fr_stats_%s_%s_t, ", dict_name, parent_name);
624 da_print_name(fp, da);
625 fprintf(fp, "),\n");
626
627 fprintf(fp, "\t\t\t.size = %u,\n", da->flags.length);
628
629 fprintf(fp, "\t\t},\n");
630 }
631
632 fprintf(fp, "\t},\n");
633 fprintf(fp, "};\n\n");
634}
635
636static void da_print_attr_autoload(FILE *fp, fr_dict_attr_t const *parent)
637{
638 int i;
639 fr_dict_attr_t const *da;
640 char dict_name[FR_DICT_ATTR_MAX_NAME_LEN*2 + 1];
642
644
645 da_normalize_name(fr_dict_root(parent->dict), dict_name);
647
648 /*
649 * Define the parent.
650 */
651 fprintf(fp, "{ .out = &attr_%s, .name = \"%s\", .type = %s, .dict = &dict_%s },\n",
653
654 /*
655 * And each child
656 */
657 for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
658 fprintf(fp, "{ .out = &attr_%s_", parent_name);
659 da_print_name(fp, da);
660 fprintf(fp, ", .name = \".%s\", .type = %s, .dict = &dict_%s },\n",
661 da->name, fr_type_to_enum_name[da->type], dict_name);
662 }
663
664 fprintf(fp, "\n\n");
665}
666
667static void da_print_stats_h(FILE *fp, fr_dict_attr_t const *parent)
668{
669 char dict_name[FR_DICT_ATTR_MAX_NAME_LEN*2 + 1];
671
673
674 da_normalize_name(fr_dict_root(parent->dict), dict_name);
676
678
679 fprintf(fp, "\n");
680
681 fprintf(fp, "/*\n * fr_stats_%s_%s_instance_t\n */\n", dict_name, parent_name);
682 fprintf(fp, "FR_STATS_TYPEDEF(%s_%s);\n\n", dict_name, parent_name);
683
684 fprintf(fp, "extern fr_stats_link_t const fr_stats_link_%s_%s;\n\n", dict_name, parent_name);
685}
686
687
688/** Totals gathered while exporting a dictionary
689 */
690typedef struct {
691 uint64_t count; //!< Attributes exported.
692 uintptr_t low; //!< Lowest attribute address seen.
693 uintptr_t high; //!< Highest attribute address seen.
694 bool found; //!< `low` and `high` hold an address.
696
697static void raddict_export(raddict_stats_t *stats, fr_dict_t const *dict, fr_dict_attr_t const *da)
698{
699 unsigned int i;
700 size_t len;
701 fr_dict_attr_t const *p;
702 char flags[256];
703 fr_dict_attr_t const **children;
704
705 fr_dict_attr_flags_print(&FR_SBUFF_OUT(flags, sizeof(flags)), dict, da->type, &da->flags);
706
707 /*
708 * Root attributes are allocated outside of the pool
709 * so it's not helpful to include them in the calculation.
710 */
711 if (!da->flags.is_root) {
712 uintptr_t addr = (uintptr_t)da;
713
714 if (!stats->found || (addr < stats->low)) {
715 stats->low = addr;
716 }
717 if (!stats->found || (addr > stats->high)) {
718 stats->high = addr;
719 }
720 stats->found = true;
721
722 da_print_info(fr_dict_by_da(da), da, 0);
723 }
724
725 stats->count++;
726
727 /*
728 * Todo - Should be fixed to use attribute walking API
729 */
730 children = dict_attr_children(da);
731 if (children) {
732 len = talloc_array_length(children);
733 for (i = 0; i < len; i++) {
734 for (p = children[i]; p; p = p->next) {
735 raddict_export(stats, dict, p);
736 }
737 }
738 }
739}
740
742 { L("fancy"), RADICT_OUT_FANCY },
743 { L("csv"), RADICT_OUT_CSV },
744 { L("dict"), RADICT_OUT_DICT },
745 { L("struct"), RADICT_OUT_STRUCT },
746 { L("stats_link"), RADICT_OUT_STATS_LINK },
747 { L("da_def"), RADICT_OUT_BASE_C_DA_DEF },
748 { L("attr_autoload"), RADICT_OUT_ATTR_AUTOLOAD },
749 { L("stats.h"), RADICT_OUT_STATS_H },
750};
752
754 { L("fancy"), NULL },
755 { L("csv"), NULL },
756 { L("dict"), NULL },
757 { L("struct"), (void *) da_print_struct },
758 { L("stats_link"), (void *) da_print_stats_link },
759 { L("da_def"), (void *) da_print_base_c_da_def },
760 { L("attr_autoload"), (void *) da_print_attr_autoload },
761 { L("stats.h"), (void *) da_print_stats_h },
762};
764
765typedef void (*da_print_func_t)(FILE *fp, fr_dict_attr_t const *da);
766
767
768/** Largest pool that radict -P loads a dictionary with
769 *
770 * radict -P reports an error, rather than reloading, when the next pool would
771 * be larger than RADICT_POOL_MAX_SIZE.
772 */
773#define RADICT_POOL_MAX_SIZE ((size_t)64 * 1000 * 1000)
774
775/** Bytes that radict -P adds to the largest overflowed pool to set the minimum pool size for the next reload
776 */
777#define RADICT_POOL_STEP_SIZE ((size_t)10 * 1000 * 1000)
778
779/** Find the size of the largest overflowed pool among the loaded protocol dictionaries
780 *
781 * @param[in] gctx holding the loaded dictionaries.
782 * @return
783 * - 0 if no dictionary overflowed the dictionary's pool.
784 * - The pool size, in bytes, of the largest pool that overflowed.
785 */
787{
788 fr_hash_iter_t iter;
790 size_t overflow_max = 0;
791
792 for (dict = fr_hash_table_iter_init(gctx->protocol_by_name, &iter);
793 dict;
795 bool overflowed;
796 size_t live;
797
798 (void) talloc_pool_used(&overflowed, &live, dict->pool, dict->pool_size);
799 if (!overflowed) continue;
800
801 DEBUG("%s overflowed a %pV pool", fr_dict_root(dict)->name, fr_box_size(dict->pool_size));
802 if (dict->pool_size > overflow_max) overflow_max = dict->pool_size;
803 }
804
805 return overflow_max;
806}
807
808/** Find the next whitespace-separated field on a dictionary line
809 *
810 * @param[out] field_end First byte after the field.
811 * @param[in] p Position on the line to start the search from.
812 * @param[in] end End of the line.
813 * @return
814 * - The start of the field.
815 * - NULL if no field remains before end or before a comment.
816 */
817static char const *line_field_next(char const **field_end, char const *p, char const *end)
818{
819 while ((p < end) && isspace((uint8_t)*p)) {
820 p++;
821 }
822 if ((p == end) || (*p == '#')) return NULL;
823
824 *field_end = p;
825 while ((*field_end < end) && !isspace((uint8_t)**field_end)) {
826 (*field_end)++;
827 }
828
829 return p;
830}
831
832/** Check whether a field is a dictionary keyword
833 */
834static bool line_field_is(char const *field, char const *field_end, char const *keyword)
835{
836 size_t len = strlen(keyword);
837
838 return ((size_t)(field_end - field) == len) && (strncasecmp(field, keyword, len) == 0);
839}
840
841/** Write data to a temporary file, then rename the temporary file over filename
842 */
843static int file_replace(char const *filename, mode_t mode, char const *data, size_t len)
844{
845 char *tmp;
846 int fd;
847 mode_t old_umask;
848
849 MEM(tmp = talloc_asprintf(NULL, "%s.XXXXXX", filename));
850 old_umask = umask(S_IRWXG | S_IRWXO);
851 fd = mkstemp(tmp);
852 umask(old_umask);
853 if (fd < 0) {
854 fr_strerror_printf("Failed creating \"%s\": %s", tmp, fr_syserror(errno));
855 talloc_free(tmp);
856 return -1;
857 }
858
859 if (fchmod(fd, mode & 07777) < 0) {
860 fr_strerror_printf("Failed setting the mode of \"%s\": %s", tmp, fr_syserror(errno));
861 error:
862 close(fd);
863 unlink(tmp);
864 talloc_free(tmp);
865 return -1;
866 }
867
868 while (len > 0) {
869 ssize_t slen;
870
871 slen = write(fd, data, len);
872 if (slen < 0) {
873 if (errno == EINTR) continue;
874 fr_strerror_printf("Failed writing \"%s\": %s", tmp, fr_syserror(errno));
875 goto error;
876 }
877 data += slen;
878 len -= slen;
879 }
880
881 if (close(fd) < 0) {
882 fr_strerror_printf("Failed writing \"%s\": %s", tmp, fr_syserror(errno));
883 unlink(tmp);
884 talloc_free(tmp);
885 return -1;
886 }
887
888 if (rename(tmp, filename) < 0) {
889 fr_strerror_printf("Failed renaming \"%s\" to \"%s\": %s", tmp, filename, fr_syserror(errno));
890 unlink(tmp);
891 talloc_free(tmp);
892 return -1;
893 }
894
895 talloc_free(tmp);
896 return 0;
897}
898
899/** Set pool=<size> on the PROTOCOL line that defines a dictionary
900 *
901 * protocol_line_pool_set() replaces any pool= option already on the PROTOCOL
902 * line, and keeps every other option, the whitespace between fields, and
903 * any comment.
904 *
905 * Worked example: BEGIN PROTOCOL RADIUS 1 verify=lib
906 * keyword: BEGIN PROTOCOL, name: RADIUS, number: 1, options: verify=lib
907 * The line becomes: BEGIN PROTOCOL RADIUS 1 verify=lib,pool=12MB
908 *
909 * @param[in] dict whose PROTOCOL line to rewrite.
910 * @param[in] pool_mb New pool size in megabytes (MB) of 1,000,000 bytes.
911 * @return
912 * - 0 on success.
913 * - -1 on failure.
914 */
915static int protocol_line_pool_set(fr_dict_t const *dict, size_t pool_mb)
916{
917 fr_dict_attr_t const *root = fr_dict_root(dict);
918 char const *filename = fr_dict_attr_filename(root);
919 FILE *fp;
920 struct stat st;
921 char *buf;
922 size_t len;
923 char const *buf_end, *line, *line_end;
924 char const *field, *field_end, *number_end, *opts, *opts_end = NULL;
925 char *new_opts, *out;
926 unsigned int i;
927 int ret = -1;
928
929 fp = fopen(filename, "r");
930 if (!fp) {
931 fr_strerror_printf("Failed opening \"%s\": %s", filename, fr_syserror(errno));
932 return -1;
933 }
934 if (fstat(fileno(fp), &st) < 0) {
935 fr_strerror_printf("Failed reading \"%s\": %s", filename, fr_syserror(errno));
936 fclose(fp);
937 return -1;
938 }
939
940 MEM(buf = talloc_array(NULL, char, st.st_size + 1));
941 len = fread(buf, 1, st.st_size, fp);
942 if (ferror(fp) || (len != (size_t)st.st_size)) {
943 fr_strerror_printf("Failed reading \"%s\"", filename);
944 fclose(fp);
945 goto finish;
946 }
947 fclose(fp);
948 buf[len] = '\0';
949 buf_end = buf + len;
950
951 /*
952 * 1. Find the PROTOCOL line. The dictionary loader records the number of
953 * the PROTOCOL line in root->line.
954 */
955 line = buf;
956 for (i = 1; i < root->line; i++) {
957 char const *nl;
958
959 nl = memchr(line, '\n', buf_end - line);
960 if (!nl) {
961 fr_strerror_printf("Failed finding the PROTOCOL line, \"%s\" has fewer than %u lines",
962 filename, root->line);
963 goto finish;
964 }
965 line = nl + 1;
966 }
967 line_end = memchr(line, '\n', buf_end - line);
968 if (!line_end) line_end = buf_end;
969
970 /*
971 * 2. Check that the line from step 1 has the form [BEGIN] PROTOCOL <name> <number>.
972 */
973 field = line_field_next(&field_end, line, line_end);
974 if (field && line_field_is(field, field_end, "BEGIN")) field = line_field_next(&field_end, field_end, line_end);
975 if (!field || !line_field_is(field, field_end, "PROTOCOL") ||
976 !line_field_next(&field_end, field_end, line_end) || /* name */
977 !line_field_next(&field_end, field_end, line_end)) { /* number */
978 fr_strerror_printf("Failed finding the PROTOCOL line for %s, %s[%u] does not match "
979 "[BEGIN] PROTOCOL <name> <number>", root->name, filename, root->line);
980 goto finish;
981 }
982 number_end = field_end;
983
984 /*
985 * 3. Copy every option except pool= from the line from step 1, then append pool=<pool_mb>MB.
986 */
987 MEM(new_opts = talloc_strdup(buf, ""));
988 opts = line_field_next(&opts_end, number_end, line_end);
989 if (opts) {
990 char const *p, *q;
991
992 for (p = opts; p < opts_end; p = q + 1) {
993 q = memchr(p, ',', opts_end - p);
994 if (!q) q = opts_end;
995
996 if (((q - p) >= 5) && (strncmp(p, "pool=", 5) == 0)) continue;
997
998 MEM(new_opts = talloc_asprintf_append_buffer(new_opts, "%s%.*s",
999 new_opts[0] ? "," : "", (int)(q - p), p));
1000 }
1001 }
1002 MEM(new_opts = talloc_asprintf_append_buffer(new_opts, "%spool=%zuMB", new_opts[0] ? "," : "", pool_mb));
1003
1004 /*
1005 * 4. Replace the options field of the line with the options from step 3.
1006 * When the line has no options field, insert the options from step 3
1007 * after the number from step 2. Then write the result over the file.
1008 */
1009 if (opts) {
1010 MEM(out = talloc_asprintf(buf, "%.*s%s%s",
1011 (int)(opts - buf), buf, new_opts, opts_end));
1012 } else {
1013 MEM(out = talloc_asprintf(buf, "%.*s\t%s%s",
1014 (int)(number_end - buf), buf, new_opts, number_end));
1015 }
1016
1017 ret = file_replace(filename, st.st_mode, out, talloc_strlen(out));
1018
1019finish:
1020 talloc_free(buf);
1021 return ret;
1022}
1023
1024/** Set pool= on the PROTOCOL line of every loaded protocol dictionary
1025 *
1026 * The new pool= is the measured size plus overhead percent, rounded up to
1027 * whole megabytes (MB) of 1,000,000 bytes. The overhead covers space that
1028 * loading used at the top of the pool and freed again, which
1029 * talloc_pool_used() does not count.
1030 *
1031 * Call protocol_lines_pool_set() only after dicts_pool_grow() returns 0, so
1032 * that no pool has overflowed.
1033 *
1034 * @param[in] gctx holding the loaded dictionaries.
1035 * @param[in] overhead Percentage to add to the measured size.
1036 * @return
1037 * - 0 on success.
1038 * - -1 if radict failed to rewrite any dictionary.
1039 */
1040static int protocol_lines_pool_set(fr_dict_gctx_t *gctx, unsigned int overhead)
1041{
1042 fr_hash_iter_t iter;
1043 fr_dict_t *dict;
1044 int ret = 0;
1045
1046 for (dict = fr_hash_table_iter_init(gctx->protocol_by_name, &iter);
1047 dict;
1049 fr_dict_attr_t const *root = fr_dict_root(dict);
1050 size_t used, live, pool_mb;
1051 bool overflowed;
1052
1053 /*
1054 * Skip any dictionary that no PROTOCOL line defines, such as the
1055 * internal dictionary. radict has no line to set pool= on.
1056 */
1057 if (root->file == 0) continue;
1058
1059 /*
1060 * dicts_pool_grow() reloaded the dictionaries until no pool
1061 * overflowed, so used is the number of bytes that the
1062 * dictionary needs.
1063 */
1064 used = talloc_pool_used(&overflowed, &live, dict->pool, dict->pool_size);
1065 if (!fr_cond_assert_msg(!overflowed, "%s overflowed its pool", root->name)) {
1066 ret = -1;
1067 continue;
1068 }
1069
1070 pool_mb = ((used + (used * overhead) / 100) + (1000 * 1000) - 1) / (1000 * 1000);
1071
1072 fr_fprintf(stdout, "%s\t%s[%u]\tused %pV\tpool=%zuMB\n",
1073 root->name, fr_dict_attr_filename(root), root->line, fr_box_size(used), pool_mb);
1074
1075 if (protocol_line_pool_set(dict, pool_mb) < 0) {
1076 fr_perror("radict");
1077 ret = -1;
1078 }
1079 }
1080
1081 return ret;
1082}
1083
1084/** Print the pool size and the bytes used for every loaded protocol dictionary
1085 *
1086 * @param[in] gctx holding the loaded dictionaries.
1087 * @return
1088 * - 0 if no protocol dictionary overflowed its pool.
1089 * - -1 if any protocol dictionary overflowed its pool.
1090 */
1092{
1093 fr_hash_iter_t iter;
1094 fr_dict_t *dict;
1095 int ret = 0;
1096
1097 for (dict = fr_hash_table_iter_init(gctx->protocol_by_name, &iter);
1098 dict;
1100 fr_dict_attr_t const *root = fr_dict_root(dict);
1101 size_t used, live;
1102 bool overflowed;
1103
1104 /*
1105 * Skip any dictionary that no PROTOCOL line defines, such as the
1106 * internal dictionary. pool= cannot resize its pool.
1107 */
1108 if (root->file == 0) continue;
1109
1110 used = talloc_pool_used(&overflowed, &live, dict->pool, dict->pool_size);
1111 if (!overflowed) {
1112 fr_fprintf(stdout, "%s\t%s[%u]\tpool %pV\tused %pV\tlive %pV\tholes %pV\ttail %pV\n",
1113 root->name, fr_dict_attr_filename(root), root->line, fr_box_size(dict->pool_size),
1114 fr_box_size(used), fr_box_size(live), fr_box_size(used - live),
1115 fr_box_size(dict->pool_size - used));
1116 continue;
1117 }
1118
1119 fr_fprintf(stdout, "%s\t%s[%u]\tpool %pV\toverflowed\n",
1120 root->name, fr_dict_attr_filename(root), root->line, fr_box_size(dict->pool_size));
1121 fr_strerror_printf("%s overflowed its %pV pool, run radict -P to set a larger pool= on %s[%u]",
1122 root->name, fr_box_size(dict->pool_size), fr_dict_attr_filename(root), root->line);
1123 fr_perror("radict");
1124 ret = -1;
1125 }
1126
1127 return ret;
1128}
1129
1130/** Load the internal dictionary, then the protocol dictionaries
1131 *
1132 * @param[out] gctx_out The new global dictionary context.
1133 * @param[in] dict_dir to load the dictionaries from.
1134 * @param[in] protocol to load, or NULL to load every protocol.
1135 * @param[in] pool_size_min Smallest pool, in bytes, that dict_alloc() creates.
1136 * @return
1137 * - 0 on success.
1138 * - -1 on failure.
1139 */
1140static int dicts_load(fr_dict_gctx_t **gctx_out, char const *dict_dir, char const *protocol, size_t pool_size_min)
1141{
1142 fr_dict_gctx_t *gctx;
1143
1144 gctx = fr_dict_global_ctx_init(NULL, true, dict_dir);
1145 if (!gctx) {
1146 fr_perror("radict - Global context init failed");
1147 return -1;
1148 }
1149 gctx->pool_size_min = pool_size_min;
1150 *gctx_out = gctx;
1151
1152 DEBUG("Loading dictionary: %s/%s", dict_dir, FR_DICTIONARY_FILE);
1153
1155 fr_perror("radict - Loading internal dictionary failed");
1156 return -1;
1157 }
1158
1159 /*
1160 * Don't emit spurious errors...
1161 */
1163 if (load_dicts(dict_dir, protocol) < 0) {
1164 fr_perror("radict - Loading dictionaries failed");
1165 return -1;
1166 }
1167
1168 return 0;
1169}
1170
1171/** Release radict's references to the dictionaries, then free the global dictionary context
1172 *
1173 * fr_dict_free() does not set the entry to NULL, so dicts_free() does. The
1174 * release at the end of main() always visits dicts[0], even after a failed
1175 * reload, and fr_dict_free() ignores a NULL entry.
1176 *
1177 * @param[in] gctx to free.
1178 * @return
1179 * - 0 on success.
1180 * - -1 on failure.
1181 */
1183{
1184 fr_dict_t **dict_p;
1185
1186 for (dict_p = dicts; dict_p < dict_end; dict_p++) {
1187 fr_dict_free(dict_p, __FILE__);
1188 *dict_p = NULL;
1189 }
1190 dict_end = dicts;
1191
1192 if (fr_dict_global_ctx_free(gctx) < 0) {
1193 fr_perror("radict - Error freeing dictionaries");
1194 return -1;
1195 }
1196
1197 return 0;
1198}
1199
1200/** Reload the dictionaries with larger pools until no pool overflows
1201 *
1202 * Each reload sets the minimum pool size to the size of the largest
1203 * overflowed pool plus RADICT_POOL_STEP_SIZE bytes.
1204 *
1205 * @param[in,out] gctx_p Global dictionary context. dicts_pool_grow() replaces
1206 * *gctx_p on each reload.
1207 * @param[in] dict_dir to load the dictionaries from.
1208 * @param[in] protocol to load, or NULL to load every protocol.
1209 * @return
1210 * - 0 when no pool overflows.
1211 * - -1 if the next pool would be larger than RADICT_POOL_MAX_SIZE, or if a
1212 * reload fails.
1213 */
1214static int dicts_pool_grow(fr_dict_gctx_t **gctx_p, char const *dict_dir, char const *protocol)
1215{
1216 size_t overflow_max;
1217
1218 while ((overflow_max = dicts_overflow_max(*gctx_p)) > 0) {
1219 size_t pool_size_min = overflow_max + RADICT_POOL_STEP_SIZE;
1220
1221 if (pool_size_min > RADICT_POOL_MAX_SIZE) {
1222 fr_strerror_printf("Stopping, a dictionary overflowed a %pV pool and the next pool would be "
1223 "larger than RADICT_POOL_MAX_SIZE (%pV)",
1225 fr_perror("radict");
1226 return -1;
1227 }
1228
1229 DEBUG("Reloading the dictionaries with pools of at least %pV", fr_box_size(pool_size_min));
1230
1231 if (dicts_free(*gctx_p) < 0) return -1;
1232 if (dicts_load(gctx_p, dict_dir, protocol, pool_size_min) < 0) return -1;
1233 }
1234
1235 return 0;
1236}
1237
1238/**
1239 *
1240 * @hidecallgraph
1241 */
1242int main(int argc, char *argv[])
1243{
1244 char const *dict_dir = DICTDIR;
1245 int c;
1246 int ret = 0;
1247 bool found = false;
1248 bool export = false;
1249 bool file_export = false;
1250 bool alias = false;
1251 char const *protocol = NULL;
1252 da_print_func_t func = NULL;
1253 fr_dict_gctx_t *gctx;
1254 bool pool_set = false;
1255 bool pool_check = false;
1256 unsigned int pool_overhead = 0;
1257
1258 TALLOC_CTX *autofree;
1259
1260 /*
1261 * Must be called first, so the handler is called last
1262 */
1264
1266
1267 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0], PANIC_ACTION_SIGNALS) < 0) {
1268 fr_perror("radict - Fault setup");
1269 fr_exit(EXIT_FAILURE);
1270 }
1271
1272 talloc_set_log_stderr();
1273
1274 fr_debug_lvl = 1;
1275 fr_log_fp = stdout;
1276
1277 while ((c = getopt(argc, argv, "AcfF:ED:M:Op:P:rVxhH")) != -1) switch (c) {
1278 case 'A':
1279 alias = true;
1280 break;
1281
1282 case 'c':
1284 break;
1285
1286 case 'H':
1287 print_headers = true;
1288 break;
1289
1290 case 'f':
1291 file_export = true;
1292 break;
1293
1294 case 'F':
1297 fprintf(stderr, "Invalid output format '%s'\n", optarg);
1298 fr_exit(EXIT_FAILURE);
1299 }
1300
1302 break;
1303
1304 case 'E':
1305 export = true;
1306 break;
1307
1308 case 'D':
1309 dict_dir = optarg;
1310 break;
1311
1312 case 'M':
1313 mib = optarg;
1314 break;
1315
1316 case 'O':
1317 pool_check = true;
1318 break;
1319
1320 case 'p':
1321 protocol = optarg;
1322 break;
1323
1324 case 'P':
1325 {
1326 char *q;
1327 unsigned long overhead;
1328
1329 overhead = strtoul(optarg, &q, 10);
1330 if ((q == optarg) || (*q != '\0') || (overhead > 1000)) {
1331 fprintf(stderr, "Invalid -P percentage '%s', expected an integer from 0 to 1000\n", optarg);
1332 fr_exit(EXIT_FAILURE);
1333 }
1334 pool_overhead = overhead;
1335 pool_set = true;
1336 }
1337 break;
1338
1339 case 'r':
1340 print_recursive = true;
1341 break;
1342
1343 case 'V':
1344 print_values = true;
1345 break;
1346
1347 case 'x':
1348 fr_debug_lvl++;
1349 break;
1350
1351 case 'h':
1352 default:
1353 usage();
1354 goto finish;
1355 }
1356 argc -= optind;
1357 argv += optind;
1358
1359 /*
1360 * Mismatch between the binary and the libraries it depends on
1361 */
1363 fr_perror("radict - library mismatch");
1364 ret = 1;
1365 goto finish;
1366 }
1367
1368 if (dicts_load(&gctx, dict_dir, protocol, 0) < 0) {
1369 ret = 1;
1370 goto finish;
1371 }
1372
1373 if (dict_end == dicts) {
1374 fr_perror("radict - No dictionaries loaded");
1375 ret = 1;
1376 goto finish;
1377 }
1378
1379 if (pool_check) {
1380 if (protocol_pools_check(gctx) < 0) ret = 1;
1381 goto finish;
1382 }
1383
1384 if (pool_set) {
1385 if ((dicts_pool_grow(&gctx, dict_dir, protocol) < 0) ||
1386 (protocol_lines_pool_set(gctx, pool_overhead) < 0)) ret = 1;
1387 goto finish;
1388 }
1389
1390 if (print_headers) switch(output_format) {
1391 case RADICT_OUT_CSV:
1392 printf("Dictionary,OID,Attribute,ID,Type,Flags\n");
1393 break;
1394
1395 case RADICT_OUT_FANCY:
1396 default:
1397 printf("Dictionary\tOID\tAttribute\tID\tType\tFlags\n");
1398 }
1399
1400 if (file_export) {
1401 fr_dict_t **dict_p = dicts;
1402
1403 do {
1404 if (protocol && (strcasecmp(fr_dict_root(*dict_p)->name, protocol) == 0)) {
1405 fr_dict_export(fr_log_fp, *dict_p);
1406 }
1407 } while (++dict_p < dict_end);
1408 }
1409
1410 if (export) {
1411 fr_dict_t **dict_p = dicts;
1412
1413 do {
1414 raddict_stats_t stats = {};
1415 bool overflowed;
1416 size_t used, live;
1417
1418 raddict_export(&stats, *dict_p, fr_dict_root(*dict_p));
1419 used = talloc_pool_used(&overflowed, &live, (*dict_p)->pool, (*dict_p)->pool_size);
1420 DEBUG2("Attribute count %" PRIu64, stats.count);
1421 DEBUG2("Memory allocd %pV", fr_box_size(talloc_total_size(*dict_p)));
1422 DEBUG2("Memory spread %pV", fr_box_size((size_t) (stats.high - stats.low)));
1423 DEBUG2("Pool size %pV", fr_box_size((*dict_p)->pool_size));
1424 if (overflowed) {
1425 DEBUG2("Pool used overflowed");
1426 } else {
1427 DEBUG2("Pool used %pV", fr_box_size(used));
1428 DEBUG2("Pool live %pV", fr_box_size(live));
1429 DEBUG2("Pool holes %pV", fr_box_size(used - live));
1430 DEBUG2("Pool tail %pV", fr_box_size((*dict_p)->pool_size - used));
1431 }
1432 } while (++dict_p < dict_end);
1433
1434 goto finish;
1435 }
1436
1437 if (alias) {
1438 fr_dict_t **dict_p = dicts;
1439
1440 do {
1441 if (protocol && (strcasecmp(fr_dict_root(*dict_p)->name, protocol) == 0)) {
1443 }
1444 } while (++dict_p < dict_end);
1445
1446 goto finish;
1447 }
1448
1449 if (argc == 0) goto finish;
1450
1451 while (argc-- > 0) {
1452 char *attr;
1453 fr_dict_attr_t const *da;
1454 fr_dict_t **dict_p = dicts;
1455
1456 attr = *argv++;
1457
1458 /*
1459 * Loop through all the dicts. An attribute may
1460 * exist in multiple dictionaries.
1461 */
1462 do {
1463 DEBUG2("Looking for \"%s\" in dict \"%s\"", attr, fr_dict_root(*dict_p)->name);
1464
1465 da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_p), attr);
1466 if (!da) {
1467 DEBUG2("Dictionary %s does not contain attribute %s\n",
1468 fr_dict_root(*dict_p)->name, attr);
1469 continue;
1470 }
1471
1472 if (!func) {
1473 da_print_info(*dict_p, da, 0);
1474 } else {
1475 parent_oid = attr;
1476 func(stdout, da);
1477 }
1478 found = true;
1479 } while (++dict_p < dict_end);
1480 }
1481
1482 if (!found) ret = 64;
1483
1484finish:
1485 /*
1486 * Release our references on all the dicts
1487 * we loaded.
1488 */
1489 {
1490 fr_dict_t **dict_p = dicts;
1491
1492 do {
1493 fr_dict_free(dict_p, __FILE__);
1494 } while (++dict_p < dict_end);
1495 }
1496 if (talloc_free(autofree) < 0) fr_perror("radict - Error freeing dictionaries");
1497
1498 /*
1499 * Ensure our atexit handlers run before any other
1500 * atexit handlers registered by third party libraries.
1501 */
1503
1504 return ret;
1505}
static int const char char buffer[256]
Definition acutest.h:641
char * line_end
Definition acutest.h:990
void int line
Definition acutest.h:810
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:179
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:310
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:422
#define DIAG_ON(_x)
Definition build.h:576
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define DIAG_OFF(_x)
Definition build.h:575
TALLOC_CTX * autofree
Definition common.c:29
fr_dict_t * dict
Definition common.c:31
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program, unsigned long fault_signals)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1071
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:266
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:194
#define PANIC_ACTION_SIGNALS
Definition debug.h:133
#define MEM(x)
Definition debug.h:38
@ FR_DICT_ATTR_EXT_ENUMV
Enumeration values.
Definition dict.h:188
fr_dict_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition dict_util.c:2935
void fr_dict_alias_export(FILE *fp, fr_dict_attr_t const *parent)
Definition dict_print.c:324
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2717
#define da_is_bit_field(_da)
Definition dict.h:171
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:281
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4466
int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent))
(Re)-initialize a protocol dictionary
char const * fr_dict_attr_filename(fr_dict_attr_t const *da)
Resolve an attribute's location file id to the filename.
Definition dict_util.c:792
int fr_dict_global_ctx_free(fr_dict_gctx_t const *gctx)
Explicitly free all data associated with a global dictionary context.
Definition dict_util.c:4922
fr_dict_attr_t const * fr_dict_attr_by_oid(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *oid))
Resolve an attribute using an OID string.
Definition dict_util.c:2690
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3667
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir))
Initialise the global protocol hashes.
Definition dict_util.c:4850
char const * name
Enum name.
Definition dict.h:278
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:525
void fr_dict_export(FILE *fp, fr_dict_t const *dict)
Export in the standard form: ATTRIBUTE name oid flags.
Definition dict_print.c:319
Value of an enumerated attribute.
Definition dict.h:277
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:122
fr_hash_table_t * value_by_name
Lookup an enumeration value by name.
Definition dict_ext.h:98
Attribute extension - Holds enumeration values.
Definition dict_ext.h:96
static fr_dict_attr_t const ** dict_attr_children(fr_dict_attr_t const *da)
size_t pool_size_min
Smallest pool, in bytes, that dict_alloc() creates.
Definition dict_priv.h:160
fr_hash_table_t * protocol_by_name
Hash containing names of all the registered protocols.
Definition dict_priv.h:166
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent)
(Re-)Initialize the special internal dictionary
void * fr_hash_table_iter_next(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Iterate over entries in a hash table.
Definition hash.c:668
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition hash.c:723
Stores the state of the current iteration operation.
Definition hash.h:41
talloc_free(hp)
int fr_debug_lvl
Definition log.c:41
FILE * fr_log_fp
Definition log.c:40
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_FLOAT32
Single precision floating point.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_IFID
Interface ID.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
ssize_t fr_dict_attr_flags_print(fr_sbuff_t *out, fr_dict_t const *dict, fr_type_t type, fr_dict_attr_flags_t const *flags)
long int ssize_t
ssize_t fr_dict_attr_oid_print(fr_sbuff_t *out, fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da, bool numeric)
unsigned char uint8_t
static size_t used
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
int strcasecmp(char *s1, char *s2)
Definition missing.c:65
#define parent_name(_x)
ssize_t fr_fprintf(FILE *fp, char const *fmt,...)
Special version of fprintf which implements custom format specifiers.
Definition print.c:906
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:883
#define fr_assert(_expr)
Definition rad_assert.h:37
static char const * line_field_next(char const **field_end, char const *p, char const *end)
Find the next whitespace-separated field on a dictionary line.
Definition radict.c:817
static char const * fr_type_to_enum_name[]
Definition radict.c:540
int main(int argc, char *argv[])
Definition radict.c:1242
static int file_replace(char const *filename, mode_t mode, char const *data, size_t len)
Write data to a temporary file, then rename the temporary file over filename.
Definition radict.c:843
static fr_dict_t ** dict_end
Definition radict.c:62
static void da_print_struct(FILE *fp, fr_dict_attr_t const *parent)
Print structures and mappings, mainly for statistics.
Definition radict.c:440
#define RADICT_POOL_STEP_SIZE
Bytes that radict -P adds to the largest overflowed pool to set the minimum pool size for the next re...
Definition radict.c:777
static void da_normalize_name(fr_dict_attr_t const *da, char buffer[static FR_DICT_ATTR_MAX_NAME_LEN *2+1])
Definition radict.c:332
static void da_print_name(FILE *fp, fr_dict_attr_t const *da)
Definition radict.c:393
static fr_table_num_ordered_t const format_table[]
Definition radict.c:741
static int load_dicts(char const *dict_dir, char const *protocol)
Definition radict.c:94
static void da_print_stats_link(FILE *fp, fr_dict_attr_t const *parent)
Definition radict.c:585
static int protocol_line_pool_set(fr_dict_t const *dict, size_t pool_mb)
Set pool=<size> on the PROTOCOL line that defines a dictionary.
Definition radict.c:915
static int dicts_pool_grow(fr_dict_gctx_t **gctx_p, char const *dict_dir, char const *protocol)
Reload the dictionaries with larger pools until no pool overflows.
Definition radict.c:1214
uint64_t count
Attributes exported.
Definition radict.c:691
static void da_print_stats_h(FILE *fp, fr_dict_attr_t const *parent)
Definition radict.c:667
static bool print_values
Definition radict.c:56
uintptr_t high
Highest attribute address seen.
Definition radict.c:693
static bool print_recursive
Definition radict.c:58
static fr_dict_t * dicts[255]
Definition radict.c:55
#define RADICT_POOL_MAX_SIZE
Largest pool that radict -P loads a dictionary with.
Definition radict.c:773
static bool print_headers
Definition radict.c:57
static char const * length_to_c_type[]
Definition radict.c:326
static const bool type_allowed[FR_TYPE_MAX]
Definition radict.c:401
static size_t format_table_len
Definition radict.c:751
static fr_table_ptr_ordered_t const function_table[]
Definition radict.c:753
bool found
low and high hold an address.
Definition radict.c:694
static bool line_field_is(char const *field, char const *field_end, char const *keyword)
Check whether a field is a dictionary keyword.
Definition radict.c:834
static size_t function_table_len
Definition radict.c:763
static int dicts_load(fr_dict_gctx_t **gctx_out, char const *dict_dir, char const *protocol, size_t pool_size_min)
Load the internal dictionary, then the protocol dictionaries.
Definition radict.c:1140
static void da_print_info(fr_dict_t const *dict, fr_dict_attr_t const *da, int depth)
Definition radict.c:187
static void da_print_attr_autoload(FILE *fp, fr_dict_attr_t const *parent)
Definition radict.c:636
static void da_print_base_c_da_def(FILE *fp, fr_dict_attr_t const *parent)
Definition radict.c:514
uintptr_t low
Lowest attribute address seen.
Definition radict.c:692
#define CHECK_TYPE(_parent)
Definition radict.c:429
static void raddict_export(raddict_stats_t *stats, fr_dict_t const *dict, fr_dict_attr_t const *da)
Definition radict.c:697
radict_out_t
Definition radict.c:43
@ RADICT_OUT_STRUCT
Definition radict.c:48
@ RADICT_OUT_DICT
Definition radict.c:47
@ RADICT_OUT_STATS_H
Definition radict.c:52
@ RADICT_OUT_ATTR_AUTOLOAD
Definition radict.c:51
@ RADICT_OUT_BASE_C_DA_DEF
Definition radict.c:50
@ RADICT_OUT_INVALID
Definition radict.c:44
@ RADICT_OUT_STATS_LINK
Definition radict.c:49
@ RADICT_OUT_FANCY
Definition radict.c:45
@ RADICT_OUT_CSV
Definition radict.c:46
#define DEBUG2(fmt,...)
Definition radict.c:65
static char const * type_to_c_type[]
Definition radict.c:300
static char const * parent_oid
Definition radict.c:60
static int protocol_pools_check(fr_dict_gctx_t *gctx)
Print the pool size and the bytes used for every loaded protocol dictionary.
Definition radict.c:1091
static int protocol_lines_pool_set(fr_dict_gctx_t *gctx, unsigned int overhead)
Set pool= on the PROTOCOL line of every loaded protocol dictionary.
Definition radict.c:1040
#define DEBUG(fmt,...)
Definition radict.c:66
static void usage(void)
Definition radict.c:70
void(* da_print_func_t)(FILE *fp, fr_dict_attr_t const *da)
Definition radict.c:765
static size_t dicts_overflow_max(fr_dict_gctx_t *gctx)
Find the size of the largest overflowed pool among the loaded protocol dictionaries.
Definition radict.c:786
static int dicts_free(fr_dict_gctx_t *gctx)
Release radict's references to the dictionaries, then free the global dictionary context.
Definition radict.c:1182
static const char * spaces
Definition radict.c:185
static radict_out_t output_format
Definition radict.c:61
static bool children_ok(fr_dict_attr_t const *parent)
Definition radict.c:417
static char const * mib
Definition radict.c:59
#define ENUM_NAME(_x)
Map data types to enum names representing those types.
Definition radict.c:538
Totals gathered while exporting a dictionary.
Definition radict.c:690
static char const * name
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#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
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in an arbitrarily ordered array of name to ptr mappings.
Definition table.h:73
size_t talloc_pool_used(bool *overflowed, size_t *live, TALLOC_CTX const *pool, size_t pool_size)
Measure how many bytes of a talloc pool are used and live, and whether the pool overflowed.
Definition talloc.c:461
#define talloc_asprintf
Definition talloc.h:151
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:55
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
#define FR_DICTIONARY_FILE
Definition conf.h:6
#define FR_DICTIONARY_INTERNAL_DIR
Definition conf.h:7
static fr_slen_t parent
Definition pair.h:864
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
@ FR_TYPE_UNION
A union of limited children.
Definition types.h:81
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
#define fr_type_is_structural(_x)
Definition types.h:392
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
static fr_slen_t data
Definition value.h:1366
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
#define fr_box_size(_val)
Definition value.h:382