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