• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uhttpd/ucode.c

  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 <ucode/compiler.h>
 22 #include <ucode/lib.h>
 23 #include <ucode/vm.h>
 24 #include <stdio.h>
 25 #include <poll.h>
 26 
 27 #include "uhttpd.h"
 28 #include "plugin.h"
 29 
 30 #define UH_UCODE_CB     "handle_request"
 31 
 32 static const struct uhttpd_ops *ops;
 33 static struct config *_conf;
 34 #define conf (*_conf)
 35 
 36 static struct ucode_prefix *current_prefix;
 37 
 38 static uc_value_t *
 39 uh_ucode_recv(uc_vm_t *vm, size_t nargs)
 40 {
 41         static struct pollfd pfd = { .fd = STDIN_FILENO, .events = POLLIN };
 42         int data_len = 0, len = BUFSIZ, rlen, r;
 43         uc_value_t *v = uc_fn_arg(0);
 44         uc_stringbuf_t *buf;
 45 
 46         if (ucv_type(v) == UC_INTEGER) {
 47                 len = ucv_int64_get(v);
 48         }
 49         else if (v != NULL) {
 50                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Argument not an integer");
 51 
 52                 return NULL;
 53         }
 54 
 55         buf = ucv_stringbuf_new();
 56 
 57         while (len > 0) {
 58                 rlen = (len < BUFSIZ) ? len : BUFSIZ;
 59 
 60                 if (printbuf_memset(buf, -1, 0, rlen)) {
 61                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Out of memory");
 62                         printbuf_free(buf);
 63 
 64                         return NULL;
 65                 }
 66 
 67                 buf->bpos -= rlen;
 68                 r = read(STDIN_FILENO, buf->buf + buf->bpos, rlen);
 69 
 70                 if (r < 0) {
 71                         if (errno == EWOULDBLOCK || errno == EAGAIN) {
 72                                 pfd.revents = 0;
 73                                 poll(&pfd, 1, 1000);
 74 
 75                                 if (pfd.revents & POLLIN)
 76                                         continue;
 77                         }
 78 
 79                         if (errno == EINTR)
 80                                 continue;
 81 
 82                         if (!data_len)
 83                                 data_len = -1;
 84 
 85                         break;
 86                 }
 87 
 88                 buf->bpos += r;
 89                 data_len += r;
 90                 len -= r;
 91 
 92                 if (r != rlen)
 93                         break;
 94         }
 95 
 96         if (data_len > 0) {
 97                 /* add final guard \0 but do not count it */
 98                 if (printbuf_memset(buf, -1, 0, 1)) {
 99                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Out of memory");
100                         printbuf_free(buf);
101 
102                         return NULL;
103                 }
104 
105                 buf->bpos--;
106 
107                 return ucv_stringbuf_finish(buf);
108         }
109 
110         printbuf_free(buf);
111 
112         return NULL;
113 }
114 
115 static uc_value_t *
116 uh_ucode_send(uc_vm_t *vm, size_t nargs)
117 {
118         uc_value_t *val;
119         size_t arridx;
120         ssize_t len = 0;
121         char *p;
122 
123         for (arridx = 0; arridx < nargs; arridx++) {
124                 val = uc_fn_arg(arridx);
125 
126                 if (ucv_type(val) == UC_STRING) {
127                         len += write(STDOUT_FILENO, ucv_string_get(val), ucv_string_length(val));
128                 }
129                 else if (val != NULL) {
130                         p = ucv_to_string(vm, val);
131                         len += p ? write(STDOUT_FILENO, p, strlen(p)) : 0;
132                         free(p);
133                 }
134         }
135 
136         return ucv_int64_new(len);
137 }
138 
139 static uc_value_t *
140 uh_ucode_strconvert(uc_vm_t *vm, size_t nargs, int (*convert)(char *, int, const char *, int))
141 {
142         uc_value_t *val = uc_fn_arg(0);
143         static char out_buf[4096];
144         int out_len;
145         char *p;
146 
147         if (ucv_type(val) == UC_STRING) {
148                 out_len = convert(out_buf, sizeof(out_buf),
149                         ucv_string_get(val), ucv_string_length(val));
150         }
151         else if (val != NULL) {
152                 p = ucv_to_string(vm, val);
153                 out_len = p ? convert(out_buf, sizeof(out_buf), p, strlen(p)) : 0;
154                 free(p);
155         }
156         else {
157                 out_len = 0;
158         }
159 
160         if (out_len < 0) {
161                 const char *error;
162 
163                 if (out_len == -1)
164                         error = "buffer overflow";
165                 else
166                         error = "malformed string";
167 
168                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
169                         "%s on URL conversion\n", error);
170 
171                 return NULL;
172         }
173 
174         return ucv_string_new_length(out_buf, out_len);
175 }
176 
177 static uc_value_t *
178 uh_ucode_urldecode(uc_vm_t *vm, size_t nargs)
179 {
180         return uh_ucode_strconvert(vm, nargs, ops->urldecode);
181 }
182 
183 static uc_value_t *
184 uh_ucode_urlencode(uc_vm_t *vm, size_t nargs)
185 {
186         return uh_ucode_strconvert(vm, nargs, ops->urlencode);
187 }
188 
189 static uc_parse_config_t config = {
190         .strict_declarations = false,
191         .lstrip_blocks = true,
192         .trim_blocks = true
193 };
194 
195 static void
196 uh_ucode_exception(uc_vm_t *vm, uc_exception_t *ex)
197 {
198         uc_value_t *ctx;
199 
200         if (ex->type == EXCEPTION_EXIT)
201                 return;
202 
203         printf("Status: 500 Internal Server Error\r\n\r\n"
204                "Exception while executing ucode program %s:\n",
205                current_prefix->handler);
206 
207         switch (ex->type) {
208         case EXCEPTION_SYNTAX:    printf("Syntax error");    break;
209         case EXCEPTION_RUNTIME:   printf("Runtime error");   break;
210         case EXCEPTION_TYPE:      printf("Type error");      break;
211         case EXCEPTION_REFERENCE: printf("Reference error"); break;
212         default:                  printf("Error");
213         }
214 
215         printf(": %s\n", ex->message);
216 
217         ctx = ucv_object_get(ucv_array_get(ex->stacktrace, 0), "context", NULL);
218 
219         if (ctx)
220                 printf("%s\n", ucv_string_get(ctx));
221 }
222 
223 static void
224 uh_ucode_state_init(struct ucode_prefix *ucode)
225 {
226         char *syntax_error = NULL;
227         uc_vm_t *vm = &ucode->ctx;
228         uc_program_t *handler;
229         uc_vm_status_t status;
230         uc_source_t *src;
231         uc_value_t *v;
232         int exitcode;
233 
234         uc_vm_init(vm, &config);
235         uc_stdlib_load(uc_vm_scope_get(vm));
236 
237         /* build uhttpd api table */
238         v = ucv_object_new(vm);
239 
240         ucv_object_add(v, "send", ucv_cfunction_new("send", uh_ucode_send));
241         ucv_object_add(v, "sendc", ucv_get(ucv_object_get(v, "send", NULL)));
242         ucv_object_add(v, "recv", ucv_cfunction_new("recv", uh_ucode_recv));
243         ucv_object_add(v, "urldecode", ucv_cfunction_new("urldecode", uh_ucode_urldecode));
244         ucv_object_add(v, "urlencode", ucv_cfunction_new("urlencode", uh_ucode_urlencode));
245         ucv_object_add(v, "docroot", ucv_string_new(conf.docroot));
246 
247         ucv_object_add(uc_vm_scope_get(vm), "uhttpd", v);
248 
249         src = uc_source_new_file(ucode->handler);
250 
251         if (!src) {
252                 fprintf(stderr, "Error: Unable to open ucode handler: %s\n",
253                         strerror(errno));
254 
255                 exit(1);
256         }
257 
258         handler = uc_compile(&config, src, &syntax_error);
259 
260         uc_source_put(src);
261 
262         if (!handler) {
263                 fprintf(stderr, "Error: Unable to compile ucode handler: %s\n",
264                         syntax_error);
265 
266                 exit(1);
267         }
268 
269         free(syntax_error);
270 
271         vm->output = fopen("/dev/null", "w");
272 
273         if (!vm->output) {
274                 fprintf(stderr, "Error: Unable to open /dev/null for writing: %s\n",
275                         strerror(errno));
276 
277                 exit(1);
278         }
279 
280         status = uc_vm_execute(vm, handler, &v);
281         exitcode = (int)ucv_int64_get(v);
282 
283         uc_program_put(handler);
284         ucv_put(v);
285 
286         switch (status) {
287         case STATUS_OK:
288                 break;
289 
290         case STATUS_EXIT:
291                 fprintf(stderr, "Error: The ucode handler invoked exit(%d)\n", exitcode);
292                 exit(exitcode ? exitcode : 1);
293 
294         case ERROR_COMPILE:
295                 fprintf(stderr, "Error: Compilation error while executing ucode handler\n");
296                 exit(1);
297 
298         case ERROR_RUNTIME:
299                 fprintf(stderr, "Error: Runtime error while executing ucode handler\n");
300                 exit(2);
301         }
302 
303         v = ucv_object_get(uc_vm_scope_get(vm), UH_UCODE_CB, NULL);
304 
305         if (!ucv_is_callable(v)) {
306                 fprintf(stderr, "Error: The ucode handler declares no " UH_UCODE_CB "() callback.\n");
307                 exit(1);
308         }
309 
310         uc_vm_exception_handler_set(vm, uh_ucode_exception);
311 
312         ucv_gc(vm);
313 
314         fclose(vm->output);
315 
316         vm->output = stdout;
317 }
318 
319 static void
320 ucode_main(struct client *cl, struct path_info *pi, char *url)
321 {
322         uc_vm_t *vm = &current_prefix->ctx;
323         uc_value_t *req, *hdr, *res;
324         int path_len, prefix_len;
325         struct blob_attr *cur;
326         struct env_var *var;
327         char *str;
328         int rem;
329 
330         /* new env table for this request */
331         req = ucv_object_new(vm);
332 
333         prefix_len = strlen(pi->name);
334         path_len = strlen(url);
335         str = strchr(url, '?');
336 
337         if (str) {
338                 if (*(str + 1))
339                         pi->query = str + 1;
340 
341                 path_len = str - url;
342         }
343 
344         if (prefix_len > 0 && pi->name[prefix_len - 1] == '/')
345                 prefix_len--;
346 
347         if (path_len > prefix_len) {
348                 ucv_object_add(req, "PATH_INFO",
349                         ucv_string_new_length(url + prefix_len, path_len - prefix_len));
350         }
351 
352         for (var = ops->get_process_vars(cl, pi); var->name; var++) {
353                 if (!var->value)
354                         continue;
355 
356                 ucv_object_add(req, var->name, ucv_string_new(var->value));
357         }
358 
359         ucv_object_add(req, "HTTP_VERSION",
360                 ucv_double_new(0.9 + (cl->request.version / 10.0)));
361 
362         hdr = ucv_object_new(vm);
363 
364         blob_for_each_attr(cur, cl->hdr.head, rem)
365                 ucv_object_add(hdr, blobmsg_name(cur), ucv_string_new(blobmsg_data(cur)));
366 
367         ucv_object_add(req, "headers", hdr);
368 
369         res = uc_vm_invoke(vm, UH_UCODE_CB, 1, req);
370 
371         ucv_put(req);
372         ucv_put(res);
373 
374         exit(0);
375 }
376 
377 static void
378 ucode_handle_request(struct client *cl, char *url, struct path_info *pi)
379 {
380         struct ucode_prefix *p;
381         static struct path_info _pi;
382 
383         list_for_each_entry(p, &conf.ucode_prefix, list) {
384                 if (!ops->path_match(p->prefix, url))
385                         continue;
386 
387                 pi = &_pi;
388                 pi->name = p->prefix;
389                 pi->phys = p->handler;
390 
391                 current_prefix = p;
392 
393                 if (!ops->create_process(cl, pi, url, ucode_main)) {
394                         ops->client_error(cl, 500, "Internal Server Error",
395                                           "Failed to create CGI process: %s",
396                                           strerror(errno));
397                 }
398 
399                 return;
400         }
401 
402         ops->client_error(cl, 500, "Internal Server Error",
403                           "Failed to lookup matching handler");
404 }
405 
406 static bool
407 check_ucode_url(const char *url)
408 {
409         struct ucode_prefix *p;
410 
411         list_for_each_entry(p, &conf.ucode_prefix, list)
412                 if (ops->path_match(p->prefix, url))
413                         return true;
414 
415         return false;
416 }
417 
418 static struct dispatch_handler ucode_dispatch = {
419         .script = true,
420         .check_url = check_ucode_url,
421         .handle_request = ucode_handle_request,
422 };
423 
424 static int
425 ucode_plugin_init(const struct uhttpd_ops *o, struct config *c)
426 {
427         struct ucode_prefix *p;
428 
429         ops = o;
430         _conf = c;
431 
432         uc_search_path_init(&config.module_search_path);
433 
434         list_for_each_entry(p, &conf.ucode_prefix, list)
435                 uh_ucode_state_init(p);
436 
437         ops->dispatch_add(&ucode_dispatch);
438         return 0;
439 }
440 
441 struct uhttpd_plugin uhttpd_plugin = {
442         .init = ucode_plugin_init,
443 };
444 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt