/src/freeradius-server/src/lib/util/dict_print.c
Line | Count | Source |
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 | | /** Print dictionary attributes, flags, etc... |
18 | | * |
19 | | * @file src/lib/util/dict_print.c |
20 | | * |
21 | | * @copyright 2019 The FreeRADIUS server project |
22 | | */ |
23 | | RCSID("$Id: b36816f2104baa8c131107f058a8cc2d83e55cbc $") |
24 | | |
25 | | #include <freeradius-devel/util/dict_priv.h> |
26 | | #include <freeradius-devel/util/print.h> |
27 | | #include <freeradius-devel/util/proto.h> |
28 | | |
29 | | 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) |
30 | 0 | { |
31 | 0 | fr_sbuff_t our_out = FR_SBUFF(out); |
32 | |
|
33 | 0 | #define FLAG_SET(_flag) if (flags->_flag) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, STRINGIFY(_flag)",") |
34 | |
|
35 | 0 | FLAG_SET(is_root); |
36 | 0 | FLAG_SET(is_unknown); |
37 | 0 | FLAG_SET(is_raw); |
38 | 0 | FLAG_SET(is_alias); |
39 | 0 | FLAG_SET(has_alias); |
40 | 0 | FLAG_SET(internal); |
41 | 0 | FLAG_SET(array); |
42 | 0 | FLAG_SET(has_value); |
43 | 0 | FLAG_SET(counter); |
44 | 0 | FLAG_SET(name_only); |
45 | | |
46 | 0 | if (dict && !flags->extra && flags->subtype) { |
47 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", flags->subtype); |
48 | 0 | FR_SBUFF_IN_CHAR_RETURN(&our_out, ','); |
49 | 0 | } |
50 | | |
51 | 0 | if (flags->length) { |
52 | 0 | switch (type) { |
53 | 0 | case FR_TYPE_FIXED_SIZE: |
54 | | /* |
55 | | * Bit fields are in the dicts as various |
56 | | * `uint*` types. But with special flags |
57 | | * saying they're bit fields. |
58 | | */ |
59 | 0 | if (flags->extra && (flags->subtype == FLAG_BIT_FIELD)) { |
60 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "bit[%u],", flags->length); |
61 | 0 | } |
62 | 0 | break; |
63 | | |
64 | 0 | default: |
65 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "length=%i,", flags->length); |
66 | 0 | break; |
67 | 0 | } |
68 | 0 | } |
69 | 0 | if (flags->extra) { |
70 | 0 | switch (flags->subtype) { |
71 | 0 | case FLAG_KEY_FIELD: |
72 | 0 | FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "key,"); |
73 | 0 | break; |
74 | | |
75 | 0 | case FLAG_BIT_FIELD: |
76 | 0 | break; |
77 | | |
78 | 0 | case FLAG_LENGTH_UINT8: |
79 | 0 | FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "length=uint8,"); |
80 | 0 | break; |
81 | | |
82 | 0 | case FLAG_LENGTH_UINT16: |
83 | 0 | FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "length=uint16,"); |
84 | 0 | break; |
85 | | |
86 | 0 | default: |
87 | 0 | break; |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * Print out the date precision. |
93 | | */ |
94 | 0 | if ((type == FR_TYPE_DATE) || (type == FR_TYPE_TIME_DELTA)) { |
95 | 0 | FR_SBUFF_IN_STRCPY_RETURN(&our_out, |
96 | 0 | fr_table_str_by_value(fr_time_precision_table, flags->flag_time_res, "?")); |
97 | 0 | FR_SBUFF_IN_CHAR_RETURN(&our_out, ','); |
98 | 0 | if (flags->is_unsigned) FR_SBUFF_IN_CHAR_RETURN(&our_out, 'u'); |
99 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "int%d", flags->length << 3); |
100 | 0 | } |
101 | | |
102 | | /* |
103 | | * Remove trailing commas. |
104 | | */ |
105 | 0 | fr_sbuff_trim(&our_out, (bool[UINT8_MAX + 1]){ [','] = true }); |
106 | | |
107 | | /* |
108 | | * Ensure that the buffer is initialized. |
109 | | */ |
110 | 0 | if (fr_sbuff_used(&our_out) == 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '\0'); |
111 | | |
112 | 0 | FR_SBUFF_SET_RETURN(out, &our_out); |
113 | 0 | } |
114 | | |
115 | | /** Build the da_stack for the specified DA and encode the path by name in OID form |
116 | | * |
117 | | * @param[out] out Where to write the OID. |
118 | | * @param[in] ancestor If not NULL, only print OID portion between ancestor and da. |
119 | | * @param[in] da to print OID string for. |
120 | | * @param[in] numeric print the OID components as numbers, not attribute names. |
121 | | * @return |
122 | | * - >0 The number of bytes written to the buffer. |
123 | | * - <= 0 The number of bytes we would have needed to write the |
124 | | * next OID component. |
125 | | */ |
126 | | ssize_t fr_dict_attr_oid_print(fr_sbuff_t *out, |
127 | | fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da, bool numeric) |
128 | 0 | { |
129 | 0 | int i; |
130 | 0 | int depth = 0; |
131 | 0 | fr_da_stack_t da_stack; |
132 | 0 | fr_sbuff_t our_out = FR_SBUFF(out); |
133 | | |
134 | | /* |
135 | | * If the ancestor and the DA match, there's |
136 | | * no OID string to print. |
137 | | */ |
138 | 0 | if ((ancestor == da) || (da->depth == 0)) return 0; |
139 | | |
140 | 0 | if (ancestor && (ancestor->flags.is_root)) ancestor = NULL; |
141 | |
|
142 | 0 | fr_proto_da_stack_build(&da_stack, da); |
143 | | |
144 | | /* |
145 | | * We may have swapped from a known to an unknown |
146 | | * attribute. We still print out the unknown one. |
147 | | */ |
148 | 0 | if (ancestor && da->flags.is_unknown) { |
149 | 0 | fr_assert(da->depth > ancestor->depth); |
150 | |
|
151 | 0 | ancestor = da_stack.da[ancestor->depth - 1]; |
152 | 0 | } |
153 | |
|
154 | 0 | if (ancestor) { |
155 | 0 | if (da_stack.da[ancestor->depth - 1] != ancestor) { |
156 | 0 | fr_strerror_printf("Attribute '%s' is not a descendent of \"%s\"", da->name, ancestor->name); |
157 | 0 | return 0; |
158 | 0 | } |
159 | 0 | depth = ancestor->depth; |
160 | 0 | } |
161 | | |
162 | | /* |
163 | | * We don't print the ancestor, we print the OID |
164 | | * between it and the da. |
165 | | */ |
166 | 0 | if (numeric) { |
167 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", da_stack.da[depth]->attr); |
168 | 0 | for (i = depth + 1; i < (int)da->depth; i++) { |
169 | 0 | FR_SBUFF_IN_CHAR_RETURN(&our_out, '.'); |
170 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", da_stack.da[i]->attr); |
171 | 0 | } |
172 | 0 | } else { |
173 | 0 | FR_SBUFF_IN_STRCPY_RETURN(&our_out, da_stack.da[depth]->name); |
174 | 0 | for (i = depth + 1; i < (int)da->depth; i++) { |
175 | 0 | FR_SBUFF_IN_CHAR_RETURN(&our_out, '.'); |
176 | 0 | FR_SBUFF_IN_STRCPY_RETURN(&our_out, da_stack.da[i]->name); |
177 | 0 | } |
178 | 0 | } |
179 | 0 | FR_SBUFF_SET_RETURN(out, &our_out); |
180 | 0 | } |
181 | | |
182 | | typedef struct { |
183 | | FILE *fp; |
184 | | fr_dict_t const *dict; |
185 | | fr_dict_attr_t const *da; //!< where we started |
186 | | char prefix[256]; |
187 | | char flags[256]; |
188 | | char oid[256]; |
189 | | unsigned int start_depth; |
190 | | } fr_dict_attr_debug_t; |
191 | | |
192 | | static int dict_attr_debug(fr_dict_attr_t const *da, void *uctx) |
193 | 0 | { |
194 | 0 | fr_dict_attr_debug_t *ctx = uctx; |
195 | 0 | fr_hash_iter_t iter; |
196 | 0 | fr_dict_enum_value_t const *enumv; |
197 | 0 | fr_dict_attr_ext_enumv_t *ext; |
198 | | |
199 | | /* |
200 | | * Don't print it twice. |
201 | | */ |
202 | 0 | if (da == ctx->da) return 0; |
203 | | |
204 | 0 | fr_dict_attr_flags_print(&FR_SBUFF_OUT(ctx->flags, sizeof(ctx->flags)), |
205 | 0 | ctx->dict, da->type, &da->flags); |
206 | |
|
207 | 0 | snprintf(ctx->prefix, sizeof(ctx->prefix), |
208 | 0 | "[%02u] 0x%016" PRIxPTR "%*s - ", |
209 | 0 | da->depth, |
210 | 0 | (unsigned long)da, |
211 | 0 | (da->depth - ctx->start_depth) * 4, ""); |
212 | |
|
213 | 0 | fprintf(ctx->fp, "%s%s(%u) %s %s\n", |
214 | 0 | ctx->prefix, |
215 | 0 | da->name, |
216 | 0 | da->attr, |
217 | 0 | fr_type_to_str(da->type), |
218 | 0 | ctx->flags); |
219 | |
|
220 | 0 | dict_attr_ext_debug(ctx->prefix, da); /* Print all the extension debug info */ |
221 | |
|
222 | 0 | ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV); |
223 | 0 | if (!ext || !ext->name_by_value) return 0; |
224 | | |
225 | 0 | for (enumv = fr_hash_table_iter_init(ext->name_by_value, &iter); |
226 | 0 | enumv; |
227 | 0 | enumv = fr_hash_table_iter_next(ext->name_by_value, &iter)) { |
228 | 0 | char *value = fr_asprintf(NULL, "%pV", enumv->value); |
229 | |
|
230 | 0 | fprintf(ctx->fp, "%s %s -> %s\n", |
231 | 0 | ctx->prefix, |
232 | 0 | enumv->name, |
233 | 0 | value); |
234 | 0 | talloc_free(value); |
235 | 0 | } |
236 | |
|
237 | 0 | return 0; |
238 | 0 | } |
239 | | |
240 | | void fr_dict_namespace_debug(FILE *fp, fr_dict_attr_t const *da) |
241 | 0 | { |
242 | 0 | fr_dict_attr_debug_t uctx = { |
243 | 0 | .fp = fp, |
244 | 0 | .dict = fr_dict_by_da(da), |
245 | 0 | .start_depth = da->depth, |
246 | 0 | }; |
247 | 0 | fr_hash_table_t *namespace; |
248 | 0 | fr_hash_iter_t iter; |
249 | 0 | fr_dict_attr_t *our_da; |
250 | |
|
251 | 0 | namespace = dict_attr_namespace(da); |
252 | 0 | if (!namespace) { |
253 | 0 | fprintf(fp, "%s does not have namespace\n", da->name); |
254 | 0 | return; |
255 | 0 | } |
256 | | |
257 | 0 | for (our_da = fr_hash_table_iter_init(namespace, &iter); |
258 | 0 | our_da; |
259 | 0 | our_da = fr_hash_table_iter_next(namespace, &iter)) { |
260 | 0 | dict_attr_debug(our_da, &uctx); |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | | void fr_dict_attr_debug(FILE *fp, fr_dict_attr_t const *da) |
265 | 0 | { |
266 | 0 | fr_dict_attr_debug_t uctx = { |
267 | 0 | .fp = fp, |
268 | 0 | .dict = fr_dict_by_da(da), |
269 | 0 | .start_depth = da->depth, |
270 | 0 | }; |
271 | |
|
272 | 0 | dict_attr_debug(da, &uctx); |
273 | 0 | uctx.da = da; |
274 | |
|
275 | 0 | (void)fr_dict_walk(da, dict_attr_debug, &uctx); |
276 | 0 | } |
277 | | |
278 | | void fr_dict_debug(FILE *fp, fr_dict_t const *dict) |
279 | 0 | { |
280 | 0 | fr_dict_attr_debug(fp, fr_dict_root(dict)); |
281 | 0 | } |
282 | | |
283 | | static int dict_attr_export(fr_dict_attr_t const *da, void *uctx) |
284 | 0 | { |
285 | 0 | fr_dict_attr_debug_t *ctx = uctx; |
286 | |
|
287 | 0 | (void) fr_dict_attr_oid_print(&FR_SBUFF_OUT(ctx->prefix, sizeof(ctx->prefix)), |
288 | 0 | NULL, da, false); |
289 | 0 | (void) fr_dict_attr_oid_print(&FR_SBUFF_OUT(ctx->oid, sizeof(ctx->oid)), |
290 | 0 | NULL, da, true); |
291 | |
|
292 | 0 | *ctx->flags = 0; /* some attributes don't have flags */ |
293 | 0 | fr_dict_attr_flags_print(&FR_SBUFF_OUT(ctx->flags, sizeof(ctx->flags)), |
294 | 0 | ctx->dict, da->type, &da->flags); |
295 | 0 | fprintf(ctx->fp, "ATTRIBUTE\t%-40s\t%-20s\t%s\t%s\n", |
296 | 0 | ctx->prefix, |
297 | 0 | ctx->oid, |
298 | 0 | fr_type_to_str(da->type), |
299 | 0 | ctx->flags); |
300 | |
|
301 | 0 | return 0; |
302 | 0 | } |
303 | | |
304 | | static void fr_dict_attr_export(FILE *fp, fr_dict_attr_t const *da) |
305 | 0 | { |
306 | 0 | fr_dict_attr_debug_t uctx = { |
307 | 0 | .fp = fp, |
308 | 0 | .dict = fr_dict_by_da(da), |
309 | 0 | .start_depth = da->depth |
310 | 0 | }; |
311 | |
|
312 | 0 | dict_attr_export(da, &uctx); |
313 | 0 | (void)fr_dict_walk(da, dict_attr_export, &uctx); |
314 | 0 | } |
315 | | |
316 | | /** Export in the standard form: ATTRIBUTE name oid flags |
317 | | * |
318 | | */ |
319 | | void fr_dict_export(FILE *fp, fr_dict_t const *dict) |
320 | 0 | { |
321 | 0 | fr_dict_attr_export(fp, fr_dict_root(dict)); |
322 | 0 | } |
323 | | |
324 | | void fr_dict_alias_export(FILE *fp, fr_dict_attr_t const *parent) |
325 | 0 | { |
326 | 0 | fr_hash_table_t *namespace; |
327 | 0 | fr_hash_iter_t iter; |
328 | 0 | fr_dict_attr_t *da; |
329 | 0 | char buffer [256]; |
330 | |
|
331 | 0 | namespace = dict_attr_namespace(parent); |
332 | 0 | if (!namespace) { |
333 | 0 | fprintf(fp, "%s does not have namespace\n", parent->name); |
334 | 0 | return; |
335 | 0 | } |
336 | | |
337 | 0 | for (da = fr_hash_table_iter_init(namespace, &iter); |
338 | 0 | da; |
339 | 0 | da = fr_hash_table_iter_next(namespace, &iter)) { |
340 | 0 | fr_dict_attr_t const *ref; |
341 | |
|
342 | 0 | if (!da->flags.is_alias) continue; |
343 | | |
344 | 0 | if (!fr_type_is_leaf(da->type)) continue; |
345 | | |
346 | 0 | ref = fr_dict_attr_ref(da); |
347 | 0 | if (!ref) continue; |
348 | | |
349 | 0 | if (da->depth == ref->depth) continue; |
350 | | |
351 | | #ifdef STATIC_ANALYZER |
352 | | buffer[0] = '\0'; |
353 | | #endif |
354 | | |
355 | 0 | (void) fr_dict_attr_oid_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), |
356 | 0 | NULL, ref, false); |
357 | |
|
358 | 0 | fprintf(fp, "%-40s\t%s\n", da->name, buffer); |
359 | 0 | } |
360 | 0 | } |