The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 637cc829fd89adb5ae38fc23556feff1c301da94 $
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: 637cc829fd89adb5ae38fc23556feff1c301da94 $")
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 <dirent.h>
33#include <sys/stat.h>
34#include <stdbool.h>
35
36#ifdef HAVE_GETOPT_H
37# include <getopt.h>
38#endif
39
45
46static fr_dict_t *dicts[255];
47static bool print_values = false;
48static bool print_headers = false;
49static bool print_recursive = false;
52
53DIAG_OFF(unused-macros)
54#define DEBUG2(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 2)) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
55#define DEBUG(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 1)) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
56#define INFO(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 0)) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
57DIAG_ON(unused-macros)
58
59static void usage(void)
60{
61 fprintf(stderr, "usage: radict [OPTS] [attribute...]\n");
62 fprintf(stderr, " -A Export aliases.\n");
63 fprintf(stderr, " -c Print out in CSV format.\n");
64 fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
65 fprintf(stderr, " -f Export dictionary definitions in the normal dictionary format\n");
66 fprintf(stderr, " -F <format> Set output format. Use 'csv', 'full', or 'dictionary'\n");
67 fprintf(stderr, " -E Export dictionary definitions.\n");
68 fprintf(stderr, " -h Print help text.\n");
69 fprintf(stderr, " -H Show the headers of each field.\n");
70 fprintf(stderr, " -p <protocol> Set protocol by name\n");
71 fprintf(stderr, " -r Write out attributes recursively.\n");
72 fprintf(stderr, " -V Write out all attribute values.\n");
73 fprintf(stderr, " -x Debugging mode.\n");
74 fprintf(stderr, "\n");
75 fprintf(stderr, "Very simple interface to extract attribute definitions from FreeRADIUS dictionaries\n");
76}
77
78static int load_dicts(char const *dict_dir, char const *protocol)
79{
80 int loaded = 0;
81 DIR *dir;
82 struct dirent *dp;
83
84 INFO("Reading directory %s", dict_dir);
85
86 dir = opendir(dict_dir);
87 if (!dir) {
88 fr_strerror_printf("Failed opening \"%s\": %s", dict_dir, fr_syserror(errno));
89 return -1;
90 }
91
92 while ((dp = readdir(dir)) != NULL) {
93 struct stat stat_buff;
94 char *file_str;
95
96 if (dp->d_name[0] == '.') continue;
97
98 /*
99 * We only want to load one...
100 */
101 if (protocol && (strcmp(dp->d_name, protocol) != 0)) continue;
102
103 /*
104 * Skip the internal FreeRADIUS dictionary.
105 */
106 if (strcmp(dp->d_name, "freeradius") == 0) continue;
107
108 file_str = talloc_asprintf(NULL, "%s/%s", dict_dir, dp->d_name);
109
110 if (stat(file_str, &stat_buff) == -1) {
111 fr_strerror_printf("Failed stating file \"%s\": %s", file_str, fr_syserror(errno));
112 error:
113 closedir(dir);
114 talloc_free(file_str);
115 return -1;
116 }
117
118 /*
119 * Only process directories
120 */
121 if ((stat_buff.st_mode & S_IFMT) == S_IFDIR) {
122 char *dict_file;
123 struct stat dict_stat_buff;
124 int ret;
125
126 dict_file = talloc_asprintf(NULL, "%s/dictionary", file_str);
127 ret = stat(dict_file, &dict_stat_buff);
128 talloc_free(dict_file);
129
130 /*
131 * If the directory contains a dictionary file,
132 * load it as a dictionary.
133 */
134 if (ret == 0) {
135 if (dict_end >= (dicts + (NUM_ELEMENTS(dicts)))) {
136 fr_strerror_const("Reached maximum number of dictionaries");
137 goto error;
138 }
139
140 INFO("Loading dictionary: %s/dictionary", file_str);
141 if (fr_dict_protocol_afrom_file(dict_end, dp->d_name, NULL, __FILE__) < 0) {
142 goto error;
143 }
144 dict_end++;
145 loaded++;
146 }
147
148 /*
149 * For now, don't do sub-protocols.
150 */
151 }
152 talloc_free(file_str);
153 }
154 closedir(dir);
155
156 if (!loaded) {
157 if (!protocol) {
158 fr_strerror_printf("Failed to load any dictionaries");
159 } else {
160 fr_strerror_printf("Failed to load dictionary for protocol %s", protocol);
161 }
162
163 return -1;
164 }
165
166 return 0;
167}
168
169static const char *spaces = " ";
170
171static void da_print_info(fr_dict_t const *dict, fr_dict_attr_t const *da, int depth)
172{
173 char oid_str[512];
174 char flags[256];
175 fr_hash_iter_t iter;
177 fr_sbuff_t old_str_sbuff = FR_SBUFF_OUT(oid_str, sizeof(oid_str));
178 fr_sbuff_t flags_sbuff = FR_SBUFF_OUT(flags, sizeof(flags));
179
180 char const *type;
181 fr_dict_attr_t const *child;
182 fr_hash_table_t *namespace;
183
184 if (fr_dict_attr_oid_print(&old_str_sbuff, NULL, da, false) <= 0) {
185 fr_strerror_printf("OID string too long");
186 fr_exit(EXIT_FAILURE);
187 }
188
189 fr_dict_attr_flags_print(&flags_sbuff, dict, da->type, &da->flags);
190
191 if (!da->flags.is_alias) {
192 type = fr_type_to_str(da->type);
193 } else {
194 fr_assert(da->type == FR_TYPE_VOID);
195 type = "ALIAS";
196 }
197
198 printf("%.*s", depth, spaces);
199
200 /* Protocol Name Type */
201
202 switch(output_format) {
203 case RADICT_OUT_CSV:
204 printf("%s,%s,%s,%d,%s,%s\n",
205 depth == 0 ? fr_dict_root(dict)->name : "",
206 fr_sbuff_start(&old_str_sbuff),
207 da->name,
208 da->attr,
209 type,
210 fr_sbuff_start(&flags_sbuff));
211 break;
212
213 case RADICT_OUT_FANCY:
214 default:
215 printf("%s\t%s\t%s\t%d\t%s\t%s\n",
216 depth == 0 ? fr_dict_root(dict)->name : "",
217 fr_sbuff_start(&old_str_sbuff),
218 da->name,
219 da->attr,
220 type,
221 fr_sbuff_start(&flags_sbuff));
222 }
223
224 if (print_values) {
226
228 if (!ext || !ext->value_by_name) return;
229
230 for (enumv = fr_hash_table_iter_init(ext->value_by_name, &iter);
231 enumv;
232 enumv = fr_hash_table_iter_next(ext->value_by_name, &iter)) {
233 char *str;
234
235 switch(output_format) {
236 case RADICT_OUT_CSV:
237 str = fr_asprintf(NULL, "%s,%s,%s,%d,%s,%s,%s,%pV",
238 depth == 0 ? fr_dict_root(dict)->name : "",
239 fr_sbuff_start(&old_str_sbuff),
240 da->name,
241 da->attr,
242 type,
243 fr_sbuff_start(&flags_sbuff),
244 enumv->name,
245 enumv->value);
246 break;
247
248 case RADICT_OUT_FANCY:
249 default:
250 str = fr_asprintf(NULL, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%pV",
251 depth == 0 ? fr_dict_root(dict)->name : "",
252 fr_sbuff_start(&old_str_sbuff),
253 da->name,
254 da->attr,
255 type,
256 fr_sbuff_start(&flags_sbuff),
257 enumv->name,
258 enumv->value);
259 }
260
261 printf("%.*s%s\n", depth, spaces, str);
262 talloc_free(str);
263 }
264 }
265
266 /*
267 * Print definitions recursively.
268 */
269 if (!print_recursive || !fr_type_is_structural(da->type)) return;
270
271 namespace = dict_attr_namespace(da);
272 fr_assert(namespace != NULL);
273
274 for (child = fr_hash_table_iter_init(namespace, &iter);
275 child != NULL;
276 child = fr_hash_table_iter_next(namespace, &iter)) {
277 da_print_info(dict, child, depth + 1);
278 }
279}
280
281static void _raddict_export(fr_dict_t const *dict, uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_attr_t const *da, unsigned int lvl)
282{
283 unsigned int i;
284 size_t len;
285 fr_dict_attr_t const *p;
286 char flags[256];
287 fr_dict_attr_t const **children;
288
289 fr_dict_attr_flags_print(&FR_SBUFF_OUT(flags, sizeof(flags)), dict, da->type, &da->flags);
290
291 /*
292 * Root attributes are allocated outside of the pool
293 * so it's not helpful to include them in the calculation.
294 */
295 if (!da->flags.is_root) {
296 if (low && ((uintptr_t)da < *low)) {
297 *low = (uintptr_t)da;
298 }
299 if (high && ((uintptr_t)da > *high)) {
300 *high = (uintptr_t)da;
301 }
302
303 da_print_info(fr_dict_by_da(da), da, 0);
304 }
305
306 if (count) (*count)++;
307
308 /*
309 * Todo - Should be fixed to use attribute walking API
310 */
311 children = dict_attr_children(da);
312 if (children) {
313 len = talloc_array_length(children);
314 for (i = 0; i < len; i++) {
315 for (p = children[i]; p; p = p->next) {
316 _raddict_export(dict, count, low, high, p, lvl + 1);
317 }
318 }
319 }
320}
321
322static void raddict_export(uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_t *dict)
323{
324 if (count) *count = 0;
325 if (low) *low = UINTPTR_MAX;
326 if (high) *high = 0;
327
328 _raddict_export(dict, count, low, high, fr_dict_root(dict), 0);
329}
330
331/**
332 *
333 * @hidecallgraph
334 */
335int main(int argc, char *argv[])
336{
337 char const *dict_dir = DICTDIR;
338 int c;
339 int ret = 0;
340 bool found = false;
341 bool export = false;
342 bool file_export = false;
343 bool alias = false;
344 char const *protocol = NULL;
345
346 TALLOC_CTX *autofree;
347
348 /*
349 * Must be called first, so the handler is called last
350 */
352
354
355#ifndef NDEBUG
356 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
357 fr_perror("radict - Fault setup");
358 fr_exit(EXIT_FAILURE);
359 }
360#endif
361
362 talloc_set_log_stderr();
363
364 fr_debug_lvl = 1;
365 fr_log_fp = stdout;
366
367 while ((c = getopt(argc, argv, "AcfF:ED:p:rVxhH")) != -1) switch (c) {
368 case 'A':
369 alias = true;
370 break;
371
372 case 'c':
374 break;
375
376 case 'H':
377 print_headers = true;
378 break;
379
380 case 'f':
381 file_export = true;
382 break;
383
384 case 'F':
385 if (strcmp(optarg, "csv") == 0) {
387
388 } else if (strcmp(optarg, "full") == 0) {
390
391 } else if (strncmp(optarg, "dict", 4) == 0) {
393
394 } else {
395 fprintf(stderr, "Invalid output format '%s'\n", optarg);
396 fr_exit(EXIT_FAILURE);
397 }
398 break;
399
400 case 'E':
401 export = true;
402 break;
403
404 case 'D':
405 dict_dir = optarg;
406 break;
407
408 case 'p':
409 protocol = optarg;
410 break;
411
412 case 'r':
413 print_recursive = true;
414 break;
415
416 case 'V':
417 print_values = true;
418 break;
419
420 case 'x':
421 fr_debug_lvl++;
422 break;
423
424 case 'h':
425 default:
426 usage();
427 goto finish;
428 }
429 argc -= optind;
430 argv += optind;
431
432 /*
433 * Mismatch between the binary and the libraries it depends on
434 */
436 fr_perror("radict - library mismatch");
437 ret = 1;
438 goto finish;
439 }
440
441 if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
442 fr_perror("radict - Global context init failed");
443 ret = 1;
444 goto finish;
445 }
446
447 INFO("Loading dictionary: %s/%s", dict_dir, FR_DICTIONARY_FILE);
448
450 fr_perror("radict - Loading internal dictionary failed");
451 ret = 1;
452 goto finish;
453 }
454
455 /*
456 * Don't emit spurious errors...
457 */
459 if (load_dicts(dict_dir, protocol) < 0) {
460 fr_perror("radict - Loading dictionaries failed");
461 ret = 1;
462 goto finish;
463 }
464
465 if (dict_end == dicts) {
466 fr_perror("radict - No dictionaries loaded");
467 ret = 1;
468
469 }
470
471 if (print_headers) switch(output_format) {
472 case RADICT_OUT_CSV:
473 printf("Dictionary,OID,Attribute,ID,Type,Flags\n");
474 break;
475
476 case RADICT_OUT_FANCY:
477 default:
478 printf("Dictionary\tOID\tAttribute\tID\tType\tFlags\n");
479 }
480
481 if (file_export) {
482 fr_dict_t **dict_p = dicts;
483
484 do {
485 if (protocol && (strcasecmp(fr_dict_root(*dict_p)->name, protocol) == 0)) {
486 fr_dict_export(fr_log_fp, *dict_p);
487 }
488 } while (++dict_p < dict_end);
489 }
490
491 if (export) {
492 fr_dict_t **dict_p = dicts;
493
494 do {
495 uint64_t count;
496 uintptr_t high;
497 uintptr_t low;
498
499 raddict_export(&count, &low, &high, *dict_p);
500 DEBUG2("Attribute count %" PRIu64, count);
501 DEBUG2("Memory allocd %zu (bytes)", talloc_total_size(*dict_p));
502 DEBUG2("Memory spread %zu (bytes)", (size_t) (high - low));
503 } while (++dict_p < dict_end);
504
505 goto finish;
506 }
507
508 if (alias) {
509 fr_dict_t **dict_p = dicts;
510
511 do {
512 if (protocol && (strcasecmp(fr_dict_root(*dict_p)->name, protocol) == 0)) {
514 }
515 } while (++dict_p < dict_end);
516
517 goto finish;
518 }
519
520 if (argc == 0) goto finish;
521
522 while (argc-- > 0) {
523 char *attr;
524 fr_dict_attr_t const *da;
525 fr_dict_t **dict_p = dicts;
526
527 attr = *argv++;
528
529 /*
530 * Loop through all the dicts. An attribute may
531 * exist in multiple dictionaries.
532 */
533 do {
534 DEBUG2("Looking for \"%s\" in dict \"%s\"", attr, fr_dict_root(*dict_p)->name);
535
536 da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_p), attr);
537 if (da) {
538 da_print_info(*dict_p, da, 0);
539 found = true;
540 } else {
541 fprintf(stderr, "Dictionary %s does not contain attribute %s\n",
542 fr_dict_root(*dict_p)->name, attr);
543 }
544 } while (++dict_p < dict_end);
545 }
546
547 if (!found) ret = 64;
548
549finish:
550 /*
551 * Release our references on all the dicts
552 * we loaded.
553 */
554 {
555 fr_dict_t **dict_p = dicts;
556
557 do {
558 fr_dict_free(dict_p, __FILE__);
559 } while (++dict_p < dict_end);
560 }
561 if (talloc_free(autofree) < 0) fr_perror("radict - Error freeing dictionaries");
562
563 /*
564 * Ensure our atexit handlers run before any other
565 * atexit handlers registered by third party libraries.
566 */
568
569 return ret;
570}
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:160
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:286
static TALLOC_CTX * autofree
Definition fuzzer.c:45
#define RCSID(id)
Definition build.h:485
#define DIAG_ON(_x)
Definition build.h:460
#define NUM_ELEMENTS(_t)
Definition build.h:339
#define DIAG_OFF(_x)
Definition build.h:459
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1055
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:220
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:4740
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *internal_name, char const *dependent)
(Re-)Initialize the special internal dictionary
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:2913
void fr_dict_alias_export(FILE *fp, fr_dict_attr_t const *parent)
Definition dict_print.c:324
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
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2707
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:258
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4372
@ FR_DICT_ATTR_EXT_ENUMV
Enumeration values.
Definition dict.h:189
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:2680
char const * name
Enum name.
Definition dict.h:255
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:254
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:134
fr_hash_table_t * value_by_name
Lookup an enumeration value by name.
Definition dict_ext.h:110
Attribute extension - Holds enumeration values.
Definition dict_ext.h:108
static fr_dict_attr_t const ** dict_attr_children(fr_dict_attr_t const *da)
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:625
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition hash.c:677
Stores the state of the current iteration operation.
Definition hash.h:41
talloc_free(reap)
int fr_debug_lvl
Definition log.c:40
FILE * fr_log_fp
Definition log.c:39
@ FR_TYPE_VOID
User data.
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)
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)
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
int strcasecmp(char *s1, char *s2)
Definition missing.c:66
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:884
#define fr_assert(_expr)
Definition rad_assert.h:38
int main(int argc, char *argv[])
Definition radict.c:335
static fr_dict_t ** dict_end
Definition radict.c:51
static int load_dicts(char const *dict_dir, char const *protocol)
Definition radict.c:78
static void raddict_export(uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_t *dict)
Definition radict.c:322
static bool print_values
Definition radict.c:47
static bool print_recursive
Definition radict.c:49
static fr_dict_t * dicts[255]
Definition radict.c:46
static bool print_headers
Definition radict.c:48
static void da_print_info(fr_dict_t const *dict, fr_dict_attr_t const *da, int depth)
Definition radict.c:171
#define INFO(fmt,...)
Definition radict.c:56
static void _raddict_export(fr_dict_t const *dict, uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_attr_t const *da, unsigned int lvl)
Definition radict.c:281
#define DEBUG2(fmt,...)
Definition radict.c:54
radict_out_t
Definition radict.c:40
@ RADICT_OUT_DICT
Definition radict.c:43
@ RADICT_OUT_FANCY
Definition radict.c:41
@ RADICT_OUT_CSV
Definition radict.c:42
static void usage(void)
Definition radict.c:59
static const char * spaces
Definition radict.c:169
static radict_out_t output_format
Definition radict.c:50
static char const * name
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
return count
Definition module.c:155
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 talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
#define FR_DICTIONARY_FILE
Definition conf.h:7
#define FR_DICTIONARY_INTERNAL_DIR
Definition conf.h:8
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_type_is_structural(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
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