/* * luci * Copyright (C) 2008 John Crispin * Copyright (C) 2008 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation * * 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. */ #include #include #include #include #include #include #include #define LUAMAIN "luci.lua" static lua_State *L = NULL; extern int luci_parse_header (lua_State *L); static int luci_init(struct httpd_plugin *p) { char *path = NULL; int ret = 0; L = luaL_newstate(); if (!L) goto error; luaL_openlibs(L); path = malloc(strlen(p->dir) + sizeof(LUAMAIN) + 2); strcpy(path, p->dir); strcat(path, "/" LUAMAIN); ret = luaL_dofile(L, path); lua_getfield(L, LUA_GLOBALSINDEX, "luci-plugin"); do { if (!lua_istable(L, -1)) { ret = 1; break; } lua_getfield(L, -1, "init"); if (!lua_isfunction(L, -1)) break; lua_pushstring(L, p->dir); ret = lua_pcall(L, 1, 0, 0); } while (0); free(path); if (ret != 0) goto error; return 1; error: fprintf(stderr, "Error: "); if (L) { const char *s = lua_tostring(L, -1); if (!s) s = "unknown error"; fprintf(stderr, "%s\n", s); lua_close(L); } else { fprintf(stderr, "Out of memory!\n"); } return 0; } static void pushvar(char *name, char *val) { if (!val) return; lua_pushstring(L, val); lua_setfield(L, -2, name); } static int luci_pcall(lua_State *L, char *func, int narg) { int ret; ret = lua_pcall(L, narg, narg, 0); if (ret) { const char *s = lua_tostring(L, -1); if (s) fprintf(stderr, "Error running %s: %s\n", func, s); return ret; } if (!narg) return ret; ret = lua_isnumber(L, -1); if (!ret) goto done; ret = lua_tonumber(L, -1); done: lua_pop(L, 1); return ret; } static int luci_prepare_req(struct httpd_plugin *p, struct http_context *ctx) { int ret; lua_getglobal(L, "luci-plugin"); lua_getfield(L, -1, "prepare_req"); ret = lua_isfunction(L, -1); if (!ret) goto done; lua_pushstring(L, ctx->uri); ret = luci_pcall(L, "prepare_req", 1); done: lua_pop(L, 1); return ret; } static int luci_handle_req(struct httpd_plugin *p, struct http_context *ctx) { int ret; lua_newtable(L); /* new table for the http context */ /* convert http_context data structure to lua table */ #define PUSH(x) pushvar(#x, ctx->x) PUSH(request_method); PUSH(server_addr); PUSH(server_proto); PUSH(query_string); PUSH(remote_addr); lua_pushinteger(L, ctx->remote_port); lua_setfield(L, -2, "remote_port"); PUSH(content_type); PUSH(content_length); PUSH(http_accept); #undef PUSH if (!strncmp(ctx->uri, p->prefix, strlen(p->prefix))) pushvar("uri", ctx->uri + strlen(p->prefix)); else pushvar("uri", ctx->uri); /* make sure the global 'luci' table is prepared */ lua_getglobal(L, "luci-plugin"); if (!lua_istable(L, -1)) return 0; lua_getfield(L, -1, "init_req"); if (!lua_isfunction(L, -1)) { /* ignore error */ lua_pop(L, 1); } else { lua_pushvalue(L, -3); luci_pcall(L, "init_req", 1); } /* storage space for cgi variables */ lua_newtable(L); lua_pushvalue(L, -1); /* copy for setfield */ lua_setfield(L, -3, "vars"); lua_pushvalue(L, -3); /* the http context table */ /* * make luci_parse_header a closure * argument 1: the luci.vars table * argument 2: the http context table */ lua_pushcclosure(L, luci_parse_header, 2); ret = luci_pcall(L, "parse_header", 0); lua_getfield(L, -1, "handle_req"); ret = lua_isfunction(L, -1); if (!ret) goto done; lua_pushvalue(L, -3); ret = luci_pcall(L, "handle_req", 1); /* pop the luci and http context tables */ done: lua_pop(L, 2); return ret; } static void luci_unload(struct httpd_plugin *p) { lua_close(L); } HTTPD_PLUGIN { .prefix = "/luci/", .init = luci_init, .done = luci_unload, .prepare_req = luci_prepare_req, .handle_req = luci_handle_req, };