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: 1d4df63d94ad5f30f2755a2411b54036d77707fc $
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: 1d4df63d94ad5f30f2755a2411b54036d77707fc $")
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];
154  fr_hash_iter_t iter;
155  fr_dict_enum_value_t *enumv;
156 
157  if (fr_dict_attr_oid_print(&FR_SBUFF_OUT(oid_str, sizeof(oid_str)), NULL, da, false) <= 0) {
158  fr_strerror_printf("OID string too long");
159  fr_exit(EXIT_FAILURE);
160  }
161 
162  fr_dict_attr_flags_print(&FR_SBUFF_OUT(flags, sizeof(flags)), dict, da->type, &da->flags);
163 
164  /* Protocol Name Type */
165 
166  switch(output_format) {
167  case RADICT_OUT_CSV:
168  printf("%s,%s,%s,%d,%s,%s\n",
170  oid_str,
171  da->name,
172  da->attr,
173  fr_type_to_str(da->type),
174  flags);
175  break;
176 
177  case RADICT_OUT_FANCY:
178  default:
179  printf("%s\t%s\t%s\t%d\t%s\t%s\n",
181  oid_str,
182  da->name,
183  da->attr,
184  fr_type_to_str(da->type),
185  flags);
186  }
187 
188  if (print_values) {
190 
192  if (!ext || !ext->value_by_name) return;
193 
194  for (enumv = fr_hash_table_iter_init(ext->value_by_name, &iter);
195  enumv;
196  enumv = fr_hash_table_iter_next(ext->value_by_name, &iter)) {
197  char *str;
198 
199 
200  switch(output_format) {
201  case RADICT_OUT_CSV:
202  str = fr_asprintf(NULL, "%s,%s,%s,%d,%s,%s,%s,%pV",
204  oid_str,
205  da->name,
206  da->attr,
207  fr_type_to_str(da->type),
208  flags,
209  enumv->name,
210  enumv->value);
211  break;
212 
213  case RADICT_OUT_FANCY:
214  default:
215  str = fr_asprintf(NULL, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%pV",
217  oid_str,
218  da->name,
219  da->attr,
220  fr_type_to_str(da->type),
221  flags,
222  enumv->name,
223  enumv->value);
224  }
225 
226  printf("%s\n", str);
227  talloc_free(str);
228  }
229  }
230 }
231 
232 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)
233 {
234  unsigned int i;
235  size_t len;
236  fr_dict_attr_t const *p;
237  char flags[256];
238  fr_dict_attr_t const **children;
239 
240  fr_dict_attr_flags_print(&FR_SBUFF_OUT(flags, sizeof(flags)), dict, da->type, &da->flags);
241 
242  /*
243  * Root attributes are allocated outside of the pool
244  * so it's not helpful to include them in the calculation.
245  */
246  if (!da->flags.is_root) {
247  if (low && ((uintptr_t)da < *low)) {
248  *low = (uintptr_t)da;
249  }
250  if (high && ((uintptr_t)da > *high)) {
251  *high = (uintptr_t)da;
252  }
253 
255  }
256 
257  if (count) (*count)++;
258 
259  /*
260  * Todo - Should be fixed to use attribute walking API
261  */
262  children = dict_attr_children(da);
263  if (children) {
264  len = talloc_array_length(children);
265  for (i = 0; i < len; i++) {
266  for (p = children[i]; p; p = p->next) {
267  _raddict_export(dict, count, low, high, p, lvl + 1);
268  }
269  }
270  }
271 }
272 
273 static void raddict_export(uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_t *dict)
274 {
275  if (count) *count = 0;
276  if (low) *low = UINTPTR_MAX;
277  if (high) *high = 0;
278 
279  _raddict_export(dict, count, low, high, fr_dict_root(dict), 0);
280 }
281 
282 /**
283  *
284  * @hidecallgraph
285  */
286 int main(int argc, char *argv[])
287 {
288  char const *dict_dir = DICTDIR;
289  char c;
290  int ret = 0;
291  bool found = false;
292  bool export = false;
293  bool file_export = false;
294  char const *protocol = NULL;
295 
296  TALLOC_CTX *autofree;
297 
298  /*
299  * Must be called first, so the handler is called last
300  */
302 
304 
305 #ifndef NDEBUG
306  if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
307  fr_perror("radict - Fault setup");
308  fr_exit(EXIT_FAILURE);
309  }
310 #endif
311 
312  talloc_set_log_stderr();
313 
314  fr_debug_lvl = 1;
315 
316  while ((c = getopt(argc, argv, "cfED:p:VxhH")) != -1) switch (c) {
317  case 'c':
319  break;
320 
321  case 'H':
322  print_headers = true;
323  break;
324 
325  case 'f':
326  file_export = true;
327  break;
328 
329  case 'E':
330  export = true;
331  break;
332 
333  case 'D':
334  dict_dir = optarg;
335  break;
336 
337  case 'p':
338  protocol = optarg;
339  break;
340 
341  case 'V':
342  print_values = true;
343  break;
344 
345  case 'x':
346  fr_log_fp = stdout;
347  fr_debug_lvl++;
348  break;
349 
350  case 'h':
351  default:
352  usage();
353  found = true;
354  goto finish;
355  }
356  argc -= optind;
357  argv += optind;
358 
359  /*
360  * Mismatch between the binary and the libraries it depends on
361  */
363  fr_perror("radict - library mismatch");
364  ret = 1;
365  goto finish;
366  }
367 
368  if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
369  fr_perror("radict - Global context init failed");
370  ret = 1;
371  goto finish;
372  }
373 
374  INFO("Loading dictionary: %s/%s", dict_dir, FR_DICTIONARY_FILE);
375 
377  fr_perror("radict - Loading internal dictionary failed");
378  ret = 1;
379  goto finish;
380  }
381 
382  if (load_dicts(dict_dir, protocol) < 0) {
383  fr_perror("radict - Loading dictionaries failed");
384  ret = 1;
385  goto finish;
386  }
387 
388  if (dict_end == dicts) {
389  fr_perror("radict - No dictionaries loaded");
390  ret = 1;
391  goto finish;
392  }
393 
394  if (print_headers) switch(output_format) {
395  case RADICT_OUT_CSV:
396  printf("Dictionary,OID,Attribute,ID,Type,Flags\n");
397  break;
398 
399  case RADICT_OUT_FANCY:
400  default:
401  printf("Dictionary\tOID\tAttribute\tID\tType\tFlags\n");
402  }
403 
404  if (file_export) {
405  fr_dict_t **dict_p = dicts;
406 
407  do {
408  if (protocol && (strcasecmp(fr_dict_root(*dict_p)->name, protocol) == 0)) {
409  fr_dict_export(*dict_p);
410  }
411  } while (++dict_p < dict_end);
412  }
413 
414  if (export) {
415  fr_dict_t **dict_p = dicts;
416 
417  do {
418  uint64_t count;
419  uintptr_t high;
420  uintptr_t low;
421 
422  raddict_export(&count, &low, &high, *dict_p);
423  DEBUG2("Attribute count %" PRIu64, count);
424  DEBUG2("Memory allocd %zu (bytes)", talloc_total_size(*dict_p));
425  DEBUG2("Memory spread %zu (bytes)", (size_t) (high - low));
426  } while (++dict_p < dict_end);
427  }
428 
429  while (argc-- > 0) {
430  char *attr;
431  fr_dict_attr_t const *da;
432  fr_dict_t **dict_p = dicts;
433 
434  attr = *argv++;
435 
436 
437  /*
438  * Loop through all the dicts. An attribute may
439  * exist in multiple dictionaries.
440  */
441  do {
442  DEBUG2("Looking for \"%s\" in dict \"%s\"", attr, fr_dict_root(*dict_p)->name);
443 
444  da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_p), attr);
445  if (da) {
446  da_print_info_td(*dict_p, da);
447  found = true;
448  }
449  } while (++dict_p < dict_end);
450  }
451 
452 finish:
453  /*
454  * Release our references on all the dicts
455  * we loaded.
456  */
457  {
458  fr_dict_t **dict_p = dicts;
459 
460  do {
461  fr_dict_free(dict_p, __FILE__);
462  } while (++dict_p < dict_end);
463  }
464  if (talloc_free(autofree) < 0) fr_perror("radict - Error freeing dictionaries");
465 
466  /*
467  * Ensure our atexit handlers run before any other
468  * atexit handlers registered by third party libraries.
469  */
471 
472  return found ? ret : 64;
473 }
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:444
#define DIAG_ON(_x)
Definition: build.h:419
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define DIAG_OFF(_x)
Definition: build.h:418
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:1215
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition: debug.h:226
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:1970
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:1997
fr_value_box_t const * value
Enum value (what name maps to).
Definition: dict.h:213
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition: dict_util.c:3581
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:2203
@ FR_DICT_ATTR_EXT_ENUMV
Enumeration values.
Definition: dict.h:166
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:210
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:3984
Value of an enumerated attribute.
Definition: dict.h:209
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition: dict_ext.h:120
fr_hash_table_t * value_by_name
Lookup an enumeration value by name.
Definition: dict_ext.h:89
Attribute extension - Holds enumeration values.
Definition: dict_ext.h:87
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:672
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:620
Stores the state of the current iteration operation.
Definition: hash.h:41
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:42
FILE * fr_log_fp
Definition: log.c:41
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:876
static TALLOC_CTX * autofree
Definition: radclient-ng.c:104
int main(int argc, char *argv[])
Definition: radict.c:286
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:273
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:232
#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_OUT(_start, _len_or_end)
return count
Definition: module.c:175
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
#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