]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1c2facd1 RW |
2 | /* |
3 | * Copyright (C) 2018 NetDEF, Inc. | |
4 | * Renato Westphal | |
1c2facd1 RW |
5 | */ |
6 | ||
7 | #define REALLY_NEED_PLAIN_GETOPT 1 | |
8 | ||
9 | #include <zebra.h> | |
10 | ||
11 | #include <unistd.h> | |
12 | ||
13 | #include "yang.h" | |
14 | #include "northbound.h" | |
15 | ||
f5f0a0e3 RW |
16 | static bool static_cbs; |
17 | ||
1c2facd1 RW |
18 | static void __attribute__((noreturn)) usage(int status) |
19 | { | |
88292d69 | 20 | extern const char *__progname; |
f5f0a0e3 | 21 | fprintf(stderr, "usage: %s [-h] [-s] [-p path] MODULE\n", __progname); |
1c2facd1 RW |
22 | exit(status); |
23 | } | |
24 | ||
25 | static struct nb_callback_info { | |
26 | int operation; | |
27 | bool optional; | |
28 | char return_type[32]; | |
29 | char return_value[32]; | |
30 | char arguments[128]; | |
31 | } nb_callbacks[] = { | |
32 | { | |
33 | .operation = NB_OP_CREATE, | |
34 | .return_type = "int ", | |
35 | .return_value = "NB_OK", | |
60ee8be1 | 36 | .arguments = "struct nb_cb_create_args *args", |
1c2facd1 RW |
37 | }, |
38 | { | |
39 | .operation = NB_OP_MODIFY, | |
40 | .return_type = "int ", | |
41 | .return_value = "NB_OK", | |
60ee8be1 | 42 | .arguments = "struct nb_cb_modify_args *args", |
1c2facd1 RW |
43 | }, |
44 | { | |
95ce849b | 45 | .operation = NB_OP_DESTROY, |
1c2facd1 RW |
46 | .return_type = "int ", |
47 | .return_value = "NB_OK", | |
60ee8be1 | 48 | .arguments = "struct nb_cb_destroy_args *args", |
1c2facd1 RW |
49 | }, |
50 | { | |
51 | .operation = NB_OP_MOVE, | |
52 | .return_type = "int ", | |
53 | .return_value = "NB_OK", | |
60ee8be1 | 54 | .arguments = "struct nb_cb_move_args *args", |
1c2facd1 RW |
55 | }, |
56 | { | |
57 | .operation = NB_OP_APPLY_FINISH, | |
58 | .optional = true, | |
59 | .return_type = "void ", | |
60 | .return_value = "", | |
60ee8be1 | 61 | .arguments = "struct nb_cb_apply_finish_args *args", |
1c2facd1 RW |
62 | }, |
63 | { | |
64 | .operation = NB_OP_GET_ELEM, | |
65 | .return_type = "struct yang_data *", | |
66 | .return_value = "NULL", | |
60ee8be1 | 67 | .arguments = "struct nb_cb_get_elem_args *args", |
1c2facd1 RW |
68 | }, |
69 | { | |
70 | .operation = NB_OP_GET_NEXT, | |
71 | .return_type = "const void *", | |
72 | .return_value = "NULL", | |
60ee8be1 | 73 | .arguments = "struct nb_cb_get_next_args *args", |
1c2facd1 RW |
74 | }, |
75 | { | |
76 | .operation = NB_OP_GET_KEYS, | |
77 | .return_type = "int ", | |
78 | .return_value = "NB_OK", | |
60ee8be1 | 79 | .arguments = "struct nb_cb_get_keys_args *args", |
1c2facd1 RW |
80 | }, |
81 | { | |
82 | .operation = NB_OP_LOOKUP_ENTRY, | |
83 | .return_type = "const void *", | |
84 | .return_value = "NULL", | |
60ee8be1 | 85 | .arguments = "struct nb_cb_lookup_entry_args *args", |
1c2facd1 RW |
86 | }, |
87 | { | |
88 | .operation = NB_OP_RPC, | |
89 | .return_type = "int ", | |
90 | .return_value = "NB_OK", | |
60ee8be1 | 91 | .arguments = "struct nb_cb_rpc_args *args", |
1c2facd1 RW |
92 | }, |
93 | { | |
94 | /* sentinel */ | |
95 | .operation = -1, | |
96 | }, | |
97 | }; | |
98 | ||
99 | static void replace_hyphens_by_underscores(char *str) | |
100 | { | |
101 | char *p; | |
102 | ||
103 | p = str; | |
104 | while ((p = strchr(p, '-')) != NULL) | |
105 | *p++ = '_'; | |
106 | } | |
107 | ||
3bb513c3 | 108 | static void generate_callback_name(const struct lysc_node *snode, |
1c2facd1 RW |
109 | enum nb_operation operation, char *buffer, |
110 | size_t size) | |
111 | { | |
112 | struct list *snodes; | |
113 | struct listnode *ln; | |
114 | ||
115 | snodes = list_new(); | |
3bb513c3 | 116 | for (; snode; snode = snode->parent) { |
1c2facd1 | 117 | /* Skip schema-only snodes. */ |
db452508 RW |
118 | if (CHECK_FLAG(snode->nodetype, LYS_USES | LYS_CHOICE | LYS_CASE |
119 | | LYS_INPUT | |
120 | | LYS_OUTPUT)) | |
1c2facd1 RW |
121 | continue; |
122 | ||
3bb513c3 | 123 | listnode_add_head(snodes, (void *)snode); |
1c2facd1 RW |
124 | } |
125 | ||
126 | memset(buffer, 0, size); | |
127 | for (ALL_LIST_ELEMENTS_RO(snodes, ln, snode)) { | |
128 | strlcat(buffer, snode->name, size); | |
129 | strlcat(buffer, "_", size); | |
130 | } | |
131 | strlcat(buffer, nb_operation_name(operation), size); | |
132 | list_delete(&snodes); | |
133 | ||
134 | replace_hyphens_by_underscores(buffer); | |
135 | } | |
136 | ||
f5f0a0e3 RW |
137 | static void generate_prototype(const struct nb_callback_info *ncinfo, |
138 | const char *cb_name) | |
139 | { | |
140 | printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); | |
141 | } | |
142 | ||
3bb513c3 | 143 | static int generate_prototypes(const struct lysc_node *snode, void *arg) |
f5f0a0e3 RW |
144 | { |
145 | switch (snode->nodetype) { | |
146 | case LYS_CONTAINER: | |
147 | case LYS_LEAF: | |
148 | case LYS_LEAFLIST: | |
149 | case LYS_LIST: | |
150 | case LYS_NOTIF: | |
151 | case LYS_RPC: | |
152 | break; | |
153 | default: | |
154 | return YANG_ITER_CONTINUE; | |
155 | } | |
156 | ||
157 | for (struct nb_callback_info *cb = &nb_callbacks[0]; | |
158 | cb->operation != -1; cb++) { | |
159 | char cb_name[BUFSIZ]; | |
160 | ||
161 | if (cb->optional | |
162 | || !nb_operation_is_valid(cb->operation, snode)) | |
163 | continue; | |
164 | ||
3bb513c3 CH |
165 | generate_callback_name(snode, cb->operation, cb_name, |
166 | sizeof(cb_name)); | |
f5f0a0e3 RW |
167 | generate_prototype(cb, cb_name); |
168 | } | |
169 | ||
170 | return YANG_ITER_CONTINUE; | |
171 | } | |
172 | ||
c681616f RZ |
173 | static void generate_callback(const struct nb_callback_info *ncinfo, |
174 | const char *cb_name) | |
175 | { | |
f5f0a0e3 | 176 | printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "", |
c681616f RZ |
177 | ncinfo->return_type, cb_name, ncinfo->arguments); |
178 | ||
179 | switch (ncinfo->operation) { | |
180 | case NB_OP_CREATE: | |
181 | case NB_OP_MODIFY: | |
182 | case NB_OP_DESTROY: | |
183 | case NB_OP_MOVE: | |
1c7f6245 | 184 | printf("\tswitch (args->event) {\n" |
c681616f RZ |
185 | "\tcase NB_EV_VALIDATE:\n" |
186 | "\tcase NB_EV_PREPARE:\n" | |
187 | "\tcase NB_EV_ABORT:\n" | |
188 | "\tcase NB_EV_APPLY:\n" | |
189 | "\t\t/* TODO: implement me. */\n" | |
190 | "\t\tbreak;\n" | |
191 | "\t}\n\n" | |
192 | ); | |
193 | break; | |
194 | ||
195 | default: | |
196 | printf("\t/* TODO: implement me. */\n"); | |
197 | break; | |
198 | } | |
199 | ||
200 | printf("\treturn %s;\n}\n\n", ncinfo->return_value); | |
201 | } | |
202 | ||
3bb513c3 | 203 | static int generate_callbacks(const struct lysc_node *snode, void *arg) |
1c2facd1 RW |
204 | { |
205 | bool first = true; | |
206 | ||
207 | switch (snode->nodetype) { | |
208 | case LYS_CONTAINER: | |
209 | case LYS_LEAF: | |
210 | case LYS_LEAFLIST: | |
211 | case LYS_LIST: | |
212 | case LYS_NOTIF: | |
213 | case LYS_RPC: | |
214 | break; | |
215 | default: | |
e0ccfad2 | 216 | return YANG_ITER_CONTINUE; |
1c2facd1 RW |
217 | } |
218 | ||
219 | for (struct nb_callback_info *cb = &nb_callbacks[0]; | |
220 | cb->operation != -1; cb++) { | |
221 | char cb_name[BUFSIZ]; | |
222 | ||
223 | if (cb->optional | |
224 | || !nb_operation_is_valid(cb->operation, snode)) | |
225 | continue; | |
226 | ||
227 | if (first) { | |
228 | char xpath[XPATH_MAXLEN]; | |
229 | ||
230 | yang_snode_get_path(snode, YANG_PATH_DATA, xpath, | |
231 | sizeof(xpath)); | |
232 | ||
233 | printf("/*\n" | |
234 | " * XPath: %s\n" | |
235 | " */\n", | |
236 | xpath); | |
237 | first = false; | |
238 | } | |
239 | ||
3bb513c3 CH |
240 | generate_callback_name(snode, cb->operation, cb_name, |
241 | sizeof(cb_name)); | |
c681616f | 242 | generate_callback(cb, cb_name); |
1c2facd1 | 243 | } |
e0ccfad2 RW |
244 | |
245 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
246 | } |
247 | ||
3bb513c3 | 248 | static int generate_nb_nodes(const struct lysc_node *snode, void *arg) |
1c2facd1 RW |
249 | { |
250 | bool first = true; | |
251 | ||
252 | switch (snode->nodetype) { | |
253 | case LYS_CONTAINER: | |
254 | case LYS_LEAF: | |
255 | case LYS_LEAFLIST: | |
256 | case LYS_LIST: | |
257 | case LYS_NOTIF: | |
258 | case LYS_RPC: | |
259 | break; | |
260 | default: | |
e0ccfad2 | 261 | return YANG_ITER_CONTINUE; |
1c2facd1 RW |
262 | } |
263 | ||
264 | for (struct nb_callback_info *cb = &nb_callbacks[0]; | |
265 | cb->operation != -1; cb++) { | |
266 | char cb_name[BUFSIZ]; | |
267 | ||
268 | if (cb->optional | |
269 | || !nb_operation_is_valid(cb->operation, snode)) | |
270 | continue; | |
271 | ||
272 | if (first) { | |
273 | char xpath[XPATH_MAXLEN]; | |
274 | ||
275 | yang_snode_get_path(snode, YANG_PATH_DATA, xpath, | |
276 | sizeof(xpath)); | |
277 | ||
278 | printf("\t\t{\n" | |
279 | "\t\t\t.xpath = \"%s\",\n", | |
280 | xpath); | |
13080468 | 281 | printf("\t\t\t.cbs = {\n"); |
1c2facd1 RW |
282 | first = false; |
283 | } | |
284 | ||
3bb513c3 CH |
285 | generate_callback_name(snode, cb->operation, cb_name, |
286 | sizeof(cb_name)); | |
13080468 RZ |
287 | printf("\t\t\t\t.%s = %s,\n", nb_operation_name(cb->operation), |
288 | cb_name); | |
1c2facd1 RW |
289 | } |
290 | ||
13080468 RZ |
291 | if (!first) { |
292 | printf("\t\t\t}\n"); | |
1c2facd1 | 293 | printf("\t\t},\n"); |
13080468 | 294 | } |
e0ccfad2 RW |
295 | |
296 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
297 | } |
298 | ||
299 | int main(int argc, char *argv[]) | |
300 | { | |
88292d69 | 301 | const char *search_path = NULL; |
1c2facd1 RW |
302 | struct yang_module *module; |
303 | char module_name_underscores[64]; | |
88292d69 | 304 | struct stat st; |
1c2facd1 RW |
305 | int opt; |
306 | ||
f5f0a0e3 | 307 | while ((opt = getopt(argc, argv, "hp:s")) != -1) { |
1c2facd1 RW |
308 | switch (opt) { |
309 | case 'h': | |
310 | usage(EXIT_SUCCESS); | |
311 | /* NOTREACHED */ | |
88292d69 RZ |
312 | case 'p': |
313 | if (stat(optarg, &st) == -1) { | |
314 | fprintf(stderr, | |
315 | "error: invalid search path '%s': %s\n", | |
316 | optarg, strerror(errno)); | |
317 | exit(EXIT_FAILURE); | |
318 | } | |
319 | if (S_ISDIR(st.st_mode) == 0) { | |
320 | fprintf(stderr, | |
321 | "error: search path is not directory"); | |
322 | exit(EXIT_FAILURE); | |
323 | } | |
324 | ||
325 | search_path = optarg; | |
326 | break; | |
f5f0a0e3 RW |
327 | case 's': |
328 | static_cbs = true; | |
329 | break; | |
1c2facd1 RW |
330 | default: |
331 | usage(EXIT_FAILURE); | |
332 | /* NOTREACHED */ | |
333 | } | |
334 | } | |
335 | argc -= optind; | |
336 | argv += optind; | |
337 | if (argc != 1) | |
338 | usage(EXIT_FAILURE); | |
339 | ||
3bb513c3 | 340 | yang_init(false, true); |
1c2facd1 | 341 | |
88292d69 RZ |
342 | if (search_path) |
343 | ly_ctx_set_searchdir(ly_native_ctx, search_path); | |
344 | ||
544ca69a RW |
345 | /* Load all FRR native models to ensure all augmentations are loaded. */ |
346 | yang_module_load_all(); | |
3bb513c3 | 347 | |
544ca69a RW |
348 | module = yang_module_find(argv[0]); |
349 | if (!module) | |
350 | /* Non-native FRR module (e.g. modules from unit tests). */ | |
351 | module = yang_module_load(argv[0]); | |
352 | ||
3bb513c3 CH |
353 | yang_init_loading_complete(); |
354 | ||
544ca69a RW |
355 | /* Create a nb_node for all YANG schema nodes. */ |
356 | nb_nodes_create(); | |
1c2facd1 | 357 | |
f5f0a0e3 RW |
358 | /* Generate callback prototypes. */ |
359 | if (!static_cbs) { | |
360 | printf("/* prototypes */\n"); | |
8d869d37 | 361 | yang_snodes_iterate(module->info, generate_prototypes, 0, NULL); |
f5f0a0e3 RW |
362 | printf("\n"); |
363 | } | |
364 | ||
1c2facd1 | 365 | /* Generate callback functions. */ |
8d869d37 | 366 | yang_snodes_iterate(module->info, generate_callbacks, 0, NULL); |
1c2facd1 RW |
367 | |
368 | strlcpy(module_name_underscores, module->name, | |
369 | sizeof(module_name_underscores)); | |
370 | replace_hyphens_by_underscores(module_name_underscores); | |
371 | ||
372 | /* Generate frr_yang_module_info array. */ | |
373 | printf("/* clang-format off */\n" | |
374 | "const struct frr_yang_module_info %s_info = {\n" | |
375 | "\t.name = \"%s\",\n" | |
376 | "\t.nodes = {\n", | |
377 | module_name_underscores, module->name); | |
8d869d37 | 378 | yang_snodes_iterate(module->info, generate_nb_nodes, 0, NULL); |
1c2facd1 RW |
379 | printf("\t\t{\n" |
380 | "\t\t\t.xpath = NULL,\n" | |
381 | "\t\t},\n"); | |
382 | printf("\t}\n" | |
383 | "};\n"); | |
384 | ||
385 | /* Cleanup and exit. */ | |
544ca69a | 386 | nb_nodes_delete(); |
1c2facd1 RW |
387 | yang_terminate(); |
388 | ||
389 | return 0; | |
390 | } |