lua.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. #include <libubox/blobmsg.h>
  20. #include <lua.h>
  21. #include <lauxlib.h>
  22. #include <lualib.h>
  23. #include <stdio.h>
  24. #include <poll.h>
  25. #include "uhttpd.h"
  26. #include "plugin.h"
  27. #define UH_LUA_CB "handle_request"
  28. static const struct uhttpd_ops *ops;
  29. static struct config *_conf;
  30. #define conf (*_conf)
  31. static lua_State *_L;
  32. static int uh_lua_recv(lua_State *L)
  33. {
  34. static struct pollfd pfd = {
  35. .fd = STDIN_FILENO,
  36. .events = POLLIN,
  37. };
  38. luaL_Buffer B;
  39. int data_len = 0;
  40. int len;
  41. int r;
  42. len = luaL_optnumber(L, 1, LUAL_BUFFERSIZE);
  43. luaL_buffinit(L, &B);
  44. while(len > 0) {
  45. char *buf;
  46. buf = luaL_prepbuffer(&B);
  47. r = read(STDIN_FILENO, buf,
  48. len < LUAL_BUFFERSIZE ? len : LUAL_BUFFERSIZE);
  49. if (r < 0) {
  50. if (errno == EWOULDBLOCK || errno == EAGAIN) {
  51. pfd.revents = 0;
  52. poll(&pfd, 1, 1000);
  53. if (pfd.revents & POLLIN)
  54. continue;
  55. }
  56. if (errno == EINTR)
  57. continue;
  58. if (!data_len)
  59. data_len = -1;
  60. break;
  61. }
  62. if (!r)
  63. break;
  64. luaL_addsize(&B, r);
  65. data_len += r;
  66. len -= r;
  67. if (r != LUAL_BUFFERSIZE)
  68. break;
  69. }
  70. luaL_pushresult(&B);
  71. lua_pushnumber(L, data_len);
  72. if (data_len > 0) {
  73. lua_pushvalue(L, -2);
  74. lua_remove(L, -3);
  75. return 2;
  76. } else {
  77. lua_remove(L, -2);
  78. return 1;
  79. }
  80. }
  81. static int uh_lua_send(lua_State *L)
  82. {
  83. const char *buf;
  84. size_t len;
  85. buf = luaL_checklstring(L, 1, &len);
  86. if (len > 0)
  87. len = write(STDOUT_FILENO, buf, len);
  88. lua_pushnumber(L, len);
  89. return 1;
  90. }
  91. static int
  92. uh_lua_strconvert(lua_State *L, int (*convert)(char *, int, const char *, int))
  93. {
  94. const char *in_buf;
  95. static char out_buf[4096];
  96. size_t in_len;
  97. int out_len;
  98. in_buf = luaL_checklstring(L, 1, &in_len);
  99. out_len = convert(out_buf, sizeof(out_buf), in_buf, in_len);
  100. if (out_len < 0) {
  101. const char *error;
  102. if (out_len == -1)
  103. error = "buffer overflow";
  104. else
  105. error = "malformed string";
  106. luaL_error(L, "%s on URL conversion\n", error);
  107. }
  108. lua_pushlstring(L, out_buf, out_len);
  109. return 1;
  110. }
  111. static int uh_lua_urldecode(lua_State *L)
  112. {
  113. return uh_lua_strconvert(L, ops->urldecode);
  114. }
  115. static int uh_lua_urlencode(lua_State *L)
  116. {
  117. return uh_lua_strconvert(L, ops->urlencode);
  118. }
  119. static lua_State *uh_lua_state_init(struct lua_prefix *lua)
  120. {
  121. const char *msg = "(unknown error)";
  122. const char *status;
  123. lua_State *L;
  124. int ret;
  125. L = luaL_newstate();
  126. luaL_openlibs(L);
  127. /* build uhttpd api table */
  128. lua_newtable(L);
  129. lua_pushcfunction(L, uh_lua_send);
  130. lua_setfield(L, -2, "send");
  131. lua_pushcfunction(L, uh_lua_send);
  132. lua_setfield(L, -2, "sendc");
  133. lua_pushcfunction(L, uh_lua_recv);
  134. lua_setfield(L, -2, "recv");
  135. lua_pushcfunction(L, uh_lua_urldecode);
  136. lua_setfield(L, -2, "urldecode");
  137. lua_pushcfunction(L, uh_lua_urlencode);
  138. lua_setfield(L, -2, "urlencode");
  139. lua_pushstring(L, conf.docroot);
  140. lua_setfield(L, -2, "docroot");
  141. lua_setglobal(L, "uhttpd");
  142. ret = luaL_loadfile(L, lua->handler);
  143. if (ret) {
  144. status = "loading";
  145. goto error;
  146. }
  147. ret = lua_pcall(L, 0, 0, 0);
  148. if (ret) {
  149. status = "initializing";
  150. goto error;
  151. }
  152. lua_getglobal(L, UH_LUA_CB);
  153. if (!lua_isfunction(L, -1)) {
  154. fprintf(stderr, "Error: Lua handler %s provides no "
  155. UH_LUA_CB "() callback.\n", lua->handler);
  156. exit(1);
  157. }
  158. lua->ctx = L;
  159. return L;
  160. error:
  161. if (!lua_isnil(L, -1))
  162. msg = lua_tostring(L, -1);
  163. fprintf(stderr, "Error %s %s Lua handler: %s\n",
  164. status, lua->handler, msg);
  165. exit(1);
  166. return NULL;
  167. }
  168. static void lua_main(struct client *cl, struct path_info *pi, char *url)
  169. {
  170. struct blob_attr *cur;
  171. const char *error;
  172. struct env_var *var;
  173. lua_State *L = _L;
  174. int path_len, prefix_len;
  175. char *str;
  176. int rem;
  177. lua_getglobal(L, UH_LUA_CB);
  178. /* new env table for this request */
  179. lua_newtable(L);
  180. prefix_len = strlen(pi->name);
  181. path_len = strlen(url);
  182. str = strchr(url, '?');
  183. if (str) {
  184. if (*(str + 1))
  185. pi->query = str + 1;
  186. path_len = str - url;
  187. }
  188. if (prefix_len > 0 && pi->name[prefix_len - 1] == '/')
  189. prefix_len--;
  190. if (path_len > prefix_len) {
  191. lua_pushlstring(L, url + prefix_len,
  192. path_len - prefix_len);
  193. lua_setfield(L, -2, "PATH_INFO");
  194. }
  195. for (var = ops->get_process_vars(cl, pi); var->name; var++) {
  196. if (!var->value)
  197. continue;
  198. lua_pushstring(L, var->value);
  199. lua_setfield(L, -2, var->name);
  200. }
  201. lua_pushnumber(L, 0.9 + (cl->request.version / 10.0));
  202. lua_setfield(L, -2, "HTTP_VERSION");
  203. lua_newtable(L);
  204. blob_for_each_attr(cur, cl->hdr.head, rem) {
  205. lua_pushstring(L, blobmsg_data(cur));
  206. lua_setfield(L, -2, blobmsg_name(cur));
  207. }
  208. lua_setfield(L, -2, "headers");
  209. switch(lua_pcall(L, 1, 0, 0)) {
  210. case LUA_ERRMEM:
  211. case LUA_ERRRUN:
  212. error = luaL_checkstring(L, -1);
  213. if (!error)
  214. error = "(unknown error)";
  215. printf("Status: 500 Internal Server Error\r\n\r\n"
  216. "Unable to launch the requested Lua program:\n"
  217. " %s: %s\n", pi->phys, error);
  218. }
  219. exit(0);
  220. }
  221. static void lua_handle_request(struct client *cl, char *url, struct path_info *pi)
  222. {
  223. struct lua_prefix *p;
  224. static struct path_info _pi;
  225. list_for_each_entry(p, &conf.lua_prefix, list) {
  226. if (!ops->path_match(p->prefix, url))
  227. continue;
  228. pi = &_pi;
  229. pi->name = p->prefix;
  230. pi->phys = p->handler;
  231. _L = p->ctx;
  232. if (!ops->create_process(cl, pi, url, lua_main)) {
  233. ops->client_error(cl, 500, "Internal Server Error",
  234. "Failed to create CGI process: %s",
  235. strerror(errno));
  236. }
  237. return;
  238. }
  239. ops->client_error(cl, 500, "Internal Server Error",
  240. "Failed to lookup matching handler");
  241. }
  242. static bool check_lua_url(const char *url)
  243. {
  244. struct lua_prefix *p;
  245. list_for_each_entry(p, &conf.lua_prefix, list)
  246. if (ops->path_match(p->prefix, url))
  247. return true;
  248. return false;
  249. }
  250. static struct dispatch_handler lua_dispatch = {
  251. .script = true,
  252. .check_url = check_lua_url,
  253. .handle_request = lua_handle_request,
  254. };
  255. static int lua_plugin_init(const struct uhttpd_ops *o, struct config *c)
  256. {
  257. struct lua_prefix *p;
  258. ops = o;
  259. _conf = c;
  260. list_for_each_entry(p, &conf.lua_prefix, list)
  261. uh_lua_state_init(p);
  262. ops->dispatch_add(&lua_dispatch);
  263. return 0;
  264. }
  265. struct uhttpd_plugin uhttpd_plugin = {
  266. .init = lua_plugin_init,
  267. };