]> git.proxmox.com Git - mirror_frr.git/blob - tools/gen_northbound_callbacks.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tools / gen_northbound_callbacks.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2018 NetDEF, Inc.
4 * Renato Westphal
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
16 static bool static_cbs;
17
18 static void __attribute__((noreturn)) usage(int status)
19 {
20 extern const char *__progname;
21 fprintf(stderr, "usage: %s [-h] [-s] [-p path] MODULE\n", __progname);
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",
36 .arguments = "struct nb_cb_create_args *args",
37 },
38 {
39 .operation = NB_OP_MODIFY,
40 .return_type = "int ",
41 .return_value = "NB_OK",
42 .arguments = "struct nb_cb_modify_args *args",
43 },
44 {
45 .operation = NB_OP_DESTROY,
46 .return_type = "int ",
47 .return_value = "NB_OK",
48 .arguments = "struct nb_cb_destroy_args *args",
49 },
50 {
51 .operation = NB_OP_MOVE,
52 .return_type = "int ",
53 .return_value = "NB_OK",
54 .arguments = "struct nb_cb_move_args *args",
55 },
56 {
57 .operation = NB_OP_APPLY_FINISH,
58 .optional = true,
59 .return_type = "void ",
60 .return_value = "",
61 .arguments = "struct nb_cb_apply_finish_args *args",
62 },
63 {
64 .operation = NB_OP_GET_ELEM,
65 .return_type = "struct yang_data *",
66 .return_value = "NULL",
67 .arguments = "struct nb_cb_get_elem_args *args",
68 },
69 {
70 .operation = NB_OP_GET_NEXT,
71 .return_type = "const void *",
72 .return_value = "NULL",
73 .arguments = "struct nb_cb_get_next_args *args",
74 },
75 {
76 .operation = NB_OP_GET_KEYS,
77 .return_type = "int ",
78 .return_value = "NB_OK",
79 .arguments = "struct nb_cb_get_keys_args *args",
80 },
81 {
82 .operation = NB_OP_LOOKUP_ENTRY,
83 .return_type = "const void *",
84 .return_value = "NULL",
85 .arguments = "struct nb_cb_lookup_entry_args *args",
86 },
87 {
88 .operation = NB_OP_RPC,
89 .return_type = "int ",
90 .return_value = "NB_OK",
91 .arguments = "struct nb_cb_rpc_args *args",
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
108 static void generate_callback_name(const struct lysc_node *snode,
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();
116 for (; snode; snode = snode->parent) {
117 /* Skip schema-only snodes. */
118 if (CHECK_FLAG(snode->nodetype, LYS_USES | LYS_CHOICE | LYS_CASE
119 | LYS_INPUT
120 | LYS_OUTPUT))
121 continue;
122
123 listnode_add_head(snodes, (void *)snode);
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
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
143 static int generate_prototypes(const struct lysc_node *snode, void *arg)
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
165 generate_callback_name(snode, cb->operation, cb_name,
166 sizeof(cb_name));
167 generate_prototype(cb, cb_name);
168 }
169
170 return YANG_ITER_CONTINUE;
171 }
172
173 static void generate_callback(const struct nb_callback_info *ncinfo,
174 const char *cb_name)
175 {
176 printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "",
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:
184 printf("\tswitch (args->event) {\n"
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
203 static int generate_callbacks(const struct lysc_node *snode, void *arg)
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:
216 return YANG_ITER_CONTINUE;
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
240 generate_callback_name(snode, cb->operation, cb_name,
241 sizeof(cb_name));
242 generate_callback(cb, cb_name);
243 }
244
245 return YANG_ITER_CONTINUE;
246 }
247
248 static int generate_nb_nodes(const struct lysc_node *snode, void *arg)
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:
261 return YANG_ITER_CONTINUE;
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);
281 printf("\t\t\t.cbs = {\n");
282 first = false;
283 }
284
285 generate_callback_name(snode, cb->operation, cb_name,
286 sizeof(cb_name));
287 printf("\t\t\t\t.%s = %s,\n", nb_operation_name(cb->operation),
288 cb_name);
289 }
290
291 if (!first) {
292 printf("\t\t\t}\n");
293 printf("\t\t},\n");
294 }
295
296 return YANG_ITER_CONTINUE;
297 }
298
299 int main(int argc, char *argv[])
300 {
301 const char *search_path = NULL;
302 struct yang_module *module;
303 char module_name_underscores[64];
304 struct stat st;
305 int opt;
306
307 while ((opt = getopt(argc, argv, "hp:s")) != -1) {
308 switch (opt) {
309 case 'h':
310 usage(EXIT_SUCCESS);
311 /* NOTREACHED */
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;
327 case 's':
328 static_cbs = true;
329 break;
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
340 yang_init(false, true);
341
342 if (search_path)
343 ly_ctx_set_searchdir(ly_native_ctx, search_path);
344
345 /* Load all FRR native models to ensure all augmentations are loaded. */
346 yang_module_load_all();
347
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
353 yang_init_loading_complete();
354
355 /* Create a nb_node for all YANG schema nodes. */
356 nb_nodes_create();
357
358 /* Generate callback prototypes. */
359 if (!static_cbs) {
360 printf("/* prototypes */\n");
361 yang_snodes_iterate(module->info, generate_prototypes, 0, NULL);
362 printf("\n");
363 }
364
365 /* Generate callback functions. */
366 yang_snodes_iterate(module->info, generate_callbacks, 0, NULL);
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);
378 yang_snodes_iterate(module->info, generate_nb_nodes, 0, NULL);
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. */
386 nb_nodes_delete();
387 yang_terminate();
388
389 return 0;
390 }