]> git.proxmox.com Git - mirror_frr.git/blob - lib/frrscript.c
Merge pull request #9902 from LabNConsulting/working/lb/lutil-wait-optimization
[mirror_frr.git] / lib / frrscript.c
1 /* Scripting foo
2 * Copyright (C) 2020 NVIDIA Corporation
3 * Quentin Young
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 #include <zebra.h>
20
21 #ifdef HAVE_SCRIPTING
22
23 #include <stdarg.h>
24 #include <lua.h>
25
26 #include "frrscript.h"
27 #include "frrlua.h"
28 #include "memory.h"
29 #include "hash.h"
30 #include "log.h"
31
32
33 DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
34
35 /*
36 * Script name hash utilities
37 */
38
39 struct frrscript_names_head frrscript_names_hash;
40
41 /*
42 * Wrapper for frrscript_names_add
43 * Use this to register hook calls when a daemon starts up
44 */
45 int frrscript_names_add_function_name(const char *function_name)
46 {
47 struct frrscript_names_entry *insert =
48 XCALLOC(MTYPE_SCRIPT, sizeof(*insert));
49 strlcpy(insert->function_name, function_name,
50 sizeof(insert->function_name));
51
52 if (frrscript_names_add(&frrscript_names_hash, insert)) {
53 zlog_warn(
54 "Failed to add hook call function name to script_names");
55 return 1;
56 }
57 return 0;
58 }
59
60 void frrscript_names_destroy(void)
61 {
62 struct frrscript_names_entry *ne;
63
64 while ((ne = frrscript_names_pop(&frrscript_names_hash)))
65 XFREE(MTYPE_SCRIPT, ne);
66 }
67
68 /*
69 * Given a function_name, set its script_name. function_names and script_names
70 * are one-to-one. Each set will wipe the previous script_name.
71 * Return 0 if set was successful, else 1.
72 *
73 * script_name is the base name of the file, without .lua.
74 */
75 int frrscript_names_set_script_name(const char *function_name,
76 const char *script_name)
77 {
78 struct frrscript_names_entry lookup;
79
80 strlcpy(lookup.function_name, function_name,
81 sizeof(lookup.function_name));
82 struct frrscript_names_entry *snhe =
83 frrscript_names_find(&frrscript_names_hash, &lookup);
84 if (!snhe)
85 return 1;
86 strlcpy(snhe->script_name, script_name, sizeof(snhe->script_name));
87 return 0;
88 }
89
90 /*
91 * Given a function_name, get its script_name.
92 * Return NULL if function_name not found.
93 *
94 * script_name is the base name of the file, without .lua.
95 */
96 char *frrscript_names_get_script_name(const char *function_name)
97 {
98 struct frrscript_names_entry lookup;
99
100 strlcpy(lookup.function_name, function_name,
101 sizeof(lookup.function_name));
102 struct frrscript_names_entry *snhe =
103 frrscript_names_find(&frrscript_names_hash, &lookup);
104 if (!snhe)
105 return NULL;
106 return snhe->script_name;
107 }
108
109 uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe)
110 {
111 return string_hash_make(snhe->function_name);
112 }
113
114 int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1,
115 const struct frrscript_names_entry *snhe2)
116 {
117 return strncmp(snhe1->function_name, snhe2->function_name,
118 sizeof(snhe1->function_name));
119 }
120
121 /* Codecs */
122
123 struct frrscript_codec frrscript_codecs_lib[] = {
124 {.typename = "integer",
125 .encoder = (encoder_func)lua_pushintegerp,
126 .decoder = lua_tointegerp},
127 {.typename = "string",
128 .encoder = (encoder_func)lua_pushstring_wrapper,
129 .decoder = lua_tostringp},
130 {.typename = "prefix",
131 .encoder = (encoder_func)lua_pushprefix,
132 .decoder = lua_toprefix},
133 {.typename = "interface",
134 .encoder = (encoder_func)lua_pushinterface,
135 .decoder = lua_tointerface},
136 {.typename = "in_addr",
137 .encoder = (encoder_func)lua_pushinaddr,
138 .decoder = lua_toinaddr},
139 {.typename = "in6_addr",
140 .encoder = (encoder_func)lua_pushin6addr,
141 .decoder = lua_toin6addr},
142 {.typename = "sockunion",
143 .encoder = (encoder_func)lua_pushsockunion,
144 .decoder = lua_tosockunion},
145 {.typename = "time_t",
146 .encoder = (encoder_func)lua_pushtimet,
147 .decoder = lua_totimet},
148 {}};
149
150 /* Type codecs */
151
152 struct hash *codec_hash;
153 char scriptdir[MAXPATHLEN];
154
155 static unsigned int codec_hash_key(const void *data)
156 {
157 const struct frrscript_codec *c = data;
158
159 return string_hash_make(c->typename);
160 }
161
162 static bool codec_hash_cmp(const void *d1, const void *d2)
163 {
164 const struct frrscript_codec *e1 = d1;
165 const struct frrscript_codec *e2 = d2;
166
167 return strmatch(e1->typename, e2->typename);
168 }
169
170 static void *codec_alloc(void *arg)
171 {
172 struct frrscript_codec *tmp = arg;
173
174 struct frrscript_codec *e =
175 XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
176 e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
177 e->encoder = tmp->encoder;
178 e->decoder = tmp->decoder;
179
180 return e;
181 }
182
183 #if 0
184 static void codec_free(struct codec *c)
185 {
186 XFREE(MTYPE_TMP, c->typename);
187 XFREE(MTYPE_TMP, c);
188 }
189 #endif
190
191 /* Lua function hash utils */
192
193 unsigned int lua_function_hash_key(const void *data)
194 {
195 const struct lua_function_state *lfs = data;
196
197 return string_hash_make(lfs->name);
198 }
199
200 bool lua_function_hash_cmp(const void *d1, const void *d2)
201 {
202 const struct lua_function_state *lfs1 = d1;
203 const struct lua_function_state *lfs2 = d2;
204
205 return strmatch(lfs1->name, lfs2->name);
206 }
207
208 void *lua_function_alloc(void *arg)
209 {
210 struct lua_function_state *tmp = arg;
211
212 struct lua_function_state *lfs =
213 XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state));
214 lfs->name = tmp->name;
215 lfs->L = tmp->L;
216 return lfs;
217 }
218
219 static void lua_function_free(struct hash_bucket *b, void *data)
220 {
221 struct lua_function_state *lfs = (struct lua_function_state *)b->data;
222 lua_close(lfs->L);
223 XFREE(MTYPE_SCRIPT, lfs);
224 }
225
226 /* internal frrscript APIs */
227
228 int _frrscript_call_lua(struct lua_function_state *lfs, int nargs)
229 {
230
231 int ret;
232 ret = lua_pcall(lfs->L, nargs, 1, 0);
233
234 switch (ret) {
235 case LUA_OK:
236 break;
237 case LUA_ERRRUN:
238 zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name,
239 lua_tostring(lfs->L, -1));
240 break;
241 case LUA_ERRMEM:
242 zlog_err("Lua hook call '%s' : memory error: %s", lfs->name,
243 lua_tostring(lfs->L, -1));
244 break;
245 case LUA_ERRERR:
246 zlog_err("Lua hook call '%s' : error handler error: %s",
247 lfs->name, lua_tostring(lfs->L, -1));
248 break;
249 case LUA_ERRGCMM:
250 zlog_err("Lua hook call '%s' : garbage collector error: %s",
251 lfs->name, lua_tostring(lfs->L, -1));
252 break;
253 default:
254 zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name,
255 lua_tostring(lfs->L, -1));
256 break;
257 }
258
259 if (ret != LUA_OK) {
260 lua_pop(lfs->L, 1);
261 goto done;
262 }
263
264 if (lua_gettop(lfs->L) != 1) {
265 zlog_err(
266 "Lua hook call '%s': Lua function should return only 1 result",
267 lfs->name);
268 ret = 1;
269 goto done;
270 }
271
272 if (lua_istable(lfs->L, 1) != 1) {
273 zlog_err(
274 "Lua hook call '%s': Lua function should return a Lua table",
275 lfs->name);
276 ret = 1;
277 }
278
279 done:
280 /* LUA_OK is 0, so we can just return lua_pcall's result directly */
281 return ret;
282 }
283
284 void *frrscript_get_result(struct frrscript *fs, const char *function_name,
285 const char *name,
286 void *(*lua_to)(lua_State *L, int idx))
287 {
288 void *p;
289 struct lua_function_state *lfs;
290 struct lua_function_state lookup = {.name = function_name};
291
292 lfs = hash_lookup(fs->lua_function_hash, &lookup);
293
294 if (lfs == NULL)
295 return NULL;
296
297 /* At this point, the Lua state should have only the returned table.
298 * We will then search the table for the key/value we're interested in.
299 * Then if the value is present (i.e. non-nil), call the lua_to*
300 * decoder.
301 */
302 assert(lua_gettop(lfs->L) == 1);
303 assert(lua_istable(lfs->L, -1) == 1);
304 lua_getfield(lfs->L, -1, name);
305 if (lua_isnil(lfs->L, -1)) {
306 lua_pop(lfs->L, 1);
307 zlog_warn(
308 "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed",
309 fs->name, function_name, name);
310 return NULL;
311 }
312 p = lua_to(lfs->L, 2);
313
314 /* At the end, the Lua state should be same as it was at the start
315 * i.e. containing solely the returned table.
316 */
317 assert(lua_gettop(lfs->L) == 1);
318 assert(lua_istable(lfs->L, -1) == 1);
319
320 return p;
321 }
322
323 void frrscript_register_type_codec(struct frrscript_codec *codec)
324 {
325 struct frrscript_codec c = *codec;
326
327 if (hash_lookup(codec_hash, &c)) {
328 zlog_backtrace(LOG_ERR);
329 assert(!"Type codec double-registered.");
330 }
331
332 assert(hash_get(codec_hash, &c, codec_alloc));
333 }
334
335 void frrscript_register_type_codecs(struct frrscript_codec *codecs)
336 {
337 for (int i = 0; codecs[i].typename != NULL; i++)
338 frrscript_register_type_codec(&codecs[i]);
339 }
340
341 struct frrscript *frrscript_new(const char *name)
342 {
343 struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
344
345 fs->name = XSTRDUP(MTYPE_SCRIPT, name);
346 fs->lua_function_hash =
347 hash_create(lua_function_hash_key, lua_function_hash_cmp,
348 "Lua function state hash");
349 return fs;
350 }
351
352 int frrscript_load(struct frrscript *fs, const char *function_name,
353 int (*load_cb)(struct frrscript *))
354 {
355
356 /* Set up the Lua script */
357 lua_State *L = luaL_newstate();
358
359 frrlua_export_logging(L);
360
361 char script_name[MAXPATHLEN];
362
363 if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir,
364 fs->name)
365 >= (int)sizeof(script_name)) {
366 zlog_err("frrscript: path to script %s/%s.lua is too long",
367 scriptdir, fs->name);
368 goto fail;
369 }
370
371 if (luaL_dofile(L, script_name) != 0) {
372 zlog_err("frrscript: failed loading script '%s.lua': error: %s",
373 script_name, lua_tostring(L, -1));
374 goto fail;
375 }
376
377 /* To check the Lua function, we get it from the global table */
378 lua_getglobal(L, function_name);
379 if (lua_isfunction(L, lua_gettop(L)) == 0) {
380 zlog_err("frrscript: loaded script '%s.lua' but %s not found",
381 script_name, function_name);
382 goto fail;
383 }
384 /* Then pop the function (frrscript_call will push it when it needs it)
385 */
386 lua_pop(L, 1);
387
388 if (load_cb && (*load_cb)(fs) != 0) {
389 zlog_err(
390 "frrscript: '%s.lua': %s: loaded but callback returned non-zero exit code",
391 script_name, function_name);
392 goto fail;
393 }
394
395 /* Add the Lua function state to frrscript */
396 struct lua_function_state key = {.name = function_name, .L = L};
397
398 hash_get(fs->lua_function_hash, &key, lua_function_alloc);
399
400 return 0;
401 fail:
402 lua_close(L);
403 return 1;
404 }
405
406 void frrscript_delete(struct frrscript *fs)
407 {
408 hash_iterate(fs->lua_function_hash, lua_function_free, NULL);
409 XFREE(MTYPE_SCRIPT, fs->name);
410 XFREE(MTYPE_SCRIPT, fs);
411 }
412
413 void frrscript_init(const char *sd)
414 {
415 codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
416 "Lua type encoders");
417
418 strlcpy(scriptdir, sd, sizeof(scriptdir));
419
420 /* Register core library types */
421 frrscript_register_type_codecs(frrscript_codecs_lib);
422 }
423
424 #endif /* HAVE_SCRIPTING */