ali@352: /* ali@352: * Copyright (C) 2009 J. Ali Harlow ali@352: * ali@352: * This program is free software; you can redistribute it and/or modify ali@352: * it under the terms of the GNU General Public License as published by ali@352: * the Free Software Foundation; either version 2 of the License, or ali@352: * (at your option) any later version. ali@352: * ali@352: * This program is distributed in the hope that it will be useful, ali@352: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@352: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@352: * GNU General Public License for more details. ali@352: * ali@352: * You should have received a copy of the GNU General Public License along ali@352: * with this program; if not, write to the Free Software Foundation, Inc., ali@352: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@352: */ ali@352: ali@352: #include "config.h" ali@352: ali@352: #include ali@352: #include ali@352: #include ali@352: #include ali@352: #include ali@352: #include ali@352: #include ali@352: #include ali@352: #include ali@352: ali@352: #include "razor.h" ali@352: #include "razor-internal.h" ali@352: ali@352: #define MAX_ARGS 2 ali@352: ali@352: #define ARG_FLAG_OPT 0x1 ali@352: #define ARG_FLAG_BYPASS 0x2 ali@352: ali@352: #define ARG(flags, swtch, index, type) \ ali@352: ((flags) << 26 | (swtch) << 21 | \ ali@352: (index) << 16 | type) ali@352: #define ARG_FLAGS(arg) ((unsigned)(arg) >> 26) ali@352: #define ARG_SWTCH(arg) ((unsigned)(arg) >> 21 & 0x1F) ali@352: #define ARG_INDEX(arg) ((unsigned)(arg) >> 16 & 0x1F) ali@352: #define ARG_TYPE(arg) ((unsigned)(arg) & 0xFFFF) ali@352: ali@352: #define ARG_NONE 0 ali@352: #define ARG_STRING(n) ARG(0, 0, n, LUA_TSTRING) ali@352: #define ARG_OPT_STRING(n) ARG(ARG_FLAG_OPT, 0, n, LUA_TSTRING) ali@352: #define ARG_BYP_STRING(n, s) ARG(ARG_FLAG_BYPASS, s, n, LUA_TSTRING) ali@352: ali@352: struct razor_proxy { ali@352: char *table, *name; ali@352: int result; ali@352: unsigned args[MAX_ARGS]; ali@352: }; ali@352: ali@352: static struct razor_proxy proxy[] = { ali@352: { NULL, "dofile", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { NULL, "loadfile", LUA_TNONE, { ARG_OPT_STRING(1) } }, ali@352: { "io", "input", LUA_TNONE, { ARG_OPT_STRING(1) } }, ali@352: { "io", "output", LUA_TNONE, { ARG_OPT_STRING(1) } }, ali@352: { "io", "lines", LUA_TNONE, { ARG_OPT_STRING(1) } }, ali@352: { "io", "open", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "io", "popen", LUA_TNONE, { ARG_STRING(1) } }, ali@352: /* ali@352: * Note: We do not proxy os.execute ali@352: * We can't do it properly (there is no way to distinguish filenames ali@352: * in arguments from any other strings) so it's better not to try. ali@352: * Scripts that attempt to be portable should use posix.exec[p] ali@352: */ ali@352: { "os", "remove", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "os", "rename", LUA_TNONE, { ARG_STRING(1), ARG_STRING(2) } }, ali@352: /* ali@352: * Note: We do not proxy os.tmpname ali@352: * It should never be used. scripts should use io.tmpfile instead ali@352: * if the rather limited functionality it provides suffices. The ali@352: * proper solution is to implement posix.mkstemp ali@352: */ ali@352: { "posix", "access", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "chdir", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "chmod", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "chown", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "dir", LUA_TNONE, { ARG_OPT_STRING(1) } }, ali@352: { "posix", "exec", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "execp", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "files", LUA_TNONE, { ARG_OPT_STRING(1) } }, ali@352: { "posix", "getcwd", LUA_TSTRING, { ARG_NONE } }, ali@352: { "posix", "glob", LUA_TTABLE, { ARG_STRING(1) } }, ali@352: { "posix", "link", LUA_TNONE, { ARG_BYP_STRING(1, 3), ARG_STRING(2) } }, ali@352: { "posix", "mkdir", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "mkfifo", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "pathconf", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "readlink", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "rmdir", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "stat", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "unlink", LUA_TNONE, { ARG_STRING(1) } }, ali@352: { "posix", "utime", LUA_TNONE, { ARG_STRING(1) } }, ali@352: }; ali@352: ali@352: static lua_CFunction execp_handler; ali@352: ali@352: struct razor_lua { ali@352: const char *root; ali@352: lua_CFunction handler[ARRAY_SIZE(proxy)]; ali@352: }; ali@352: ali@352: static void *alloc_lua(void *user_data, void *ptr, size_t osize, size_t nsize) ali@352: { ali@352: if (!nsize) { ali@352: free(ptr); ali@352: return NULL; ali@352: } else ali@352: return realloc(ptr, nsize); ali@352: } ali@352: ali@352: static int proxy_execp(lua_State *L) ali@352: { ali@352: int r, n, err, got_eacces = 0; ali@352: char *file = strdup(luaL_checkstring(L, 1)); ali@352: char *exe; ali@352: const char *s, *e, *path; ali@352: struct razor_lua *rl; ali@352: ali@352: if (*file == '\0' || strchr(file, '/')) { ali@352: r = execp_handler(L); ali@352: free(file); ali@352: return r; ali@352: } ali@352: ali@352: (void)lua_getallocf(L, (void **)&rl); ali@352: ali@352: path = getenv("PATH"); ali@352: if (!path) ali@352: path = ":/bin:/usr/bin"; ali@352: ali@352: s = path; ali@352: for (;;) { ali@352: e = strchr(s, ':'); ali@352: if (!e) ali@352: e = s + strlen(s); ali@352: if (*s == '/') { ali@352: n = strlen(rl->root); ali@352: exe = malloc(n + (e - s) + 1 + strlen(file) + 1); ali@352: memcpy(exe, rl->root, n); ali@352: memcpy(exe + n, s, e - s); ali@352: n += e - s; ali@352: exe[n++] = '/'; ali@352: strcpy(exe + n, file); ali@352: } else if (e != s) { ali@352: exe = malloc((e - s) + 1 + strlen(file) + 1); ali@352: memcpy(exe, s, e - s); ali@352: n = e - s; ali@352: exe[n++] = '/'; ali@352: strcpy(exe + n, file); ali@352: } else { ali@352: /* Ensure exe contains a slash to avoid PATH search */ ali@352: exe = malloc(2 + strlen(file) + 1); ali@352: exe[0] = '.'; ali@352: exe[1] = '/'; ali@352: strcpy(exe + 2, file); ali@352: } ali@352: lua_pushstring(L, exe); ali@352: lua_replace(L, 1); ali@352: free(exe); ali@352: n = lua_gettop(L); ali@352: r = execp_handler(L); ali@352: ali@352: err = lua_tointeger(L, -1); ali@352: switch (err) { ali@352: case EACCES: ali@352: got_eacces = 1; ali@352: /* Fall through */ ali@352: case ENOENT: ali@352: case ESTALE: ali@352: case ENOTDIR: ali@352: case ENODEV: ali@352: case ETIMEDOUT: ali@352: lua_pop(L, lua_gettop(L) - n); ali@352: break; ali@352: default: ali@352: free(file); ali@352: return r; ali@352: } ali@352: ali@352: if (*e) ali@352: s = e + 1; ali@352: else ali@352: break; ali@352: } ali@352: ali@352: if (got_eacces) ali@352: err = EACCES; ali@352: ali@352: lua_pushnil(L); ali@352: lua_pushfstring(L, "%s: %s", file, strerror(err)); ali@352: lua_pushinteger(L, err); ali@352: ali@352: free(file); ali@352: return 3; ali@352: } ali@352: ali@352: static int real2fake(lua_State *L, struct razor_lua *rl, int indx) ali@352: { ali@352: const char *path; ali@352: ali@352: if (indx < 0) ali@352: indx += lua_gettop(L) + 1; ali@352: ali@352: if (lua_type(L, indx) == LUA_TSTRING) { ali@352: path = lua_tostring(L, indx); ali@352: if (path && !strncmp(path, rl->root, strlen(rl->root)) && ali@352: (path[strlen(rl->root)] == '/' || !path[strlen(rl->root)])) { ali@352: lua_pushstring(L, path + strlen(rl->root)); ali@352: lua_replace(L, indx); ali@352: return 1; ali@352: } ali@352: } ali@352: ali@352: return 0; ali@352: } ali@352: ali@352: static int proxy_handler(lua_State *L) ali@352: { ali@352: int i, retval, cond; ali@352: unsigned arg; ali@352: const char *path; ali@352: struct razor_lua *rl; ali@352: int method = lua_tointeger(L, lua_upvalueindex(1)); ali@352: struct razor_proxy *rp = proxy + method; ali@352: ali@352: (void)lua_getallocf(L, (void **)&rl); ali@352: ali@352: for(i = 0; i < MAX_ARGS; i++) { ali@352: arg = rp->args[i]; ali@352: if (arg == ARG_NONE) ali@352: continue; ali@352: switch (ARG_TYPE(arg)) { ali@352: case LUA_TSTRING: ali@352: if (ARG_FLAGS(arg) & ARG_FLAG_BYPASS) ali@352: cond = !lua_toboolean(L, ARG_SWTCH(arg)); ali@352: else ali@352: cond = 1; ali@352: if (ARG_FLAGS(arg) & ARG_FLAG_OPT) { ali@352: if (lua_isstring(L, ARG_INDEX(arg))) ali@352: path = lua_tostring(L, ARG_INDEX(arg)); ali@352: else ali@352: path = NULL; ali@352: } else ali@352: path = luaL_checkstring(L, ARG_INDEX(arg)); ali@352: if (cond && path && *path == '/') { ali@352: lua_pushfstring(L, "%s%s", rl->root, path); ali@352: lua_replace(L, ARG_INDEX(arg)); ali@352: } ali@352: break; ali@352: default: ali@352: lua_pushfstring(L, "razor proxy: Unhandled type (%d)", ali@352: ARG_TYPE(arg)); ali@352: lua_error(L); ali@352: } ali@352: } ali@352: ali@352: retval = rl->handler[method](L); ali@352: ali@352: if (rp->result != LUA_TNONE && !lua_isnil(L, 1)) { ali@352: switch (rp->result) { ali@352: case LUA_TNONE: ali@352: break; ali@352: case LUA_TSTRING: ali@352: real2fake(L, rl, -1); ali@352: break; ali@352: case LUA_TTABLE: ali@352: lua_pushnil(L); ali@352: while (lua_next(L, -2)) { ali@352: if (real2fake(L, rl, -1)) { ali@352: i = lua_tonumber(L, -2); ali@352: lua_rawseti(L, -3, i); ali@352: } else ali@352: lua_pop(L, 1); ali@352: } ali@352: break; ali@352: default: ali@352: lua_pushfstring(L, "razor proxy: Unhandled type (%d)", ali@352: rp->result); ali@352: lua_error(L); ali@352: } ali@352: } ali@352: ali@352: return retval; ali@352: } ali@352: ali@361: struct razor_lua_loader { ali@361: uint32_t name; ali@361: lua_CFunction func; ali@361: }; ali@361: ali@361: static struct razor_preload { ali@361: int init; ali@361: struct hashtable modules; ali@361: struct array name_pool; ali@361: struct array loaders; ali@361: } razor_preload = {0}; ali@361: ali@361: RAZOR_EXPORT void razor_set_lua_loader(const char *modname, void (*loader)()) ali@361: { ali@361: uint32_t name; ali@361: struct razor_lua_loader *ploader, *end; ali@361: ali@361: if (!razor_preload.init) { ali@361: razor_preload.init = 1; ali@361: array_init(&razor_preload.name_pool); ali@361: array_init(&razor_preload.loaders); ali@361: hashtable_init(&razor_preload.modules, ali@361: &razor_preload.name_pool); ali@361: } ali@361: ali@361: name = hashtable_tokenize(&razor_preload.modules, modname); ali@361: ali@361: end = razor_preload.loaders.data + razor_preload.loaders.size; ali@361: for(ploader = razor_preload.loaders.data; ploader < end; ploader++) ali@361: if (ploader->name == name) { ali@361: ploader->func = loader; ali@361: return; ali@361: } ali@361: ali@361: ploader = array_add(&razor_preload.loaders, sizeof(*ploader)); ali@361: ploader->name = name; ali@361: ploader->func = loader; ali@361: } ali@361: ali@368: RAZOR_EXPORT void (*razor_get_lua_loader(const char *modname))() ali@368: { ali@368: uint32_t name; ali@368: struct razor_lua_loader *ploader, *end; ali@368: ali@368: if (!razor_preload.init) ali@368: return 0; ali@368: ali@368: name = hashtable_lookup(&razor_preload.modules, modname); ali@368: ali@368: end = razor_preload.loaders.data + razor_preload.loaders.size; ali@368: for(ploader = razor_preload.loaders.data; ploader < end; ploader++) ali@368: if (ploader->name == name) ali@368: return ploader->func; ali@368: ali@368: return 0; ali@368: } ali@368: ali@361: static void razor_lua_preload(lua_State *L) ali@361: { ali@361: struct razor_lua_loader *ploader, *end; ali@361: ali@361: if (!razor_preload.init) ali@361: return; ali@361: ali@361: lua_getfield(L, LUA_GLOBALSINDEX, "package"); ali@361: lua_getfield(L, -1, "preload"); ali@361: lua_remove(L, -2); ali@361: ali@361: end = razor_preload.loaders.data + razor_preload.loaders.size; ali@361: for(ploader = razor_preload.loaders.data; ploader < end; ploader++) { ali@361: lua_pushcfunction(L, ploader->func); ali@361: lua_setfield(L, -2, ali@361: razor_preload.name_pool.data + ploader->name); ali@361: } ali@361: ali@361: lua_pop(L, 1); ali@361: } ali@361: ali@352: int run_lua_script(const char *root, const char *name, const char *body, ali@376: ssize_t len, int arg1) ali@352: { ali@352: int i, n; ali@352: lua_State *L; ali@352: struct razor_lua rl; ali@352: ali@352: if (root && strcmp(root, "/")) ali@352: rl.root = root; ali@352: else ali@352: rl.root = NULL; ali@352: ali@352: L = lua_newstate(alloc_lua, &rl); ali@352: luaL_openlibs(L); ali@361: razor_lua_preload(L); ali@352: lua_getglobal(L, "require"); ali@352: lua_pushstring(L, "posix"); ali@352: if (lua_pcall(L, 1, 1, 0)) { ali@352: fprintf(stderr, "lua posix: %s\n", lua_tostring(L, -1)); ali@352: lua_pop(L, 1); ali@352: lua_close(L); ali@352: return -1; ali@352: } ali@352: ali@352: if (rl.root) ali@352: for(i = 0; i < ARRAY_SIZE(proxy); i++) { ali@352: if (proxy[i].table) { ali@352: lua_getglobal(L, proxy[i].table); ali@352: n = lua_gettop(L); ali@352: } else ali@352: n = LUA_GLOBALSINDEX; ali@352: lua_getfield(L, n, proxy[i].name); ali@352: rl.handler[i] = lua_tocfunction(L, -1); ali@352: lua_getfenv(L, -1); ali@352: lua_remove(L, -2); ali@352: if (proxy[i].table && ali@352: !strcmp(proxy[i].table, "posix") && ali@352: !strcmp(proxy[i].name, "execp")) { ali@352: execp_handler = rl.handler[i]; ali@352: rl.handler[i] = proxy_execp; ali@352: } ali@352: lua_pushinteger(L, i); ali@352: lua_pushcclosure(L, proxy_handler, 1); ali@352: lua_pushvalue(L, -2); ali@352: lua_setfenv(L, -2); ali@352: lua_setfield(L, n, proxy[i].name); ali@352: if (proxy[i].table) ali@352: lua_pop(L, 2); ali@352: else ali@352: lua_pop(L, 1); ali@352: } ali@352: ali@352: if (len < 0) ali@352: len = strlen(body); ali@352: ali@352: if (luaL_loadbuffer(L, body, len, name)) { ali@352: fprintf(stderr, "failed to load lua script\n"); ali@352: lua_close(L); ali@352: return -1; ali@352: } ali@352: ali@376: lua_newtable(L); ali@376: lua_pushvalue(L, LUA_GLOBALSINDEX); ali@376: lua_pushliteral(L, "arg"); ali@376: lua_pushvalue(L, -3); ali@376: lua_rawset(L, -3); ali@376: lua_pop(L, 1); ali@376: lua_pushliteral(L, ""); ali@376: lua_rawseti(L, -2, 1); ali@376: if (arg1 >= 0) { ali@376: lua_pushinteger(L, arg1); ali@376: lua_rawseti(L, -2, 2); ali@376: } ali@376: lua_pop(L, 1); ali@376: ali@352: if (lua_pcall(L, 0, 0, 0)) { ali@352: fprintf(stderr, "lua script failed: %s\n", lua_tostring(L, -1)); ali@352: lua_pop(L, 1); ali@352: lua_close(L); ali@352: return -1; ali@352: } ali@352: ali@352: lua_close(L); ali@352: return 0; ali@352: }