Add support for preloading lua modules. This is useful both when
providing lua bindings to applications based on librazor and when
producing static binaries using librazor (where otherwise the lua
POSIX library would need to be included as an additional dynamic
object).
2 * Copyright (C) 2009 J. Ali Harlow <ali@juiblex.co.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <sys/types.h>
32 #include "razor-internal.h"
36 #define ARG_FLAG_OPT 0x1
37 #define ARG_FLAG_BYPASS 0x2
39 #define ARG(flags, swtch, index, type) \
40 ((flags) << 26 | (swtch) << 21 | \
42 #define ARG_FLAGS(arg) ((unsigned)(arg) >> 26)
43 #define ARG_SWTCH(arg) ((unsigned)(arg) >> 21 & 0x1F)
44 #define ARG_INDEX(arg) ((unsigned)(arg) >> 16 & 0x1F)
45 #define ARG_TYPE(arg) ((unsigned)(arg) & 0xFFFF)
48 #define ARG_STRING(n) ARG(0, 0, n, LUA_TSTRING)
49 #define ARG_OPT_STRING(n) ARG(ARG_FLAG_OPT, 0, n, LUA_TSTRING)
50 #define ARG_BYP_STRING(n, s) ARG(ARG_FLAG_BYPASS, s, n, LUA_TSTRING)
55 unsigned args[MAX_ARGS];
58 static struct razor_proxy proxy[] = {
59 { NULL, "dofile", LUA_TNONE, { ARG_STRING(1) } },
60 { NULL, "loadfile", LUA_TNONE, { ARG_OPT_STRING(1) } },
61 { "io", "input", LUA_TNONE, { ARG_OPT_STRING(1) } },
62 { "io", "output", LUA_TNONE, { ARG_OPT_STRING(1) } },
63 { "io", "lines", LUA_TNONE, { ARG_OPT_STRING(1) } },
64 { "io", "open", LUA_TNONE, { ARG_STRING(1) } },
65 { "io", "popen", LUA_TNONE, { ARG_STRING(1) } },
67 * Note: We do not proxy os.execute
68 * We can't do it properly (there is no way to distinguish filenames
69 * in arguments from any other strings) so it's better not to try.
70 * Scripts that attempt to be portable should use posix.exec[p]
72 { "os", "remove", LUA_TNONE, { ARG_STRING(1) } },
73 { "os", "rename", LUA_TNONE, { ARG_STRING(1), ARG_STRING(2) } },
75 * Note: We do not proxy os.tmpname
76 * It should never be used. scripts should use io.tmpfile instead
77 * if the rather limited functionality it provides suffices. The
78 * proper solution is to implement posix.mkstemp
80 { "posix", "access", LUA_TNONE, { ARG_STRING(1) } },
81 { "posix", "chdir", LUA_TNONE, { ARG_STRING(1) } },
82 { "posix", "chmod", LUA_TNONE, { ARG_STRING(1) } },
83 { "posix", "chown", LUA_TNONE, { ARG_STRING(1) } },
84 { "posix", "dir", LUA_TNONE, { ARG_OPT_STRING(1) } },
85 { "posix", "exec", LUA_TNONE, { ARG_STRING(1) } },
86 { "posix", "execp", LUA_TNONE, { ARG_STRING(1) } },
87 { "posix", "files", LUA_TNONE, { ARG_OPT_STRING(1) } },
88 { "posix", "getcwd", LUA_TSTRING, { ARG_NONE } },
89 { "posix", "glob", LUA_TTABLE, { ARG_STRING(1) } },
90 { "posix", "link", LUA_TNONE, { ARG_BYP_STRING(1, 3), ARG_STRING(2) } },
91 { "posix", "mkdir", LUA_TNONE, { ARG_STRING(1) } },
92 { "posix", "mkfifo", LUA_TNONE, { ARG_STRING(1) } },
93 { "posix", "pathconf", LUA_TNONE, { ARG_STRING(1) } },
94 { "posix", "readlink", LUA_TNONE, { ARG_STRING(1) } },
95 { "posix", "rmdir", LUA_TNONE, { ARG_STRING(1) } },
96 { "posix", "stat", LUA_TNONE, { ARG_STRING(1) } },
97 { "posix", "unlink", LUA_TNONE, { ARG_STRING(1) } },
98 { "posix", "utime", LUA_TNONE, { ARG_STRING(1) } },
101 static lua_CFunction execp_handler;
105 lua_CFunction handler[ARRAY_SIZE(proxy)];
108 static void *alloc_lua(void *user_data, void *ptr, size_t osize, size_t nsize)
114 return realloc(ptr, nsize);
117 static int proxy_execp(lua_State *L)
119 int r, n, err, got_eacces = 0;
120 char *file = strdup(luaL_checkstring(L, 1));
122 const char *s, *e, *path;
123 struct razor_lua *rl;
125 if (*file == '\0' || strchr(file, '/')) {
126 r = execp_handler(L);
131 (void)lua_getallocf(L, (void **)&rl);
133 path = getenv("PATH");
135 path = ":/bin:/usr/bin";
143 n = strlen(rl->root);
144 exe = malloc(n + (e - s) + 1 + strlen(file) + 1);
145 memcpy(exe, rl->root, n);
146 memcpy(exe + n, s, e - s);
149 strcpy(exe + n, file);
151 exe = malloc((e - s) + 1 + strlen(file) + 1);
152 memcpy(exe, s, e - s);
155 strcpy(exe + n, file);
157 /* Ensure exe contains a slash to avoid PATH search */
158 exe = malloc(2 + strlen(file) + 1);
161 strcpy(exe + 2, file);
163 lua_pushstring(L, exe);
167 r = execp_handler(L);
169 err = lua_tointeger(L, -1);
179 lua_pop(L, lua_gettop(L) - n);
196 lua_pushfstring(L, "%s: %s", file, strerror(err));
197 lua_pushinteger(L, err);
203 static int real2fake(lua_State *L, struct razor_lua *rl, int indx)
208 indx += lua_gettop(L) + 1;
210 if (lua_type(L, indx) == LUA_TSTRING) {
211 path = lua_tostring(L, indx);
212 if (path && !strncmp(path, rl->root, strlen(rl->root)) &&
213 (path[strlen(rl->root)] == '/' || !path[strlen(rl->root)])) {
214 lua_pushstring(L, path + strlen(rl->root));
215 lua_replace(L, indx);
223 static int proxy_handler(lua_State *L)
228 struct razor_lua *rl;
229 int method = lua_tointeger(L, lua_upvalueindex(1));
230 struct razor_proxy *rp = proxy + method;
232 (void)lua_getallocf(L, (void **)&rl);
234 for(i = 0; i < MAX_ARGS; i++) {
238 switch (ARG_TYPE(arg)) {
240 if (ARG_FLAGS(arg) & ARG_FLAG_BYPASS)
241 cond = !lua_toboolean(L, ARG_SWTCH(arg));
244 if (ARG_FLAGS(arg) & ARG_FLAG_OPT) {
245 if (lua_isstring(L, ARG_INDEX(arg)))
246 path = lua_tostring(L, ARG_INDEX(arg));
250 path = luaL_checkstring(L, ARG_INDEX(arg));
251 if (cond && path && *path == '/') {
252 lua_pushfstring(L, "%s%s", rl->root, path);
253 lua_replace(L, ARG_INDEX(arg));
257 lua_pushfstring(L, "razor proxy: Unhandled type (%d)",
263 retval = rl->handler[method](L);
265 if (rp->result != LUA_TNONE && !lua_isnil(L, 1)) {
266 switch (rp->result) {
270 real2fake(L, rl, -1);
274 while (lua_next(L, -2)) {
275 if (real2fake(L, rl, -1)) {
276 i = lua_tonumber(L, -2);
277 lua_rawseti(L, -3, i);
283 lua_pushfstring(L, "razor proxy: Unhandled type (%d)",
292 struct razor_lua_loader {
297 static struct razor_preload {
299 struct hashtable modules;
300 struct array name_pool;
301 struct array loaders;
302 } razor_preload = {0};
304 RAZOR_EXPORT void razor_set_lua_loader(const char *modname, void (*loader)())
307 struct razor_lua_loader *ploader, *end;
309 if (!razor_preload.init) {
310 razor_preload.init = 1;
311 array_init(&razor_preload.name_pool);
312 array_init(&razor_preload.loaders);
313 hashtable_init(&razor_preload.modules,
314 &razor_preload.name_pool);
317 name = hashtable_tokenize(&razor_preload.modules, modname);
319 end = razor_preload.loaders.data + razor_preload.loaders.size;
320 for(ploader = razor_preload.loaders.data; ploader < end; ploader++)
321 if (ploader->name == name) {
322 ploader->func = loader;
326 ploader = array_add(&razor_preload.loaders, sizeof(*ploader));
327 ploader->name = name;
328 ploader->func = loader;
331 static void razor_lua_preload(lua_State *L)
333 struct razor_lua_loader *ploader, *end;
335 if (!razor_preload.init)
338 lua_getfield(L, LUA_GLOBALSINDEX, "package");
339 lua_getfield(L, -1, "preload");
342 end = razor_preload.loaders.data + razor_preload.loaders.size;
343 for(ploader = razor_preload.loaders.data; ploader < end; ploader++) {
344 lua_pushcfunction(L, ploader->func);
346 razor_preload.name_pool.data + ploader->name);
352 int run_lua_script(const char *root, const char *name, const char *body,
359 if (root && strcmp(root, "/"))
364 L = lua_newstate(alloc_lua, &rl);
366 razor_lua_preload(L);
367 lua_getglobal(L, "require");
368 lua_pushstring(L, "posix");
369 if (lua_pcall(L, 1, 1, 0)) {
370 fprintf(stderr, "lua posix: %s\n", lua_tostring(L, -1));
377 for(i = 0; i < ARRAY_SIZE(proxy); i++) {
378 if (proxy[i].table) {
379 lua_getglobal(L, proxy[i].table);
382 n = LUA_GLOBALSINDEX;
383 lua_getfield(L, n, proxy[i].name);
384 rl.handler[i] = lua_tocfunction(L, -1);
387 if (proxy[i].table &&
388 !strcmp(proxy[i].table, "posix") &&
389 !strcmp(proxy[i].name, "execp")) {
390 execp_handler = rl.handler[i];
391 rl.handler[i] = proxy_execp;
393 lua_pushinteger(L, i);
394 lua_pushcclosure(L, proxy_handler, 1);
395 lua_pushvalue(L, -2);
397 lua_setfield(L, n, proxy[i].name);
407 if (luaL_loadbuffer(L, body, len, name)) {
408 fprintf(stderr, "failed to load lua script\n");
413 if (lua_pcall(L, 0, 0, 0)) {
414 fprintf(stderr, "lua script failed: %s\n", lua_tostring(L, -1));