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 int run_lua_script(const char *root, const char *name, const char *body,
299 if (root && strcmp(root, "/"))
304 L = lua_newstate(alloc_lua, &rl);
306 lua_getglobal(L, "require");
307 lua_pushstring(L, "posix");
308 if (lua_pcall(L, 1, 1, 0)) {
309 fprintf(stderr, "lua posix: %s\n", lua_tostring(L, -1));
316 for(i = 0; i < ARRAY_SIZE(proxy); i++) {
317 if (proxy[i].table) {
318 lua_getglobal(L, proxy[i].table);
321 n = LUA_GLOBALSINDEX;
322 lua_getfield(L, n, proxy[i].name);
323 rl.handler[i] = lua_tocfunction(L, -1);
326 if (proxy[i].table &&
327 !strcmp(proxy[i].table, "posix") &&
328 !strcmp(proxy[i].name, "execp")) {
329 execp_handler = rl.handler[i];
330 rl.handler[i] = proxy_execp;
332 lua_pushinteger(L, i);
333 lua_pushcclosure(L, proxy_handler, 1);
334 lua_pushvalue(L, -2);
336 lua_setfield(L, n, proxy[i].name);
346 if (luaL_loadbuffer(L, body, len, name)) {
347 fprintf(stderr, "failed to load lua script\n");
352 if (lua_pcall(L, 0, 0, 0)) {
353 fprintf(stderr, "lua script failed: %s\n", lua_tostring(L, -1));