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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt