1 /* 2 * uhttpd - Tiny single-threaded httpd 3 * 4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org> 5 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <libubox/blobmsg.h> 21 #include <lua.h> 22 #include <lauxlib.h> 23 #include <lualib.h> 24 #include <stdio.h> 25 #include <poll.h> 26 27 #include "uhttpd.h" 28 #include "plugin.h" 29 30 #define UH_LUA_CB "handle_request" 31 32 static const struct uhttpd_ops *ops; 33 static struct config *_conf; 34 #define conf (*_conf) 35 36 static lua_State *_L; 37 38 static int uh_lua_recv(lua_State *L) 39 { 40 static struct pollfd pfd = { 41 .fd = STDIN_FILENO, 42 .events = POLLIN, 43 }; 44 luaL_Buffer B; 45 int data_len = 0; 46 int len; 47 int r; 48 49 len = luaL_optnumber(L, 1, LUAL_BUFFERSIZE); 50 luaL_buffinit(L, &B); 51 while(len > 0) { 52 char *buf; 53 54 buf = luaL_prepbuffer(&B); 55 r = read(STDIN_FILENO, buf, 56 len < LUAL_BUFFERSIZE ? len : LUAL_BUFFERSIZE); 57 if (r < 0) { 58 if (errno == EWOULDBLOCK || errno == EAGAIN) { 59 pfd.revents = 0; 60 poll(&pfd, 1, 1000); 61 if (pfd.revents & POLLIN) 62 continue; 63 } 64 if (errno == EINTR) 65 continue; 66 67 if (!data_len) 68 data_len = -1; 69 break; 70 } 71 if (!r) 72 break; 73 74 luaL_addsize(&B, r); 75 data_len += r; 76 len -= r; 77 if (r != LUAL_BUFFERSIZE) 78 break; 79 } 80 81 luaL_pushresult(&B); 82 lua_pushnumber(L, data_len); 83 if (data_len > 0) { 84 lua_pushvalue(L, -2); 85 lua_remove(L, -3); 86 return 2; 87 } else { 88 lua_remove(L, -2); 89 return 1; 90 } 91 } 92 93 static int uh_lua_send(lua_State *L) 94 { 95 const char *buf; 96 size_t len; 97 98 buf = luaL_checklstring(L, 1, &len); 99 if (len > 0) 100 len = write(STDOUT_FILENO, buf, len); 101 102 lua_pushnumber(L, len); 103 return 1; 104 } 105 106 static int 107 uh_lua_strconvert(lua_State *L, int (*convert)(char *, int, const char *, int)) 108 { 109 const char *in_buf; 110 static char out_buf[4096]; 111 size_t in_len; 112 int out_len; 113 114 in_buf = luaL_checklstring(L, 1, &in_len); 115 out_len = convert(out_buf, sizeof(out_buf), in_buf, in_len); 116 117 if (out_len < 0) { 118 const char *error; 119 120 if (out_len == -1) 121 error = "buffer overflow"; 122 else 123 error = "malformed string"; 124 125 luaL_error(L, "%s on URL conversion\n", error); 126 } 127 128 lua_pushlstring(L, out_buf, out_len); 129 return 1; 130 } 131 132 static int uh_lua_urldecode(lua_State *L) 133 { 134 return uh_lua_strconvert(L, ops->urldecode); 135 } 136 137 static int uh_lua_urlencode(lua_State *L) 138 { 139 return uh_lua_strconvert(L, ops->urlencode); 140 } 141 142 static lua_State *uh_lua_state_init(struct lua_prefix *lua) 143 { 144 const char *msg = "(unknown error)"; 145 const char *status; 146 lua_State *L; 147 int ret; 148 149 L = luaL_newstate(); 150 luaL_openlibs(L); 151 152 /* build uhttpd api table */ 153 lua_newtable(L); 154 155 lua_pushcfunction(L, uh_lua_send); 156 lua_setfield(L, -2, "send"); 157 158 lua_pushcfunction(L, uh_lua_send); 159 lua_setfield(L, -2, "sendc"); 160 161 lua_pushcfunction(L, uh_lua_recv); 162 lua_setfield(L, -2, "recv"); 163 164 lua_pushcfunction(L, uh_lua_urldecode); 165 lua_setfield(L, -2, "urldecode"); 166 167 lua_pushcfunction(L, uh_lua_urlencode); 168 lua_setfield(L, -2, "urlencode"); 169 170 lua_pushstring(L, conf.docroot); 171 lua_setfield(L, -2, "docroot"); 172 173 lua_setglobal(L, "uhttpd"); 174 175 ret = luaL_loadfile(L, lua->handler); 176 if (ret) { 177 status = "loading"; 178 goto error; 179 } 180 181 ret = lua_pcall(L, 0, 0, 0); 182 if (ret) { 183 status = "initializing"; 184 goto error; 185 } 186 187 lua_getglobal(L, UH_LUA_CB); 188 if (!lua_isfunction(L, -1)) { 189 fprintf(stderr, "Error: Lua handler %s provides no " 190 UH_LUA_CB "() callback.\n", lua->handler); 191 exit(1); 192 } 193 194 lua->ctx = L; 195 196 return L; 197 198 error: 199 if (!lua_isnil(L, -1)) 200 msg = lua_tostring(L, -1); 201 202 fprintf(stderr, "Error %s %s Lua handler: %s\n", 203 status, lua->handler, msg); 204 exit(1); 205 return NULL; 206 } 207 208 static void lua_main(struct client *cl, struct path_info *pi, char *url) 209 { 210 struct blob_attr *cur; 211 const char *error; 212 struct env_var *var; 213 lua_State *L = _L; 214 int path_len, prefix_len; 215 char *str; 216 int rem; 217 218 lua_getglobal(L, UH_LUA_CB); 219 220 /* new env table for this request */ 221 lua_newtable(L); 222 223 prefix_len = strlen(pi->name); 224 path_len = strlen(url); 225 str = strchr(url, '?'); 226 if (str) { 227 if (*(str + 1)) 228 pi->query = str + 1; 229 path_len = str - url; 230 } 231 232 if (prefix_len > 0 && pi->name[prefix_len - 1] == '/') 233 prefix_len--; 234 235 if (path_len > prefix_len) { 236 lua_pushlstring(L, url + prefix_len, 237 path_len - prefix_len); 238 lua_setfield(L, -2, "PATH_INFO"); 239 } 240 241 for (var = ops->get_process_vars(cl, pi); var->name; var++) { 242 if (!var->value) 243 continue; 244 245 lua_pushstring(L, var->value); 246 lua_setfield(L, -2, var->name); 247 } 248 249 lua_pushnumber(L, 0.9 + (cl->request.version / 10.0)); 250 lua_setfield(L, -2, "HTTP_VERSION"); 251 252 lua_newtable(L); 253 blob_for_each_attr(cur, cl->hdr.head, rem) { 254 lua_pushstring(L, blobmsg_data(cur)); 255 lua_setfield(L, -2, blobmsg_name(cur)); 256 } 257 lua_setfield(L, -2, "headers"); 258 259 switch(lua_pcall(L, 1, 0, 0)) { 260 case LUA_ERRMEM: 261 case LUA_ERRRUN: 262 error = luaL_checkstring(L, -1); 263 if (!error) 264 error = "(unknown error)"; 265 266 printf("Status: 500 Internal Server Error\r\n\r\n" 267 "Unable to launch the requested Lua program:\n" 268 " %s: %s\n", pi->phys, error); 269 } 270 271 exit(0); 272 } 273 274 static void lua_handle_request(struct client *cl, char *url, struct path_info *pi) 275 { 276 struct lua_prefix *p; 277 static struct path_info _pi; 278 279 list_for_each_entry(p, &conf.lua_prefix, list) { 280 if (!ops->path_match(p->prefix, url)) 281 continue; 282 283 pi = &_pi; 284 pi->name = p->prefix; 285 pi->phys = p->handler; 286 287 _L = p->ctx; 288 289 if (!ops->create_process(cl, pi, url, lua_main)) { 290 ops->client_error(cl, 500, "Internal Server Error", 291 "Failed to create CGI process: %s", 292 strerror(errno)); 293 } 294 295 return; 296 } 297 298 ops->client_error(cl, 500, "Internal Server Error", 299 "Failed to lookup matching handler"); 300 } 301 302 static bool check_lua_url(const char *url) 303 { 304 struct lua_prefix *p; 305 306 list_for_each_entry(p, &conf.lua_prefix, list) 307 if (ops->path_match(p->prefix, url)) 308 return true; 309 310 return false; 311 } 312 313 static struct dispatch_handler lua_dispatch = { 314 .script = true, 315 .check_url = check_lua_url, 316 .handle_request = lua_handle_request, 317 }; 318 319 static int lua_plugin_init(const struct uhttpd_ops *o, struct config *c) 320 { 321 struct lua_prefix *p; 322 323 ops = o; 324 _conf = c; 325 326 list_for_each_entry(p, &conf.lua_prefix, list) 327 uh_lua_state_init(p); 328 329 ops->dispatch_add(&lua_dispatch); 330 return 0; 331 } 332 333 struct uhttpd_plugin uhttpd_plugin = { 334 .init = lua_plugin_init, 335 }; 336
This page was automatically generated by LXR 0.3.1. • OpenWrt