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);