allow the sgi-webuci prepare_req function to reload the lua context if necessary...
[project/luci.git] / libs / sgi-webuci / src / luci.c
1 /*
2  * luci
3  * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
4  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <string.h>
17 #include <stdio.h>
18 #include <boa-plugin.h>
19 #include <lauxlib.h>
20 #include <lualib.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23
24 #define LUAMAIN "luci.lua"
25
26 static lua_State *L = NULL;
27
28 extern int luci_parse_header (lua_State *L);
29
30 static lua_State *luci_context_init(struct httpd_plugin *p)
31 {
32         char *path = NULL;
33         lua_State *Lnew;
34         int ret = 0;
35
36         Lnew = luaL_newstate();
37         if (!Lnew)
38                 goto error;
39
40         luaL_openlibs(Lnew);
41
42         path = malloc(strlen(p->dir) + sizeof(LUAMAIN) + 2);
43         strcpy(path, p->dir);
44         strcat(path, "/" LUAMAIN);
45
46         ret = luaL_dofile(Lnew, path);
47
48         lua_getfield(Lnew, LUA_GLOBALSINDEX, "luci-plugin");
49         do {
50                 if (!lua_istable(Lnew, -1)) {
51                         ret = 1;
52                         break;
53                 }
54
55                 lua_getfield(Lnew, -1, "init");
56                 if (!lua_isfunction(Lnew, -1))
57                         break;
58
59                 lua_pushstring(Lnew, p->dir);
60                 ret = lua_pcall(Lnew, 1, 0, 0);
61         } while (0);
62         free(path);
63
64         if (ret != 0)
65                 goto error;
66
67         return Lnew;
68
69 error:
70         fprintf(stderr, "Error: ");
71         if (Lnew) {
72                 const char *s = lua_tostring(Lnew, -1);
73                 if (!s)
74                         s = "unknown error";
75                 fprintf(stderr, "%s\n", s);
76                 lua_close(Lnew);
77         } else {
78                 fprintf(stderr, "Out of memory!\n");
79         }
80         return NULL;
81 }
82
83 static int luci_init(struct httpd_plugin *p)
84 {
85         L = luci_context_init(p);
86         return (L != NULL);
87 }
88
89 static void pushvar(char *name, char *val)
90 {
91         if (!val)
92                 return;
93
94         lua_pushstring(L, val);
95         lua_setfield(L, -2, name);
96 }
97
98 static int luci_pcall(lua_State *L, char *func, int narg)
99 {
100         int ret;
101
102         ret = lua_pcall(L, narg, narg, 0);
103         if (ret) {
104                 const char *s = lua_tostring(L, -1);
105                 if (s)
106                         fprintf(stderr, "Error running %s: %s\n", func, s);
107                 return ret;
108         }
109         if (!narg)
110                 return ret;
111
112         ret = lua_isnumber(L, -1);
113         if (!ret)
114                 goto done;
115
116         ret = lua_tonumber(L, -1);
117
118 done:
119         lua_pop(L, 1);
120         return ret;
121 }
122
123 static int luci_prepare_req(struct httpd_plugin *p, struct http_context *ctx)
124 {
125         int ret;
126         bool reload = false;
127
128         lua_getglobal(L, "luci-plugin");
129         lua_getfield(L, -1, "reload");
130         if (lua_isboolean(L, -1))
131                 reload = lua_toboolean(L, -1);
132         lua_pop(L, 1);
133
134         if (reload) {
135                 lua_close(L);
136                 L = luci_context_init(p);
137                 lua_getglobal(L, "luci-plugin");
138         }
139
140         lua_getfield(L, -1, "prepare_req");
141
142         ret = lua_isfunction(L, -1);
143         if (!ret)
144                 goto done;
145
146         lua_pushstring(L, ctx->uri);
147
148         ret = luci_pcall(L, "prepare_req", 1);
149
150 done:
151         lua_pop(L, 1);
152         return ret;
153 }
154
155 static int luci_handle_req(struct httpd_plugin *p, struct http_context *ctx)
156 {
157         int ret;
158
159         lua_newtable(L); /* new table for the http context */
160
161         /* convert http_context data structure to lua table */
162 #define PUSH(x) pushvar(#x, ctx->x)
163         PUSH(request_method);
164         PUSH(server_addr);
165         PUSH(server_proto);
166         PUSH(query_string);
167         PUSH(remote_addr);
168         lua_pushinteger(L, ctx->remote_port);
169         lua_setfield(L, -2, "remote_port");
170         PUSH(content_type);
171         PUSH(content_length);
172         PUSH(http_accept);
173 #undef PUSH
174
175         if (!strncmp(ctx->uri, p->prefix, strlen(p->prefix)))
176                 pushvar("uri", ctx->uri + strlen(p->prefix));
177         else
178                 pushvar("uri", ctx->uri);
179
180
181         /* make sure the global 'luci' table is prepared */
182         lua_getglobal(L, "luci-plugin");
183         if (!lua_istable(L, -1))
184                 return 0;
185
186         lua_getfield(L, -1, "init_req");
187         if (!lua_isfunction(L, -1)) {
188                 /* ignore error */
189                 lua_pop(L, 1);
190         } else {
191                 lua_pushvalue(L, -3);
192                 luci_pcall(L, "init_req", 1);
193         }
194
195         /* storage space for cgi variables */
196         lua_newtable(L);
197         lua_pushvalue(L, -1); /* copy for setfield */
198         lua_setfield(L, -3, "vars");
199
200         lua_pushvalue(L, -3); /* the http context table */
201
202         /* 
203          * make luci_parse_header a closure
204          * argument 1: the luci.vars table
205          * argument 2: the http context table
206          */
207         lua_pushcclosure(L, luci_parse_header, 2);
208         ret = luci_pcall(L, "parse_header", 0);
209
210         lua_getfield(L, -1, "handle_req");
211         ret = lua_isfunction(L, -1);
212         if (!ret)
213                 goto done;
214
215         lua_pushvalue(L, -3);
216         ret = luci_pcall(L, "handle_req", 1);
217
218         /* pop the luci and http context tables */
219 done:
220         lua_pop(L, 2);
221         return ret;
222 }
223
224 static void luci_unload(struct httpd_plugin *p)
225 {
226         lua_close(L);
227 }
228
229 HTTPD_PLUGIN {
230         .prefix = "/luci/",
231         .init = luci_init,
232         .done = luci_unload,
233         .prepare_req = luci_prepare_req,
234         .handle_req = luci_handle_req,
235 };