Switched to mmap for loading files. Added POST variable handling. Added
sandboxed loadstring, loadfile and dofile functions for Lua. Fixed bug where errors generated during request parsing may cause memory leaks on certain platforms. Fixed bug where non-string errors generated by Lua would cause a segmentation fault. Corrected various typos and unclear code comments.
This commit is contained in:
parent
95e1a981ab
commit
76d187e568
2
LICENCE
2
LICENCE
@ -9,7 +9,7 @@ are met:
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
|
1
Makefile
1
Makefile
@ -8,6 +8,7 @@ LDFLAGS=-O2 -Wl,-Bstatic -lfcgi -llua5.1 -Wl,-Bdynamic -lm -lpthread
|
||||
all: lua-fastcgi
|
||||
|
||||
debug: CFLAGS+=-g -DDEBUG
|
||||
debug: LDFLAGS+=-lrt
|
||||
debug: lua-fastcgi
|
||||
|
||||
lua-fastcgi: src/lua-fastcgi.o src/lfuncs.o src/lua.o src/config.o
|
||||
|
@ -4,7 +4,7 @@ lua-fastcgi
|
||||
lua-fastcgi is a sandboxed Lua backend for FastCGI. That is, you can write
|
||||
Lua scripts that serve up web pages. Options exist in lua-fastcgi.lua
|
||||
to configure a fixed amount of memory, cpu usage, and output bytes
|
||||
for each request. While sandboxed, lua-fastcgi supports a limit set
|
||||
for each request. While sandboxed, lua-fastcgi supports a limited set
|
||||
of functions. If sandboxing is disabled, lua-fastcgi loads the standard
|
||||
libraries and users may load modules as needed.
|
||||
|
||||
|
10
TODO
10
TODO
@ -1,17 +1,17 @@
|
||||
High Priority
|
||||
-------------
|
||||
POST variable parsing
|
||||
Basic sandboxed database support (e.g. GET/PUT)
|
||||
Sandboxed dofile() / loadfile() / loadstring()
|
||||
Function for reading files into string
|
||||
|
||||
|
||||
Medium Priority
|
||||
---------------
|
||||
File Upload Support
|
||||
Database error logging
|
||||
Consider switching to mmap() for file reads
|
||||
File upload support
|
||||
Logging of errors to database
|
||||
|
||||
|
||||
Low Priority
|
||||
------------
|
||||
Database file adapter
|
||||
Session scoped variables
|
||||
Application scoped variables
|
||||
|
@ -30,7 +30,7 @@ return {
|
||||
|
||||
-- Limit page output to x bytes or 0 for unlimited
|
||||
-- Default: 65536
|
||||
output_max = 0,
|
||||
output_max = 65536,
|
||||
|
||||
-- Default content type returned in header
|
||||
content_type = "text/html; charset=iso-8859-1"
|
||||
|
154
src/lfuncs.c
154
src/lfuncs.c
@ -17,9 +17,9 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
int args = lua_gettop(l);
|
||||
|
||||
// Fetch the response
|
||||
lua_pushstring(l, "RESPONSE");
|
||||
lua_pushstring(l, "STATE");
|
||||
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||
LF_response *response = lua_touserdata(l, args+1);
|
||||
LF_state *state = lua_touserdata(l, args+1);
|
||||
lua_pop(l, 1);
|
||||
|
||||
// fetch limits
|
||||
@ -28,9 +28,8 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
size_t *limit = lua_touserdata(l, args+1);
|
||||
lua_pop(l, 1);
|
||||
|
||||
|
||||
// If the response isn't committed, send the header
|
||||
if(!response->committed){
|
||||
if(!state->committed){
|
||||
lua_getglobal(l, "HEADER");
|
||||
if(!lua_istable(l, args+1)){ luaL_error(l, "Invalid HEADER (Not table)."); }
|
||||
|
||||
@ -51,10 +50,10 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
*limit -= (len+10);
|
||||
}
|
||||
|
||||
FCGX_PutStr("Status: ", 8, response->out);
|
||||
FCGX_PutStr(str, len, response->out);
|
||||
FCGX_PutStr("\r\n", 2, response->out);
|
||||
response->committed = 1;
|
||||
FCGX_PutStr("Status: ", 8, state->response);
|
||||
FCGX_PutStr(str, len, state->response);
|
||||
FCGX_PutStr("\r\n", 2, state->response);
|
||||
state->committed = 1;
|
||||
}
|
||||
lua_pop(l, 1); // Pop the status
|
||||
|
||||
@ -82,12 +81,12 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
*limit -= (vallen+keylen+4);
|
||||
}
|
||||
|
||||
FCGX_PutStr(key, keylen, response->out);
|
||||
FCGX_PutStr(": ", 2, response->out);
|
||||
FCGX_PutStr(val, vallen, response->out);
|
||||
FCGX_PutStr("\r\n", 2, response->out);
|
||||
FCGX_PutStr(key, keylen, state->response);
|
||||
FCGX_PutStr(": ", 2, state->response);
|
||||
FCGX_PutStr(val, vallen, state->response);
|
||||
FCGX_PutStr("\r\n", 2, state->response);
|
||||
|
||||
response->committed = 1;
|
||||
state->committed = 1;
|
||||
lua_pop(l, 1); // Clear the last value out
|
||||
}
|
||||
lua_pop(l, 1); // Clear the table out
|
||||
@ -97,8 +96,8 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
*limit -= 2;
|
||||
}
|
||||
|
||||
FCGX_PutS("\r\n", response->out);
|
||||
response->committed = 1;
|
||||
FCGX_PutS("\r\n", state->response);
|
||||
state->committed = 1;
|
||||
}
|
||||
|
||||
size_t strlen;
|
||||
@ -114,7 +113,7 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
*limit -= strlen;
|
||||
}
|
||||
|
||||
FCGX_PutStr(str, strlen, response->out);
|
||||
FCGX_PutStr(str, strlen, state->response);
|
||||
break;
|
||||
|
||||
default: /* Ignore other types */ break;
|
||||
@ -127,7 +126,7 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
(*limit)--;
|
||||
}
|
||||
|
||||
FCGX_PutChar('\n', response->out);
|
||||
FCGX_PutChar('\n', state->response);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -135,3 +134,124 @@ static int LF_pprint(lua_State *l, int cr)
|
||||
|
||||
int LF_print(lua_State *l){ return LF_pprint(l, 1); }
|
||||
int LF_write(lua_State *l){ return LF_pprint(l, 0); }
|
||||
|
||||
|
||||
int LF_loadstring(lua_State *l)
|
||||
{
|
||||
size_t sz;
|
||||
const char *s = luaL_checklstring(l, 1, &sz);
|
||||
|
||||
if(sz > 3 && memcmp(s, LUA_SIGNATURE, 4) == 0){
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Compiled bytecode not supported.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(luaL_loadbuffer(l, s, sz, luaL_optstring(l, 2, s)) == 0){
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushnil(l);
|
||||
lua_insert(l, -2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int LF_loadfile(lua_State *l)
|
||||
{
|
||||
size_t sz;
|
||||
const char *spath = luaL_checklstring(l, 1, &sz);
|
||||
|
||||
lua_pushstring(l, "DOCUMENT_ROOT");
|
||||
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||
char *document_root = lua_touserdata(l, -1);
|
||||
lua_pop(l, 1);
|
||||
|
||||
if(document_root == NULL){
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "DOCUMENT_ROOT not defined.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
size_t dz = strlen(document_root);
|
||||
|
||||
if(dz == 0){
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "DOCUMENT_ROOT empty.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
size_t hz = dz + sz;
|
||||
if((hz + 2) > 4096){
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Path too large.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
char hpath[4096];
|
||||
memcpy(&hpath[0], document_root, dz);
|
||||
if(hpath[dz-1] != '/' && spath[0] != '/'){ hpath[dz] = '/'; }
|
||||
memcpy(&hpath[dz+1], spath, sz);
|
||||
hpath[hz+1] = 0;
|
||||
|
||||
char rpath[4096];
|
||||
char *ptr = realpath(hpath, rpath);
|
||||
if(ptr == NULL || memcmp(document_root, rpath, dz) != 0){
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Invalid path.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
switch(LF_fileload(l, &spath[0], &hpath[0])){
|
||||
case 0:
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case LF_ERRACCESS:
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Access denied.");
|
||||
break;
|
||||
|
||||
case LF_ERRMEMORY:
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Not enough memory.");
|
||||
break;
|
||||
|
||||
case LF_ERRNOTFOUND:
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "No such file or directory.");
|
||||
break;
|
||||
|
||||
case LF_ERRSYNTAX:
|
||||
lua_pushnil(l);
|
||||
lua_insert(l, -2);
|
||||
break;
|
||||
|
||||
case LF_ERRBYTECODE:
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Compiled bytecode not supported.");
|
||||
break;
|
||||
|
||||
case LF_ERRNOPATH:
|
||||
case LF_ERRNONAME:
|
||||
lua_pushnil(l);
|
||||
lua_pushstring(l, "Invalid path.");
|
||||
break;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
int LF_dofile(lua_State *l)
|
||||
{
|
||||
int r = LF_loadfile(l);
|
||||
if(r == 1 && lua_isfunction(l, -1)){
|
||||
lua_call(l, 0, LUA_MULTRET);
|
||||
return lua_gettop(l) - 1;
|
||||
} else {
|
||||
lua_error(l);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
10
src/lfuncs.h
10
src/lfuncs.h
@ -1,4 +1,14 @@
|
||||
// Writes FCGI output followed by a carriage return
|
||||
int LF_print(lua_State *);
|
||||
|
||||
// Writes FCGI output without a carriage return
|
||||
int LF_write(lua_State *);
|
||||
|
||||
// loadstring() function with anti-bytecode security measures
|
||||
int LF_loadstring(lua_State *);
|
||||
|
||||
// loadfile() function with sandboxing security measures
|
||||
int LF_loadfile(lua_State *);
|
||||
|
||||
// dofile() function with sandboxing security measures
|
||||
int LF_dofile(lua_State *);
|
||||
|
@ -25,12 +25,12 @@ static char *http_status_strings[] = {
|
||||
|
||||
|
||||
#define senderror(status_code,error_string) \
|
||||
if(!response.committed){ \
|
||||
if(!state.committed){ \
|
||||
FCGX_FPrintF(request.out, "Status: %d %s\r\n", status_code, http_status_strings[status_code]); \
|
||||
FCGX_FPrintF(request.out, "Content-Type: %s\r\n\r\n", config->content_type); \
|
||||
response.committed = 1; \
|
||||
state.committed = 1; \
|
||||
} \
|
||||
FCGX_PutS(error_string, response.out);
|
||||
FCGX_PutS(error_string, state.response);
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -64,7 +64,7 @@ void *thread_run(void *arg)
|
||||
LF_params *params = arg;
|
||||
LF_config *config = params->config;
|
||||
LF_limits *limits = LF_newlimits();
|
||||
LF_response response;
|
||||
LF_state state;
|
||||
lua_State *l;
|
||||
|
||||
FCGX_Request request;
|
||||
@ -86,31 +86,43 @@ void *thread_run(void *arg)
|
||||
|
||||
#ifdef DEBUG
|
||||
printvars(&request);
|
||||
struct timespec rstart, rend;
|
||||
clock_gettime(CLOCK_MONOTONIC, &rstart);
|
||||
#endif
|
||||
|
||||
LF_parserequest(l, &request, &state);
|
||||
|
||||
#ifdef DEBUG
|
||||
clock_gettime(CLOCK_MONOTONIC, &rend);
|
||||
// Assumes the request returns in less than a second (which it should)
|
||||
printf("Request parsed in %luns\n", (rend.tv_nsec-rstart.tv_nsec));
|
||||
#endif
|
||||
|
||||
LF_parserequest(l, &request, &response);
|
||||
LF_enablelimits(l, limits);
|
||||
|
||||
switch(LF_loadfile(l)){
|
||||
switch(LF_loadscript(l)){
|
||||
case 0:
|
||||
if(lua_pcall(l, 0, 0, 0)){
|
||||
senderror(500, lua_tostring(l, -1));
|
||||
} else if(!response.committed){
|
||||
if(lua_isstring(l, -1)){
|
||||
senderror(500, lua_tostring(l, -1));
|
||||
} else {
|
||||
senderror(500, "unspecified lua error");
|
||||
}
|
||||
} else if(!state.committed){
|
||||
senderror(200, "");
|
||||
}
|
||||
break;
|
||||
|
||||
case EACCES:
|
||||
senderror(403, lua_tostring(l, -1));
|
||||
break;
|
||||
|
||||
case ENOENT:
|
||||
senderror(404, lua_tostring(l, -1));
|
||||
break;
|
||||
|
||||
default:
|
||||
senderror(500, lua_tostring(l, -1));
|
||||
case LF_ERRACCESS: senderror(403, "access denied"); break;
|
||||
case LF_ERRMEMORY: senderror(500, "not enough memory"); break;
|
||||
case LF_ERRNOTFOUND:
|
||||
printf("404\n");
|
||||
senderror(404, "no such file or directory");
|
||||
break;
|
||||
case LF_ERRSYNTAX: senderror(500, lua_tostring(l, -1)); break;
|
||||
case LF_ERRBYTECODE: senderror(403, "compiled bytecode not supported"); break;
|
||||
case LF_ERRNOPATH: senderror(500, "SCRIPT_FILENAME not provided"); break;
|
||||
case LF_ERRNONAME: senderror(500, "SCRIPT_NAME not provided"); break;
|
||||
}
|
||||
|
||||
FCGX_Finish_r(&request);
|
||||
|
202
src/lua.c
202
src/lua.c
@ -1,10 +1,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
@ -17,14 +20,6 @@
|
||||
#include "lua.h"
|
||||
#include "lfuncs.h"
|
||||
|
||||
#define LF_BUFFERSIZE 4096
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
char buffer[LF_BUFFERSIZE];
|
||||
size_t total;
|
||||
} LF_loaderdata;
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void LF_printstack(lua_State *l)
|
||||
@ -119,7 +114,6 @@ void LF_setlimits(LF_limits *limits, size_t memory, size_t output, uint32_t cpu_
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LF_enablelimits(lua_State *l, LF_limits *limits)
|
||||
{
|
||||
if(limits->cpu.tv_usec > 0 || limits->cpu.tv_sec > 0){
|
||||
@ -142,7 +136,13 @@ void LF_enablelimits(lua_State *l, LF_limits *limits)
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
if(limits->memory){ lua_setallocf(l, &LF_limit_alloc, &limits->memory); }
|
||||
if(limits->memory){
|
||||
lua_pushstring(l, "MEMORY_LIMIT");
|
||||
lua_pushlightuserdata(l, &limits->memory);
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
|
||||
lua_setallocf(l, &LF_limit_alloc, &limits->memory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -174,13 +174,16 @@ lua_State *LF_newstate(int sandbox, char *content_type)
|
||||
lua_call(l, 1, 0);
|
||||
|
||||
// Nil out unsafe functions/objects
|
||||
LF_nilglobal(l, "dofile");
|
||||
LF_nilglobal(l, "load");
|
||||
LF_nilglobal(l, "loadfile");
|
||||
LF_nilglobal(l, "xpcall");
|
||||
LF_nilglobal(l, "pcall");
|
||||
LF_nilglobal(l, "module");
|
||||
LF_nilglobal(l, "require");
|
||||
|
||||
// Override unsafe functions
|
||||
lua_register(l, "loadstring", &LF_loadstring);
|
||||
lua_register(l, "loadfile", &LF_loadfile);
|
||||
lua_register(l, "dofile", &LF_dofile);
|
||||
}
|
||||
|
||||
// Register the print function
|
||||
@ -202,7 +205,7 @@ lua_State *LF_newstate(int sandbox, char *content_type)
|
||||
|
||||
|
||||
// Set GET variables
|
||||
static void LF_parsequerystring(lua_State *l, char *query_string)
|
||||
static void LF_parsequerystring(lua_State *l, char *query_string, char *table)
|
||||
{
|
||||
lua_newtable(l);
|
||||
|
||||
@ -262,7 +265,7 @@ static void LF_parsequerystring(lua_State *l, char *query_string)
|
||||
if(lua_gettop(l) == (stack+2)){ lua_rawset(l, stack); }
|
||||
|
||||
// Finally, set the table
|
||||
lua_setglobal(l, "GET");
|
||||
lua_setglobal(l, table);
|
||||
return;
|
||||
break;
|
||||
|
||||
@ -275,8 +278,17 @@ static void LF_parsequerystring(lua_State *l, char *query_string)
|
||||
|
||||
|
||||
// Parses fastcgi request
|
||||
void LF_parserequest(lua_State *l, FCGX_Request *request, LF_response *response)
|
||||
void LF_parserequest(lua_State *l, FCGX_Request *request, LF_state *state)
|
||||
{
|
||||
uintmax_t content_length = 0;
|
||||
char *content_type = NULL;
|
||||
|
||||
state->committed = 0;
|
||||
state->response = request->out;
|
||||
lua_pushstring(l, "STATE");
|
||||
lua_pushlightuserdata(l, state);
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
|
||||
lua_newtable(l);
|
||||
for(char **p = request->envp; *p; ++p){
|
||||
char *vptr = strchr(*p, '=');
|
||||
@ -286,76 +298,128 @@ void LF_parserequest(lua_State *l, FCGX_Request *request, LF_response *response)
|
||||
lua_pushstring(l, (vptr+1)); // Push Value
|
||||
lua_rawset(l, 1); // Set key/value into table
|
||||
|
||||
if(keylen == 12 && memcmp(*p, "QUERY_STRING", 12) == 0){
|
||||
LF_parsequerystring(l, (vptr+1));
|
||||
switch(keylen){
|
||||
case 11:
|
||||
if(memcmp(*p, "SCRIPT_NAME", 11) == 0){
|
||||
lua_pushstring(l, "SCRIPT_NAME");
|
||||
lua_pushlightuserdata(l, (vptr+1));
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
}
|
||||
break;
|
||||
|
||||
case 12:
|
||||
if(memcmp(*p, "QUERY_STRING", 12) == 0){
|
||||
LF_parsequerystring(l, (vptr+1), "GET");
|
||||
} if(memcmp(*p, "CONTENT_TYPE", 12) == 0){
|
||||
content_type = (vptr+1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 13:
|
||||
if(memcmp(*p, "DOCUMENT_ROOT", 13) == 0){
|
||||
lua_pushstring(l, "DOCUMENT_ROOT");
|
||||
lua_pushlightuserdata(l, (vptr+1));
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
}
|
||||
break;
|
||||
|
||||
case 14:
|
||||
if(memcmp(*p, "CONTENT_LENGTH", 14) == 0){
|
||||
content_length = strtoumax((vptr+1), NULL, 10);
|
||||
}
|
||||
break;
|
||||
|
||||
case 15:
|
||||
if(memcmp(*p, "SCRIPT_FILENAME", 15) == 0){
|
||||
lua_pushstring(l, "SCRIPT_FILENAME");
|
||||
lua_pushlightuserdata(l, (vptr+1));
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
lua_setglobal(l, "REQUEST");
|
||||
|
||||
response->committed = 0;
|
||||
response->out = request->out;
|
||||
lua_pushstring(l, "RESPONSE");
|
||||
lua_pushlightuserdata(l, response);
|
||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
|
||||
static const char *LF_filereader(lua_State *l, void *data, size_t *size)
|
||||
{
|
||||
LF_loaderdata *ld = data;
|
||||
|
||||
*size = read(ld->fd, ld->buffer, LF_BUFFERSIZE);
|
||||
|
||||
if(ld->total == 0 && *size > 3){
|
||||
if(memcmp(ld->buffer, LUA_SIGNATURE, 4) == 0){
|
||||
luaL_error(l, "Compiled bytecode not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
switch(*size){
|
||||
case 0: return NULL;
|
||||
case -1: luaL_error(l, strerror(errno));
|
||||
default:
|
||||
ld->total += *size;
|
||||
return ld->buffer;
|
||||
if(content_length > 0 && content_type != NULL && memcmp(content_type, "application/x-www-form-urlencoded", 33) == 0){
|
||||
char *content = lua_newuserdata(l, content_length+1);
|
||||
int r = FCGX_GetStr(
|
||||
content, (content_length > INT_MAX ? INT_MAX : content_length),
|
||||
request->in
|
||||
);
|
||||
*(content + r) = 0; // Add NUL byte at end for proper string
|
||||
LF_parsequerystring(l, content, "POST");
|
||||
lua_pop(l, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Loads a lua file into a state
|
||||
int LF_loadfile(lua_State *l)
|
||||
// Load script by name and path
|
||||
int LF_fileload(lua_State *l, const char *name, char *scriptpath)
|
||||
{
|
||||
lua_getglobal(l, "REQUEST");
|
||||
char *script = NULL;
|
||||
int fd = -1, r = 0;
|
||||
struct stat sb;
|
||||
|
||||
int stack = lua_gettop(l);
|
||||
|
||||
lua_pushstring(l, "SCRIPT_FILENAME");
|
||||
lua_rawget(l, stack);
|
||||
const char *path = lua_tostring(l, stack+1);
|
||||
|
||||
lua_pushstring(l, "SCRIPT_NAME");
|
||||
lua_rawget(l, stack);
|
||||
const char *name = lua_tostring(l, stack+2);
|
||||
|
||||
LF_loaderdata ld;
|
||||
ld.total = 0;
|
||||
ld.fd = open(path, O_RDONLY);
|
||||
if(ld.fd == -1){
|
||||
lua_pushstring(l, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
if(scriptpath == NULL){ return LF_ERRNOPATH; }
|
||||
if(name == NULL){ return LF_ERRNONAME; }
|
||||
|
||||
// Generate a string with an '=' followed by the script name
|
||||
// this ensures lua will generation a reasonable error
|
||||
size_t len = strlen(name) + 1;
|
||||
char scriptname[len + 1];
|
||||
size_t namelen = strlen(name);
|
||||
char scriptname[namelen+2];
|
||||
scriptname[0] = '=';
|
||||
memcpy(&scriptname[1], name, len);
|
||||
memcpy(&scriptname[1], name, namelen+1);
|
||||
|
||||
int r = lua_load(l, &LF_filereader, &ld, scriptname);
|
||||
if((fd = open(scriptpath, O_RDONLY)) == -1){ goto errorL; }
|
||||
|
||||
close(ld.fd);
|
||||
return (r == 0 ? 0 : ENOMSG);
|
||||
if(fstat(fd, &sb) == -1){ goto errorL; }
|
||||
|
||||
if((script = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == NULL){
|
||||
goto errorL;
|
||||
}
|
||||
|
||||
if(madvise(script, sb.st_size, MADV_SEQUENTIAL) == -1){ goto errorL; }
|
||||
|
||||
if(sb.st_size > 3 && memcmp(script, LUA_SIGNATURE, 4) == 0){
|
||||
r = LF_ERRBYTECODE;
|
||||
} else {
|
||||
switch(luaL_loadbuffer(l, script, sb.st_size, scriptname)){
|
||||
case LUA_ERRSYNTAX: r = LF_ERRSYNTAX; break;
|
||||
case LUA_ERRMEM: r = LF_ERRMEMORY; break;
|
||||
}
|
||||
}
|
||||
|
||||
if(script != NULL){ munmap(script, sb.st_size); }
|
||||
if(fd != -1){ close(fd); }
|
||||
return r;
|
||||
|
||||
errorL:
|
||||
if(script != NULL){ munmap(script, sb.st_size); }
|
||||
if(fd != -1){ close(fd); }
|
||||
switch(errno){
|
||||
case EACCES: return r = LF_ERRACCESS;
|
||||
case ENOENT: return r = LF_ERRNOTFOUND;
|
||||
case ENOMEM: return r = LF_ERRMEMORY;
|
||||
default: return r = LF_ERRANY;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// Loads script specified in registryindex into lua state
|
||||
int LF_loadscript(lua_State *l)
|
||||
{
|
||||
lua_pushstring(l, "SCRIPT_FILENAME");
|
||||
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||
char *scriptpath = lua_touserdata(l, 1);
|
||||
lua_pop(l, 1);
|
||||
|
||||
lua_pushstring(l, "SCRIPT_NAME");
|
||||
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||
char *name = lua_touserdata(l, 1);
|
||||
lua_pop(l, 1);
|
||||
|
||||
return LF_fileload(l, name, scriptpath);
|
||||
}
|
||||
|
||||
|
||||
|
19
src/lua.h
19
src/lua.h
@ -1,7 +1,17 @@
|
||||
#define LF_ERRNONE 0
|
||||
#define LF_ERRANY 1
|
||||
#define LF_ERRACCESS 2
|
||||
#define LF_ERRMEMORY 3
|
||||
#define LF_ERRNOTFOUND 4
|
||||
#define LF_ERRSYNTAX 5
|
||||
#define LF_ERRBYTECODE 6
|
||||
#define LF_ERRNOPATH 7
|
||||
#define LF_ERRNONAME 8
|
||||
|
||||
typedef struct {
|
||||
FCGX_Stream *response;
|
||||
int committed;
|
||||
FCGX_Stream *out;
|
||||
} LF_response;
|
||||
} LF_state;
|
||||
|
||||
typedef struct {
|
||||
size_t memory;
|
||||
@ -14,7 +24,8 @@ lua_State *LF_newstate(int, char *);
|
||||
LF_limits *LF_newlimits();
|
||||
void LF_setlimits(LF_limits *, size_t, size_t, uint32_t, uint32_t);
|
||||
void LF_enablelimits(lua_State *, LF_limits *);
|
||||
void LF_parserequest(lua_State *l, FCGX_Request *, LF_response *);
|
||||
void LF_parserequest(lua_State *l, FCGX_Request *, LF_state *);
|
||||
void LF_emptystack(lua_State *);
|
||||
int LF_loadfile(lua_State *);
|
||||
int LF_fileload(lua_State *, const char *, char *);
|
||||
int LF_loadscript(lua_State *);
|
||||
void LF_closestate(lua_State *);
|
||||
|
Reference in New Issue
Block a user