]> git.proxmox.com Git - mirror_frr.git/blob - lib/frrscript.c
Merge pull request #10917 from rampxxxx/fix_fxx_centos8_docker
[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
107 if (snhe->script_name[0] == '\0')
108 return NULL;
109
110 return snhe->script_name;
111 }
112
113 uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe)
114 {
115 return string_hash_make(snhe->function_name);
116 }
117
118 int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1,
119 const struct frrscript_names_entry *snhe2)
120 {
121 return strncmp(snhe1->function_name, snhe2->function_name,
122 sizeof(snhe1->function_name));
123 }
124
125 /* Codecs */
126
127 struct frrscript_codec frrscript_codecs_lib[] = {
128 {.typename = "integer",
129 .encoder = (encoder_func)lua_pushintegerp,
130 .decoder = lua_tointegerp},
131 {.typename = "string",
132 .encoder = (encoder_func)lua_pushstring_wrapper,
133 .decoder = lua_tostringp},
134 {.typename = "prefix",
135 .encoder = (encoder_func)lua_pushprefix,
136 .decoder = lua_toprefix},
137 {.typename = "interface",
138 .encoder = (encoder_func)lua_pushinterface,
139 .decoder = lua_tointerface},
140 {.typename = "in_addr",
141 .encoder = (encoder_func)lua_pushinaddr,
142 .decoder = lua_toinaddr},
143 {.typename = "in6_addr",
144 .encoder = (encoder_func)lua_pushin6addr,
145 .decoder = lua_toin6addr},
146 {.typename = "sockunion",
147 .encoder = (encoder_func)lua_pushsockunion,
148 .decoder = lua_tosockunion},
149 {.typename = "time_t",
150 .encoder = (encoder_func)lua_pushtimet,
151 .decoder = lua_totimet},
152 {}};
153
154 /* Type codecs */
155
156 struct hash *codec_hash;
157 char scriptdir[MAXPATHLEN];
158
159 static unsigned int codec_hash_key(const void *data)
160 {
161 const struct frrscript_codec *c = data;
162
163 return string_hash_make(c->typename);
164 }
165
166 static bool codec_hash_cmp(const void *d1, const void *d2)
167 {
168 const struct frrscript_codec *e1 = d1;
169 const struct frrscript_codec *e2 = d2;
170
171 return strmatch(e1->typename, e2->typename);
172 }
173
174 static void *codec_alloc(void *arg)
175 {
176 struct frrscript_codec *tmp = arg;
177
178 struct frrscript_codec *e =
179 XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
180 e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
181 e->encoder = tmp->encoder;
182 e->decoder = tmp->decoder;
183
184 return e;
185 }
186
187 #if 0
188 static void codec_free(struct codec *c)
189 {
190 XFREE(MTYPE_TMP, c->typename);
191 XFREE(MTYPE_TMP, c);
192 }
193 #endif
194
195 /* Lua function hash utils */
196
197 unsigned int lua_function_hash_key(const void *data)
198 {
199 const struct lua_function_state *lfs = data;
200
201 return string_hash_make(lfs->name);
202 }
203
204 bool lua_function_hash_cmp(const void *d1, const void *d2)
205 {
206 const struct lua_function_state *lfs1 = d1;
207 const struct lua_function_state *lfs2 = d2;
208
209 return strmatch(lfs1->name, lfs2->name);
210 }
211
212 void *lua_function_alloc(void *arg)
213 {
214 struct lua_function_state *tmp = arg;
215
216 struct lua_function_state *lfs =
217 XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state));
218 lfs->name = tmp->name;
219 lfs->L = tmp->L;
220 return lfs;
221 }
222
223 static void lua_function_free(struct hash_bucket *b, void *data)
224 {
225 struct lua_function_state *lfs = (struct lua_function_state *)b->data;
226 lua_close(lfs->L);
227 XFREE(MTYPE_SCRIPT, lfs);
228 }
229
230 /* internal frrscript APIs */
231
232 int _frrscript_call_lua(struct lua_function_state *lfs, int nargs)
233 {
234
235 int ret;
236 ret = lua_pcall(lfs->L, nargs, 1, 0);
237
238 switch (ret) {
239 case LUA_OK:
240 break;
241 case LUA_ERRRUN:
242 zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name,
243 lua_tostring(lfs->L, -1));
244 break;
245 case LUA_ERRMEM:
246 zlog_err("Lua hook call '%s' : memory error: %s", lfs->name,
247 lua_tostring(lfs->L, -1));
248 break;
249 case LUA_ERRERR:
250 zlog_err("Lua hook call '%s' : error handler error: %s",
251 lfs->name, lua_tostring(lfs->L, -1));
252 break;
253 case LUA_ERRGCMM:
254 zlog_err("Lua hook call '%s' : garbage collector error: %s",
255 lfs->name, lua_tostring(lfs->L, -1));
256 break;
257 default:
258 zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name,
259 lua_tostring(lfs->L, -1));
260 break;
261 }
262
263 if (ret != LUA_OK) {
264 lua_pop(lfs->L, 1);
265 goto done;
266 }
267
268 if (lua_gettop(lfs->L) != 1) {
269 zlog_err(
270 "Lua hook call '%s': Lua function should return only 1 result",
271 lfs->name);
272 ret = 1;
273 goto done;
274 }
275
276 if (lua_istable(lfs->L, 1) != 1) {
277 zlog_err(
278 "Lua hook call '%s': Lua function should return a Lua table",
279 lfs->name);
280 ret = 1;
281 }
282
283 done:
284 /* LUA_OK is 0, so we can just return lua_pcall's result directly */
285 return ret;
286 }
287
288 void *frrscript_get_result(struct frrscript *fs, const char *function_name,
289 const char *name,
290 void *(*lua_to)(lua_State *L, int idx))
291 {
292 void *p;
293 struct lua_function_state *lfs;
294 struct lua_function_state lookup = {.name = function_name};
295
296 lfs = hash_lookup(fs->lua_function_hash, &lookup);
297
298 if (lfs == NULL)
299 return NULL;
300
301 /* At this point, the Lua state should have only the returned table.
302 * We will then search the table for the key/value we're interested in.
303 * Then if the value is present (i.e. non-nil), call the lua_to*
304 * decoder.
305 */
306 assert(lua_gettop(lfs->L) == 1);
307 assert(lua_istable(lfs->L, -1) == 1);
308 lua_getfield(lfs->L, -1, name);
309 if (lua_isnil(lfs->L, -1)) {
310 lua_pop(lfs->L, 1);
311 zlog_warn(
312 "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed",
313 fs->name, function_name, name);
314 return NULL;
315 }
316 p = lua_to(lfs->L, 2);
317
318 /* At the end, the Lua state should be same as it was at the start
319 * i.e. containing solely the returned table.
320 */
321 assert(lua_gettop(lfs->L) == 1);
322 assert(lua_istable(lfs->L, -1) == 1);
323
324 return p;
325 }
326
327 void frrscript_register_type_codec(struct frrscript_codec *codec)
328 {
329 struct frrscript_codec c = *codec;
330
331 if (hash_lookup(codec_hash, &c)) {
332 zlog_backtrace(LOG_ERR);
333 assert(!"Type codec double-registered.");
334 }
335
336 assert(hash_get(codec_hash, &c, codec_alloc));
337 }
338
339 void frrscript_register_type_codecs(struct frrscript_codec *codecs)
340 {
341 for (int i = 0; codecs[i].typename != NULL; i++)
342 frrscript_register_type_codec(&codecs[i]);
343 }
344
345 struct frrscript *frrscript_new(const char *name)
346 {
347 struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
348
349 fs->name = XSTRDUP(MTYPE_SCRIPT, name);
350 fs->lua_function_hash =
351 hash_create(lua_function_hash_key, lua_function_hash_cmp,
352 "Lua function state hash");
353 return fs;
354 }
355
356 int frrscript_load(struct frrscript *fs, const char *function_name,
357 int (*load_cb)(struct frrscript *))
358 {
359
360 /* Set up the Lua script */
361 lua_State *L = luaL_newstate();
362
363 frrlua_export_logging(L);
364
365 char script_name[MAXPATHLEN];
366
367 if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir,
368 fs->name)
369 >= (int)sizeof(script_name)) {
370 zlog_err("frrscript: path to script %s/%s.lua is too long",
371 scriptdir, fs->name);
372 goto fail;
373 }
374
375 if (luaL_dofile(L, script_name) != 0) {
376 zlog_err("frrscript: failed loading script '%s': error: %s",
377 script_name, lua_tostring(L, -1));
378 goto fail;
379 }
380
381 /* To check the Lua function, we get it from the global table */
382 lua_getglobal(L, function_name);
383 if (lua_isfunction(L, lua_gettop(L)) == 0) {
384 zlog_err("frrscript: loaded script '%s' but %s not found",
385 script_name, function_name);
386 goto fail;
387 }
388 /* Then pop the function (frrscript_call will push it when it needs it)
389 */
390 lua_pop(L, 1);
391
392 if (load_cb && (*load_cb)(fs) != 0) {
393 zlog_err(
394 "frrscript: '%s': %s: loaded but callback returned non-zero exit code",
395 script_name, function_name);
396 goto fail;
397 }
398
399 /* Add the Lua function state to frrscript */
400 struct lua_function_state key = {.name = function_name, .L = L};
401
402 hash_get(fs->lua_function_hash, &key, lua_function_alloc);
403
404 return 0;
405 fail:
406 lua_close(L);
407 return 1;
408 }
409
410 void frrscript_delete(struct frrscript *fs)
411 {
412 hash_iterate(fs->lua_function_hash, lua_function_free, NULL);
413 XFREE(MTYPE_SCRIPT, fs->name);
414 XFREE(MTYPE_SCRIPT, fs);
415 }
416
417 void frrscript_init(const char *sd)
418 {
419 codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
420 "Lua type encoders");
421
422 strlcpy(scriptdir, sd, sizeof(scriptdir));
423
424 /* Register core library types */
425 frrscript_register_type_codecs(frrscript_codecs_lib);
426 }
427
428 #endif /* HAVE_SCRIPTING */