2 * luaplugin - fast lua plugin indexing
3 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <sys/types.h>
17 #include <sys/cdefs.h>
19 #ifndef _POSIX_C_SOURCE
20 #define _POSIX_C_SOURCE /* XXX: portability hack for timestamp */
35 #include <lib/luaplugin.h>
39 #define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
41 #define DPRINTF(...) do {} while (0)
45 * list_for_each_offset - iterate over a list, start with the provided pointer
46 * @pos: the &struct list_head to use as a loop cursor.
47 * @head: the head for your list.
49 #define list_for_each_offset(pos, head, offset) \
50 for (pos = (offset)->next; pos != (offset); \
51 pos = ((pos->next == (head)) && ((offset) != (head)) ? (head)->next : pos->next))
53 static char pbuf
[PATH_MAX
];
54 static void load_module(struct luaplugin_ctx
*ctx
, struct luaplugin_entry
*e
);
56 static struct luaplugin_entry
*
57 find_entry(struct luaplugin_ctx
*ctx
, const char *name
, bool modname
)
62 ctx
->last
= &ctx
->entries
;
64 list_for_each_offset(p
, &ctx
->entries
, ctx
->last
) {
65 struct luaplugin_entry
*e
;
68 e
= container_of(p
, struct luaplugin_entry
, list
);
74 if (!strcmp(cmp
, name
))
80 static struct luaplugin_entry
*
81 new_entry(struct luaplugin_ctx
*ctx
, const char *name
, const char *modname
)
83 struct luaplugin_entry
*e
;
86 e
= malloc(sizeof(struct luaplugin_entry
));
90 memset(e
, 0, sizeof(struct luaplugin_entry
));
91 INIT_LIST_HEAD(&e
->list
);
95 e
->name
= strdup(name
);
99 e
->module
= strdup(modname
);
103 /* strip filename extension */
104 c
= strrchr(e
->module
, '.');
108 /* lua namespace: replace / with . */
110 while ((c
= strchr(c
, '/')) != NULL
) {
123 static const char *module_loader
=
124 "loader = function (newgt, filename)\n"
125 " setmetatable(newgt, { __index = _G })\n"
126 " local f = loadfile(filename)\n"
127 " if (type(f) == \"function\") then\n"
128 " setfenv(f, newgt)\n"
136 access_plugin_table (lua_State
*L
, const char *modname
, bool set
)
140 lua_pushvalue(L
, LUA_GLOBALSINDEX
);
144 e
= strchr(modname
, '.');
146 e
= modname
+ strlen(modname
);
150 lua_pushlstring(L
, modname
, e
- modname
);
152 if (lua_isnil(L
, -1) ||
153 /* no such field or last field */
154 (lua_istable(L
, -1) && (*e
!= '.'))) {
155 lua_pop(L
, 1); /* remove this result */
159 lua_pushvalue(L
, -2); /* use table from given index */
161 lua_createtable(L
, 0, 1); /* new table for field */
164 lua_pushlstring(L
, modname
, e
- modname
);
167 lua_pushvalue(L
, -2);
168 lua_settable(L
, -4); /* set new table into field */
173 else if (!lua_istable(L
, -1)) { /* field has a non-table value? */
174 lua_pop(L
, 2 + !!set
); /* remove table and values */
177 lua_remove(L
, -2); /* remove previous table */
186 load_module(struct luaplugin_ctx
*ctx
, struct luaplugin_entry
*e
)
188 lua_State
*L
= ctx
->L
;
191 /* grab the loader wrapper function */
192 ret
= luaL_dostring(L
, module_loader
);
196 lua_getglobal(L
, "loader");
198 lua_setglobal(L
, "loader");
203 /* new environment table for function call */
206 /* register the table globally */
207 lua_pushvalue(L
, -1);
208 access_plugin_table(L
, e
->module
, true);
210 lua_pushstring(L
, e
->name
);
212 if (lua_pcall(L
, 2, 0, 0) != 0) {
213 const char *err
= "unknown error";
215 if (lua_isstring(L
, -1))
216 err
= lua_tostring(L
, -1);
218 fprintf(stderr
, err
);
223 free_entry(struct luaplugin_ctx
*ctx
, struct luaplugin_entry
*e
)
225 lua_State
*L
= ctx
->L
;
227 if (e
->loaded
&& L
) {
228 /* allow the gc to free the module */
230 access_plugin_table(L
, e
->module
, true);
239 __luaplugin_scan(struct luaplugin_ctx
*ctx
, int base_len
, int rec
)
241 int gl_flags
= GLOB_NOESCAPE
| GLOB_NOSORT
| GLOB_MARK
;
245 strncpy(pbuf
+ base_len
, "*.lua", PATH_MAX
- base_len
);
246 if (glob(pbuf
, gl_flags
, NULL
, &gl
) < 0) {
251 for (i
= 0; i
< gl
.gl_pathc
; i
++) {
252 const char *entry
= gl
.gl_pathv
[i
];
253 struct luaplugin_entry
*e
;
257 elen
= strlen(entry
);
259 /* should not happen */
260 if ((elen
<= base_len
) || (strncmp(entry
, pbuf
, base_len
) != 0)) {
261 fprintf(stderr
, "[%s] sanity check failed in %s(%d)!\n", __FILE__
, __func__
, __LINE__
);
265 /* descend into subdirectories */
266 if (entry
[elen
- 1] == '/') {
267 strncpy(pbuf
+ base_len
, entry
+ base_len
, PATH_MAX
- base_len
);
268 __luaplugin_scan(ctx
, base_len
, rec
+ 1);
269 pbuf
[base_len
] = '\0';
273 if (stat(gl
.gl_pathv
[i
], &st
))
276 if ((st
.st_mode
& S_IFMT
) != S_IFREG
)
279 e
= find_entry(ctx
, entry
+ base_len
, false);
281 e
= new_entry(ctx
, entry
, entry
+ base_len
);
282 list_add_tail(&e
->list
, &ctx
->entries
);
287 e
->checked
= ctx
->checked
;
288 e
->reload
= (e
->timestamp
< st
.st_mtime
);
289 e
->timestamp
= st
.st_mtime
;
295 luaplugin_call(struct luaplugin_entry
*e
, int narg
)
297 struct luaplugin_ctx
*ctx
= e
->ctx
;
298 lua_State
*L
= ctx
->L
;
302 func
= luaL_checkstring(L
, -1 - narg
);
304 /* grab a reference to the plugin's table */
305 access_plugin_table(L
, e
->module
, false);
306 lua_getfield(L
, -1, func
);
307 if (!lua_isfunction(L
, -1)) {
308 lua_pop(L
, narg
+ 1);
313 /* replace function name with a ref to the function */
314 lua_replace(L
, -3 - narg
);
318 ret
= lua_pcall(L
, narg
, 0, 0);
321 fprintf(stderr
, lua_tostring(L
, -1));
329 luaplugin_scan(struct luaplugin_ctx
*ctx
)
331 struct list_head
*tmp
, *p
;
333 sprintf(pbuf
, "%s/", ctx
->path
);
336 __luaplugin_scan(ctx
, strlen(pbuf
), 0);
338 /* expire old entries */
339 list_for_each_safe(p
, tmp
, &ctx
->entries
) {
340 struct luaplugin_entry
*e
= container_of(p
, struct luaplugin_entry
, list
);
341 if (e
->checked
< ctx
->checked
)
349 luaplugin_init(struct luaplugin_ctx
*ctx
, const char *path
)
351 memset(ctx
, 0, sizeof(struct luaplugin_ctx
));
352 INIT_LIST_HEAD(&ctx
->entries
);
355 ctx
->L
= luaL_newstate();
359 luaL_openlibs(ctx
->L
);
361 /* disable the module functionality, a plugin is restricted to its own environment */
363 lua_pushcfunction(ctx->L, luaplugin_module);
364 lua_setfield(ctx->L, LUA_GLOBALSINDEX, "module");
371 luaplugin_done(struct luaplugin_ctx
*ctx
)
373 struct list_head
*p
, *tmp
;
378 list_for_each_safe(p
, tmp
, &ctx
->entries
) {
379 struct luaplugin_entry
*e
;
380 e
= container_of(p
, struct luaplugin_entry
, list
);