The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
radconf2json.c
Go to the documentation of this file.
1/*
2 * radconf2json.c Dump a parsed FreeRADIUS v4 configuration tree as JSON.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 * Copyright (C) 2026 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
19 */
20
21RCSID("$Id: 120af6aa734dd642bb5ab6f36431a57c50d3a39c $")
22
23#include <freeradius-devel/server/base.h>
24#include <freeradius-devel/server/cf_file.h>
25#include <freeradius-devel/server/cf_util.h>
26#include <freeradius-devel/server/main_config.h>
27#include <freeradius-devel/server/module.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/server/virtual_servers.h>
30#include <freeradius-devel/unlang/base.h>
31#include <freeradius-devel/util/atexit.h>
32#include <freeradius-devel/util/debug.h>
33#include <freeradius-devel/util/syserror.h>
34
35#ifdef WITH_TLS
36# include <freeradius-devel/tls/base.h>
37# include <freeradius-devel/tls/version.h>
38#endif
39
40#include <json-c/json.h>
41
42#ifdef HAVE_GETOPT_H
43# include <getopt.h>
44#endif
45
46#define EXIT_WITH_FAILURE \
47 do { \
48 rcode = EXIT_FAILURE; \
49 goto finish; \
50 } while (0)
51
52char const *radiusd_version = RADIUSD_VERSION_BUILD("radconf2json");
53
54/*
55 * Token-name look-ups. `fr_token_to_enum_str()` (src/lib/util/token.c)
56 * returns the source-identifier form ("T_BARE_WORD", "T_OP_SET", ...)
57 * which is what the converter's rule layer greps for. `fr_tokens[]`
58 * gives the operator-character form (":=", "=", "==", ...) for the
59 * operator side; we emit that as `op`.
60 */
61#define quote_name(_t) fr_token_to_enum_str(_t)
62#define op_name(_op) (((unsigned int)(_op) < T_TOKEN_LAST && fr_tokens[(_op)]) ? fr_tokens[(_op)] : "?")
63
64static struct json_object *build_location(char const *filename, int lineno)
65{
66 struct json_object *loc;
67
68 if (!filename && lineno == 0) return NULL;
69
70 loc = json_object_new_object();
71 json_object_object_add(loc, "filename", filename ? json_object_new_string(filename) : NULL);
72 json_object_object_add(loc, "lineno", json_object_new_int(lineno));
73 return loc;
74}
75
76static struct json_object *build_comment(CONF_COMMENT const *c)
77{
78 struct json_object *o = json_object_new_object();
79 char const *filename = cf_filename(c);
80 int lineno = cf_lineno(c);
81
82 json_object_object_add(o, "type", json_object_new_string("comment"));
83 json_object_object_add(o, "text", cf_comment_text(c) ? json_object_new_string(cf_comment_text(c)) : NULL);
84 json_object_object_add(o, "location", build_location(filename, lineno));
85 return o;
86}
87
88static struct json_object *build_pair(CONF_PAIR const *cp)
89{
90 struct json_object *o = json_object_new_object();
91 char const *value = cf_pair_value(cp);
92
93 json_object_object_add(o, "type", json_object_new_string("pair"));
94 json_object_object_add(o, "attr", cf_pair_attr(cp) ? json_object_new_string(cf_pair_attr(cp)) : NULL);
95 json_object_object_add(o, "lhs_quote", json_object_new_string(quote_name(cf_pair_attr_quote(cp))));
96 json_object_object_add(o, "op", json_object_new_string(op_name(cf_pair_operator(cp))));
97 json_object_object_add(o, "value", value ? json_object_new_string(value) : NULL);
98 json_object_object_add(o, "rhs_quote", json_object_new_string(quote_name(cf_pair_value_quote(cp))));
99 json_object_object_add(o, "location", build_location(cf_filename(cp), cf_lineno(cp)));
100
101 return o;
102}
103
104static struct json_object *build_section(CONF_SECTION const *cs)
105{
106 struct json_object *o = json_object_new_object();
107 struct json_object *children = json_object_new_array();
108 char const *name2 = cf_section_name2(cs);
109
110 json_object_object_add(o, "type", json_object_new_string("section"));
111 json_object_object_add(o, "name1", cf_section_name1(cs) ? json_object_new_string(cf_section_name1(cs)) : NULL);
112 json_object_object_add(o, "name2", name2 ? json_object_new_string(name2) : NULL);
113 json_object_object_add(o, "name2_quote", json_object_new_string(quote_name(cf_section_name2_quote(cs))));
114 json_object_object_add(o, "location", build_location(cf_filename(cs), cf_lineno(cs)));
115
116 cf_item_foreach(cs, ci)
117 {
118 if (cf_item_is_section(ci)) {
119 json_object_array_add(children, build_section(cf_item_to_section(UNCONST(CONF_ITEM *, ci))));
120 } else if (cf_item_is_pair(ci)) {
121 json_object_array_add(children, build_pair(cf_item_to_pair(UNCONST(CONF_ITEM *, ci))));
122 } else if (cf_item_is_comment(ci)) {
123 json_object_array_add(children, build_comment(cf_item_to_comment(UNCONST(CONF_ITEM *, ci))));
124 }
125 }
126
127 json_object_object_add(o, "children", children);
128 return o;
129}
130
131static NEVER_RETURNS void usage(int rcode)
132{
133 FILE *fp = (rcode == 0) ? stdout : stderr;
134
135 fprintf(fp, "Usage: radconf2json [options]\n"
136 " -d <raddb> Set raddb directory.\n"
137 " -D <dict> Set dictionary directory.\n"
138 " -n <name> Read <name>.conf instead of radiusd.conf.\n"
139 " -o <file> Write JSON to <file> (default stdout).\n"
140 " -x Enable debug output (repeatable).\n"
141 " -X Verbose debug output.\n"
142 " -h This help.\n");
143 exit(rcode);
144}
145
146int main(int argc, char *argv[])
147{
148 int c;
149 int rcode = EXIT_SUCCESS;
150 char const *output_file = NULL;
151 TALLOC_CTX *autofree;
152 main_config_t *config = NULL;
153 fr_dict_t *dict = NULL;
154 struct json_object *root = NULL;
155
157
159 if (!config) {
160 fr_perror("radconf2json");
161 fr_exit_now(EXIT_FAILURE);
162 }
163
164 main_config_name_set_default(config, "radiusd", false);
165
167 fr_debug_lvl = 0;
169
171 default_log.fd = STDERR_FILENO;
173
174 while ((c = getopt(argc, argv, "d:D:hn:o:xX")) != -1) {
175 switch (c) {
176 case 'd':
178 break;
179
180 case 'D':
182 break;
183
184 case 'h':
185 usage(EXIT_SUCCESS);
186
187 case 'n':
188 config->name = optarg;
189 break;
190
191 case 'o':
192 output_file = optarg;
193 break;
194
195 case 'X':
196 fr_debug_lvl += 2;
197 break;
198
199 case 'x':
200 fr_debug_lvl++;
201 break;
202
203 default:
204 usage(EXIT_FAILURE);
205 }
206 }
207
208#ifdef WITH_TLS
209 if (fr_openssl_init() < 0) {
210 fr_perror("%s", config->name);
212 }
213#endif
214
216 fr_perror("%s", config->name);
218 }
219
220 /*
221 * Opt in to comment preservation - the runtime server parser
222 * drops them, but we want a faithful round-trip through JSON.
223 */
225
226 modules_init(config->lib_dir);
227
228 if (!fr_dict_global_ctx_init(NULL, true, config->dict_dir)) {
229 fr_perror("%s", config->name);
231 }
232
234 fr_perror("%s", config->name);
236 }
237
238#ifdef WITH_TLS
239 if (fr_tls_dict_init() < 0) EXIT_WITH_FAILURE;
240#endif
241
242 if (fr_dict_read(dict, config->confdir, FR_DICTIONARY_FILE) == -1) {
243 PERROR("Failed to initialize the dictionaries");
245 }
246
247 if (request_global_init() < 0) {
248 fr_perror("%s", config->name);
250 }
251
252 if (unlang_global_init() < 0) {
253 fr_perror("%s", config->name);
255 }
256
257 if (modules_rlm_init() < 0) {
258 fr_perror("%s", config->name);
260 }
261
262 if (virtual_servers_init() < 0) {
263 fr_perror("%s", config->name);
265 }
266
267 if (main_config_init(config) < 0) {
269 }
270
271 root = build_section(config->root_cs);
272
273 {
274 char const *json_str =
275 json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE);
276
277 if (output_file) {
278 FILE *out = fopen(output_file, "w");
279 if (!out) {
280 fprintf(stderr, "Failed opening %s: %s\n", output_file, fr_syserror(errno));
282 }
283 fputs(json_str, out);
284 fputc('\n', out);
285 fclose(out);
286 } else {
287 fputs(json_str, stdout);
288 fputc('\n', stdout);
289 }
290 }
291
292finish:
293 if (root) json_object_put(root);
295 return rcode;
296}
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:512
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:334
A # ... comment line preserved verbatim from the input.
Definition cf_priv.h:145
Common header for all CONF_* types.
Definition cf_priv.h:54
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:640
fr_token_t cf_pair_attr_quote(CONF_PAIR const *pair)
Return the value (lhs) quoting of a pair.
Definition cf_util.c:1729
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1306
char const * cf_section_name1(CONF_SECTION const *cs)
Return the first identifier of a CONF_SECTION.
Definition cf_util.c:1292
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
bool cf_item_is_comment(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_COMMENT.
Definition cf_util.c:771
void cf_preserve_comments_set(bool preserve)
Control whether the CF parser preserves # ... comments.
Definition cf_util.c:807
CONF_COMMENT * cf_item_to_comment(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_COMMENT (asserts on type mismatch).
Definition cf_util.c:779
fr_token_t cf_pair_operator(CONF_PAIR const *pair)
Return the operator of a pair.
Definition cf_util.c:1714
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1744
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:626
char const * cf_comment_text(CONF_COMMENT const *c)
Definition cf_util.c:794
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:672
fr_token_t cf_section_name2_quote(CONF_SECTION const *cs)
Return the quoting of the name2 identifier.
Definition cf_util.c:1351
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1700
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1684
#define cf_lineno(_cf)
Definition cf_util.h:121
#define cf_item_foreach(_parent, _iter)
Iterate over every child item of a CONF_SECTION (or any CONF_ITEM that has children).
Definition cf_util.h:109
#define cf_filename(_cf)
Definition cf_util.h:124
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition debug.c:1051
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent))
(Re-)Initialize the special internal dictionary
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename))
Read supplementary attribute definitions into an existing dictionary.
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:4707
Test enumeration values.
Definition dict_test.h:92
static TALLOC_CTX * autofree
Definition fuzzer.c:44
#define PERROR(_fmt,...)
Definition log.h:228
int unlang_global_init(void)
Definition base.c:158
int fr_debug_lvl
Definition log.c:41
fr_log_t default_log
Definition log.c:306
@ L_DST_STDERR
Log to stderr.
Definition log.h:78
int main_config_free(main_config_t **config)
main_config_t * main_config_alloc(TALLOC_CTX *ctx)
Allocate a main_config_t struct, setting defaults.
void main_config_name_set_default(main_config_t *config, char const *name, bool overwrite_config)
Set the server name.
int main_config_init(main_config_t *config)
void main_config_confdir_set(main_config_t *config, char const *name)
Set the global radius config directory.
void main_config_dict_dir_set(main_config_t *config, char const *name)
Set the global dictionary directory.
Main server configuration.
Definition main_config.h:51
int modules_rlm_init(void)
Initialise the module list structure.
static const conf_parser_t config[]
Definition base.c:163
int main(int argc, char *argv[])
#define quote_name(_t)
static struct json_object * build_section(CONF_SECTION const *cs)
static struct json_object * build_location(char const *filename, int lineno)
static struct json_object * build_pair(CONF_PAIR const *cp)
char const * radiusd_version
#define op_name(_op)
#define EXIT_WITH_FAILURE
static struct json_object * build_comment(CONF_COMMENT const *c)
int request_global_init(void)
Definition request.c:596
void modules_init(char const *lib_dir)
Perform global initialisation for modules.
Definition module.c:1951
fr_log_dst_t dst
Log destination.
Definition log.h:94
int fd
File descriptor to write messages to.
Definition log.h:109
bool print_level
sometimes we don't want log levels printed
Definition log.h:103
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:55
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
#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:737
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_VERSION_BUILD(_x)
Create a version string for a utility in the suite of FreeRADIUS utilities.
Definition version.h:58
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
static size_t char ** out
Definition value.h:1030
int virtual_servers_init(void)
Performs global initialisation for the virtual server code.