The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 0934122f6e55427387b7637585df9e33b1c5fafe $
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  */
26 RCSID("$Id: 0934122f6e55427387b7637585df9e33b1c5fafe $")
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 
40 typedef enum {
44 
45 static fr_dict_t *dicts[255];
46 static bool print_values = false;
47 static bool print_headers = false;
50 
51 DIAG_OFF(unused-macros)
52 #define DEBUG2(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 2)) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
53 #define DEBUG(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 1)) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
54 #define INFO(fmt, ...) if (fr_log_fp && (fr_debug_lvl > 0)) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
55 DIAG_ON(unused-macros)
56 
57 static void usage(void)
58 {
59  fprintf(stderr, "usage: radict [OPTS] <attribute> [attribute...]\n");
60  fprintf(stderr, " -E Export dictionary definitions.\n");
61  fprintf(stderr, " -V Write out all attribute values.\n");
62  fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
63  fprintf(stderr, " -p <protocol> Set protocol by name\n");
64  fprintf(stderr, " -x Debugging mode.\n");
65  fprintf(stderr, " -c Print out in CSV format.\n");
66  fprintf(stderr, " -H Show the headers of each field.\n");
67  fprintf(stderr, "\n");
68  fprintf(stderr, "Very simple interface to extract attribute definitions from FreeRADIUS dictionaries\n");
69 }
70 
71 static int load_dicts(char const *dict_dir, char const *protocol)
72 {
73  DIR *dir;
74  struct dirent *dp;
75 
76  INFO("Reading directory %s", dict_dir);
77 
78  dir = opendir(dict_dir);
79  if (!dir) {
80  fr_strerror_printf("Failed opening \"%s\": %s", dict_dir, fr_syserror(errno));
81  return -1;
82  }
83 
84  while ((dp = readdir(dir)) != NULL) {
85  struct stat stat_buff;
86  char *file_str;
87 
88  if (dp->d_name[0] == '.') continue;
89 
90  /*
91  * We only want to load one...
92  */
93  if (protocol && (strcmp(dp->d_name, protocol) != 0)) continue;
94 
95  /*
96  * Skip the internal FreeRADIUS dictionary.
97  */
98  if (strcmp(dp->d_name, "freeradius") == 0) continue;
99 
100  file_str = talloc_asprintf(NULL, "%s/%s", dict_dir, dp->d_name);
101 
102  if (stat(file_str, &stat_buff) == -1) {
103  fr_strerror_printf("Failed stating file \"%s\": %s", file_str, fr_syserror(errno));
104  error:
105  closedir(dir);
106  talloc_free(file_str);
107  return -1;
108  }
109 
110  /*
111  * Only process directories
112  */
113  if ((stat_buff.st_mode & S_IFMT) == S_IFDIR) {
114  char *dict_file;
115  struct stat dict_stat_buff;
116  int ret;
117 
118  dict_file = talloc_asprintf(NULL, "%s/dictionary", file_str);
119  ret = stat(dict_file, &dict_stat_buff);
120  talloc_free(dict_file);
121 
122  /*
123  * If the directory contains a dictionary file,
124  * load it as a dictionary.
125  */
126  if (ret == 0) {
127  if (dict_end >= (dicts + (NUM_ELEMENTS(dicts)))) {
128  fr_strerror_const("Reached maximum number of dictionaries");
129  goto error;
130  }
131 
132  INFO("Loading dictionary: %s/dictionary", file_str);
133  if (fr_dict_protocol_afrom_file(dict_end, dp->d_name, NULL, __FILE__) < 0) {
134  goto error;
135  }
136  dict_end++;
137  }
138 
139  /*
140  * For now, don't do sub-protocols.
141  */
142  }
143  talloc_free(file_str);
144  }
145  closedir(dir);
146 
147  return 0;
148 }
149 
150 static void da_print_info_td(fr_dict_t const *dict, fr_dict_attr_t const *da)
151 {
152  char oid_str[512];
153  char flags[256];
155  fr_dict_enum_value_t *enumv;
156  fr_sbuff_t old_str_sbuff = FR_SBUFF_OUT(oid_str, sizeof(oid_str));
157  fr_sbuff_t flags_sbuff = FR_SBUFF_OUT(flags, sizeof(flags));
158 
159  if (fr_dict_attr_oid_print(&old_str_sbuff, NULL, da, false) <= 0) {
160  fr_strerror_printf("OID string too long");
161  fr_exit(EXIT_FAILURE);
162  }
163 
164  fr_dict_attr_flags_print(&flags_sbuff, dict, da->type, &da->flags);
165 
166  /* Protocol Name Type */
167 
168  switch(output_format) {
169  case RADICT_OUT_CSV:
170  printf("%s,%s,%s,%d,%s,%s\n",
172  fr_sbuff_start(&old_str_sbuff),
173  da->name,
174  da->attr,
175  fr_type_to_str(da->type),
176  fr_sbuff_start(&flags_sbuff));
177  break;
178 
179  case RADICT_OUT_FANCY:
180  default:
181  printf("%s\t%s\t%s\t%d\t%s\t%s\n",
183  fr_sbuff_start(&old_str_sbuff),
184  da->name,
185  da->attr,
186  fr_type_to_str(da->type),
187  fr_sbuff_start(&flags_sbuff));
188  }
189 
190  if (print_values) {
192 
194  if (!ext || !ext->value_by_name) return;
195 
196  for (enumv = fr_hash_table_iter_init(ext->value_by_name, &iter);
197  enumv;
198  enumv = fr_hash_table_iter_next(ext->value_by_name, &iter)) {
199  char *str;
200 
201 
202  switch(output_format) {
203  case RADICT_OUT_CSV:
204  str = fr_asprintf(NULL, "%s,%s,%s,%d,%s,%s,%s,%pV",
206  fr_sbuff_start(&old_str_sbuff),
207  da->name,
208  da->attr,
209  fr_type_to_str(da->type),
210  fr_sbuff_start(&flags_sbuff),
211  enumv->name,
212  enumv->value);
213  break;
214 
215  case RADICT_OUT_FANCY:
216  default:
217  str = fr_asprintf(NULL, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%pV",
219  fr_sbuff_start(&old_str_sbuff),
220  da->name,
221  da->attr,
222  fr_type_to_str(da->type),
223  fr_sbuff_start(&flags_sbuff),
224  enumv->name,
225  enumv->value);
226  }
227 
228  printf("%s\n", str);
229  talloc_free(str);
230  }
231  }
232 }
233 
234 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)
235 {
236  unsigned int i;
237  size_t len;
238  fr_dict_attr_t const *p;
239  char flags[256];
240  fr_dict_attr_t const **children;
241 
242  fr_dict_attr_flags_print(&FR_SBUFF_OUT(flags, sizeof(flags)), dict, da->type, &da->flags);
243 
244  /*
245  * Root attributes are allocated outside of the pool
246  * so it's not helpful to include them in the calculation.
247  */
248  if (!da->flags.is_root) {
249  if (low && ((uintptr_t)da < *low)) {
250  *low = (uintptr_t)da;
251  }
252  if (high && ((uintptr_t)da > *high)) {
253  *high = (uintptr_t)da;
254  }
255 
257  }
258 
259  if (count) (*count)++;
260 
261  /*
262  * Todo - Should be fixed to use attribute walking API
263  */
264  children = dict_attr_children(da);
265  if (children) {
266  len = talloc_array_length(children);
267  for (i = 0; i < len; i++) {
268  for (p = children[i]; p; p = p->next) {
269  _raddict_export(dict, count, low, high, p, lvl + 1);
270  }
271  }
272  }
273 }
274 
275 static void raddict_export(uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_t *dict)
276 {
277  if (count) *count = 0;
278  if (low) *low = UINTPTR_MAX;
279  if (high) *high = 0;
280 
281  _raddict_export(dict, count, low, high, fr_dict_root(dict), 0);
282 }
283 
284 /**
285  *
286  * @hidecallgraph
287  */
288 int main(int argc, char *argv[])
289 {
290  char const *dict_dir = DICTDIR;
291  char c;
292  int ret = 0;
293  bool found = false;
294  bool export = false;
295  bool file_export = false;
296  char const *protocol = NULL;
297 
298  TALLOC_CTX *autofree;
299 
300  /*
301  * Must be called first, so the handler is called last
302  */
304 
306 
307 #ifndef NDEBUG
308  if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
309  fr_perror("radict - Fault setup");
310  fr_exit(EXIT_FAILURE);
311  }
312 #endif
313 
314  talloc_set_log_stderr();
315 
316  fr_debug_lvl = 1;
317 
318  while ((c = getopt(argc, argv, "cfED:p:VxhH")) != -1) switch (c) {
319  case 'c':
321  break;
322 
323  case 'H':
324  print_headers = true;
325  break;
326 
327  case 'f':
328  file_export = true;
329  break;
330 
331  case 'E':
332  export = true;
333  break;
334 
335  case 'D':
336  dict_dir = optarg;
337  break;
338 
339  case 'p':
340  protocol = optarg;
341  break;
342 
343  case 'V':
344  print_values = true;
345  break;
346 
347  case 'x':
348  fr_log_fp = stdout;
349  fr_debug_lvl++;
350  break;
351 
352  case 'h':
353  default:
354  usage();
355  found = true;
356  goto finish;
357  }
358  argc -= optind;
359  argv += optind;
360 
361  /*
362  * Mismatch between the binary and the libraries it depends on
363  */
365  fr_perror("radict - library mismatch");
366  ret = 1;
367  goto finish;
368  }
369 
370  if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
371  fr_perror("radict - Global context init failed");
372  ret = 1;
373  goto finish;
374  }
375 
376  INFO("Loading dictionary: %s/%s", dict_dir, FR_DICTIONARY_FILE);
377 
379  fr_perror("radict - Loading internal dictionary failed");
380  ret = 1;
381  goto finish;
382  }
383  /*
384  * Don't emit spurious errors...
385  */
387  if (load_dicts(dict_dir, protocol) < 0) {
388  fr_perror("radict - Loading dictionaries failed");
389  ret = 1;
390  goto finish;
391  }
392 
393  if (dict_end == dicts) {
394  fr_perror("radict - No dictionaries loaded");
395  ret = 1;
396  goto finish;
397  }
398 
399  if (print_headers) switch(output_format) {
400  case RADICT_OUT_CSV:
401  printf("Dictionary,OID,Attribute,ID,Type,Flags\n");
402  break;
403 
404  case RADICT_OUT_FANCY:
405  default:
406  printf("Dictionary\tOID\tAttribute\tID\tType\tFlags\n");
407  }
408 
409  if (file_export) {
410  fr_dict_t **dict_p = dicts;
411 
412  do {
413  if (protocol && (strcasecmp(fr_dict_root(*dict_p)->name, protocol) == 0)) {
414  fr_dict_export(*dict_p);
415  }
416  } while (++dict_p < dict_end);
417  }
418 
419  if (export) {
420  fr_dict_t **dict_p = dicts;
421 
422  do {
423  uint64_t count;
424  uintptr_t high;
425  uintptr_t low;
426 
427  raddict_export(&count, &low, &high, *dict_p);
428  DEBUG2("Attribute count %" PRIu64, count);
429  DEBUG2("Memory allocd %zu (bytes)", talloc_total_size(*dict_p));
430  DEBUG2("Memory spread %zu (bytes)", (size_t) (high - low));
431  } while (++dict_p < dict_end);
432  }
433 
434  while (argc-- > 0) {
435  char *attr;
436  fr_dict_attr_t const *da;
437  fr_dict_t **dict_p = dicts;
438 
439  attr = *argv++;
440 
441 
442  /*
443  * Loop through all the dicts. An attribute may
444  * exist in multiple dictionaries.
445  */
446  do {
447  DEBUG2("Looking for \"%s\" in dict \"%s\"", attr, fr_dict_root(*dict_p)->name);
448 
449  da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_p), attr);
450  if (da) {
451  da_print_info_td(*dict_p, da);
452  found = true;
453  }
454  } while (++dict_p < dict_end);
455  }
456 
457 finish:
458  /*
459  * Release our references on all the dicts
460  * we loaded.
461  */
462  {
463  fr_dict_t **dict_p = dicts;
464 
465  do {
466  fr_dict_free(dict_p, __FILE__);
467  } while (++dict_p < dict_end);
468  }
469  if (talloc_free(autofree) < 0) fr_perror("radict - Error freeing dictionaries");
470 
471  /*
472  * Ensure our atexit handlers run before any other
473  * atexit handlers registered by third party libraries.
474  */
476 
477  return found ? ret : 64;
478 }
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 fr_dict_t * dict
Definition: fuzzer.c:46
#define RCSID(id)
Definition: build.h:481
#define DIAG_ON(_x)
Definition: build.h:456
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define DIAG_OFF(_x)
Definition: build.h:455
fr_dcursor_iter_t iter
Definition: dcursor.h:147
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:1242
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition: debug.h:228
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:2373
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *internal_name, char const *dependent)
(Re-)Initialize the special internal dictionary
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:2400
fr_value_box_t const * value
Enum value (what name maps to).
Definition: dict.h:230
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition: dict_util.c:4024
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:2606
@ FR_DICT_ATTR_EXT_ENUMV
Enumeration values.
Definition: dict.h:168
void fr_dict_export(fr_dict_t const *dict)
Export in the standard form: ATTRIBUTE name oid flags.
Definition: dict_print.c:295
char const * name
Enum name.
Definition: dict.h:227
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:4392
Value of an enumerated attribute.
Definition: dict.h:226
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition: dict_ext.h:140
fr_hash_table_t * value_by_name
Lookup an enumeration value by name.
Definition: dict_ext.h:109
Attribute extension - Holds enumeration values.
Definition: dict_ext.h:107
static fr_dict_attr_t const ** dict_attr_children(fr_dict_attr_t const *da)
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition: hash.c:678
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:626
Stores the state of the current iteration operation.
Definition: hash.h:41
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:43
FILE * fr_log_fp
Definition: log.c:42
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)
Definition: merged_model.c:201
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)
Definition: merged_model.c:188
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:874
static TALLOC_CTX * autofree
Definition: radclient-ng.c:107
int main(int argc, char *argv[])
Definition: radict.c:288
static fr_dict_t ** dict_end
Definition: radict.c:49
static int load_dicts(char const *dict_dir, char const *protocol)
Definition: radict.c:71
static void raddict_export(uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_t *dict)
Definition: radict.c:275
static bool print_values
Definition: radict.c:46
static fr_dict_t * dicts[255]
Definition: radict.c:45
static bool print_headers
Definition: radict.c:47
#define INFO(fmt,...)
Definition: radict.c:54
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:234
#define DEBUG2(fmt,...)
Definition: radict.c:52
radict_out_t
Definition: radict.c:40
@ RADICT_OUT_FANCY
Definition: radict.c:41
@ RADICT_OUT_CSV
Definition: radict.c:42
static void usage(void)
Definition: radict.c:57
static void da_print_info_td(fr_dict_t const *dict, fr_dict_attr_t const *da)
Definition: radict.c:150
static radict_out_t output_format
Definition: radict.c:48
static char const * name
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
return count
Definition: module.c:163
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:733
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
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