• 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         printf("Status: 500 Internal Server Error\r\n\r\n"
199        "Exception while executing ucode program %s:\n",
200        current_prefix->handler);
201 
202         switch (ex->type) {
203         case EXCEPTION_SYNTAX:    printf("Syntax error");    break;
204         case EXCEPTION_RUNTIME:   printf("Runtime error");   break;
205         case EXCEPTION_TYPE:      printf("Type error");      break;
206         case EXCEPTION_REFERENCE: printf("Reference error"); break;
207         default:                  printf("Error");
208         }
209 
210         printf(": %s\n", ex->message);
211 
212         ctx = ucv_object_get(ucv_array_get(ex->stacktrace, 0), "context", NULL);
213 
214         if (ctx)
215                 printf("%s\n", ucv_string_get(ctx));
216 }
217 
218 static void
219 uh_ucode_state_init(struct ucode_prefix *ucode)
220 {
221         char *syntax_error = NULL;
222         uc_vm_t *vm = &ucode->ctx;
223         uc_program_t *handler;
224         uc_vm_status_t status;
225         uc_source_t *src;
226         uc_value_t *v;
227         int exitcode;
228 
229         uc_vm_init(vm, &config);
230         uc_stdlib_load(uc_vm_scope_get(vm));
231 
232         /* build uhttpd api table */
233         v = ucv_object_new(vm);
234 
235         ucv_object_add(v, "send", ucv_cfunction_new("send", uh_ucode_send));
236         ucv_object_add(v, "sendc", ucv_get(ucv_object_get(v, "send", NULL)));
237         ucv_object_add(v, "recv", ucv_cfunction_new("recv", uh_ucode_recv));
238         ucv_object_add(v, "urldecode", ucv_cfunction_new("urldecode", uh_ucode_urldecode));
239         ucv_object_add(v, "urlencode", ucv_cfunction_new("urlencode", uh_ucode_urlencode));
240         ucv_object_add(v, "docroot", ucv_string_new(conf.docroot));
241 
242         ucv_object_add(uc_vm_scope_get(vm), "uhttpd", v);
243 
244         src = uc_source_new_file(ucode->handler);
245 
246         if (!src) {
247                 fprintf(stderr, "Error: Unable to open ucode handler: %s\n",
248                         strerror(errno));
249 
250                 exit(1);
251         }
252 
253         handler = uc_compile(&config, src, &syntax_error);
254 
255         uc_source_put(src);
256 
257         if (!handler) {
258                 fprintf(stderr, "Error: Unable to compile ucode handler: %s\n",
259                         syntax_error);
260 
261                 exit(1);
262         }
263 
264         free(syntax_error);
265 
266         vm->output = fopen("/dev/null", "w");
267 
268         if (!vm->output) {
269                 fprintf(stderr, "Error: Unable to open /dev/null for writing: %s\n",
270                         strerror(errno));
271 
272                 exit(1);
273         }
274 
275         status = uc_vm_execute(vm, handler, &v);
276         exitcode = (int)ucv_int64_get(v);
277 
278         uc_program_put(handler);
279         ucv_put(v);
280 
281         switch (status) {
282         case STATUS_OK:
283                 break;
284 
285         case STATUS_EXIT:
286                 fprintf(stderr, "Error: The ucode handler invoked exit(%d)\n", exitcode);
287                 exit(exitcode ? exitcode : 1);
288 
289         case ERROR_COMPILE:
290                 fprintf(stderr, "Error: Compilation error while executing ucode handler\n");
291                 exit(1);
292 
293         case ERROR_RUNTIME:
294                 fprintf(stderr, "Error: Runtime error while executing ucode handler\n");
295                 exit(2);
296         }
297 
298         v = ucv_object_get(uc_vm_scope_get(vm), UH_UCODE_CB, NULL);
299 
300         if (!ucv_is_callable(v)) {
301                 fprintf(stderr, "Error: The ucode handler declares no " UH_UCODE_CB "() callback.\n");
302                 exit(1);
303         }
304 
305         uc_vm_exception_handler_set(vm, uh_ucode_exception);
306 
307         ucv_gc(vm);
308 
309         fclose(vm->output);
310 
311         vm->output = stdout;
312 }
313 
314 static void
315 ucode_main(struct client *cl, struct path_info *pi, char *url)
316 {
317         uc_vm_t *vm = &current_prefix->ctx;
318         uc_value_t *req, *hdr, *res;
319         int path_len, prefix_len;
320         struct blob_attr *cur;
321         struct env_var *var;
322         char *str;
323         int rem;
324 
325         /* new env table for this request */
326         req = ucv_object_new(vm);
327 
328         prefix_len = strlen(pi->name);
329         path_len = strlen(url);
330         str = strchr(url, '?');
331 
332         if (str) {
333                 if (*(str + 1))
334                         pi->query = str + 1;
335 
336                 path_len = str - url;
337         }
338 
339         if (prefix_len > 0 && pi->name[prefix_len - 1] == '/')
340                 prefix_len--;
341 
342         if (path_len > prefix_len) {
343                 ucv_object_add(req, "PATH_INFO",
344                         ucv_string_new_length(url + prefix_len, path_len - prefix_len));
345         }
346 
347         for (var = ops->get_process_vars(cl, pi); var->name; var++) {
348                 if (!var->value)
349                         continue;
350 
351                 ucv_object_add(req, var->name, ucv_string_new(var->value));
352         }
353 
354         ucv_object_add(req, "HTTP_VERSION",
355                 ucv_double_new(0.9 + (cl->request.version / 10.0)));
356 
357         hdr = ucv_object_new(vm);
358 
359         blob_for_each_attr(cur, cl->hdr.head, rem)
360                 ucv_object_add(hdr, blobmsg_name(cur), ucv_string_new(blobmsg_data(cur)));
361 
362         ucv_object_add(req, "headers", hdr);
363 
364         res = uc_vm_invoke(vm, UH_UCODE_CB, 1, req);
365 
366         ucv_put(req);
367         ucv_put(res);
368 
369         exit(0);
370 }
371 
372 static void
373 ucode_handle_request(struct client *cl, char *url, struct path_info *pi)
374 {
375         struct ucode_prefix *p;
376         static struct path_info _pi;
377 
378         list_for_each_entry(p, &conf.ucode_prefix, list) {
379                 if (!ops->path_match(p->prefix, url))
380                         continue;
381 
382                 pi = &_pi;
383                 pi->name = p->prefix;
384                 pi->phys = p->handler;
385 
386                 current_prefix = p;
387 
388                 if (!ops->create_process(cl, pi, url, ucode_main)) {
389                         ops->client_error(cl, 500, "Internal Server Error",
390                                           "Failed to create CGI process: %s",
391                                           strerror(errno));
392                 }
393 
394                 return;
395         }
396 
397         ops->client_error(cl, 500, "Internal Server Error",
398                           "Failed to lookup matching handler");
399 }
400 
401 static bool
402 check_ucode_url(const char *url)
403 {
404         struct ucode_prefix *p;
405 
406         list_for_each_entry(p, &conf.ucode_prefix, list)
407                 if (ops->path_match(p->prefix, url))
408                         return true;
409 
410         return false;
411 }
412 
413 static struct dispatch_handler ucode_dispatch = {
414         .script = true,
415         .check_url = check_ucode_url,
416         .handle_request = ucode_handle_request,
417 };
418 
419 static int
420 ucode_plugin_init(const struct uhttpd_ops *o, struct config *c)
421 {
422         struct ucode_prefix *p;
423 
424         ops = o;
425         _conf = c;
426 
427         list_for_each_entry(p, &conf.ucode_prefix, list)
428                 uh_ucode_state_init(p);
429 
430         ops->dispatch_add(&ucode_dispatch);
431         return 0;
432 }
433 
434 struct uhttpd_plugin uhttpd_plugin = {
435         .init = ucode_plugin_init,
436 };
437 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt