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

Sources/ucode/lib/uloop.c

  1 /*
  2  * Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <errno.h>
 18 #include <string.h>
 19 #include <unistd.h>
 20 #include <limits.h>
 21 #include <fcntl.h>
 22 
 23 #include <libubox/uloop.h>
 24 
 25 #include "ucode/module.h"
 26 #include "ucode/platform.h"
 27 
 28 #define ok_return(expr) do { last_error = 0; return (expr); } while(0)
 29 #define err_return(err) do { last_error = err; return NULL; } while(0)
 30 
 31 static uc_resource_type_t *timer_type, *handle_type, *process_type, *task_type, *pipe_type;
 32 
 33 #ifdef HAVE_ULOOP_INTERVAL
 34 static uc_resource_type_t *interval_type;
 35 #endif
 36 
 37 #ifdef HAVE_ULOOP_SIGNAL
 38 static uc_resource_type_t *signal_type;
 39 #endif
 40 
 41 static uc_value_t *object_registry;
 42 
 43 static int last_error = 0;
 44 
 45 static size_t
 46 uc_uloop_reg_add(uc_value_t *obj, uc_value_t *cb)
 47 {
 48         size_t i = 0;
 49 
 50         while (ucv_array_get(object_registry, i))
 51                 i += 2;
 52 
 53         ucv_array_set(object_registry, i + 0, ucv_get(obj));
 54         ucv_array_set(object_registry, i + 1, ucv_get(cb));
 55 
 56         return i;
 57 }
 58 
 59 static bool
 60 uc_uloop_reg_remove(size_t i)
 61 {
 62         if (i + 1 >= ucv_array_length(object_registry))
 63                 return false;
 64 
 65         ucv_array_set(object_registry, i + 0, NULL);
 66         ucv_array_set(object_registry, i + 1, NULL);
 67 
 68         return true;
 69 }
 70 
 71 static bool
 72 uc_uloop_reg_invoke(uc_vm_t *vm, size_t i, uc_value_t *arg)
 73 {
 74         uc_value_t *obj = ucv_array_get(object_registry, i + 0);
 75         uc_value_t *cb = ucv_array_get(object_registry, i + 1);
 76 
 77         if (!ucv_is_callable(cb))
 78                 return false;
 79 
 80         uc_vm_stack_push(vm, ucv_get(obj));
 81         uc_vm_stack_push(vm, ucv_get(cb));
 82         uc_vm_stack_push(vm, ucv_get(arg));
 83 
 84         if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
 85                 uloop_end();
 86 
 87                 return false;
 88         }
 89 
 90         ucv_put(uc_vm_stack_pop(vm));
 91 
 92         return true;
 93 }
 94 
 95 static uc_value_t *
 96 uc_uloop_error(uc_vm_t *vm, size_t nargs)
 97 {
 98         uc_value_t *errmsg;
 99 
100         if (last_error == 0)
101                 return NULL;
102 
103         errmsg = ucv_string_new(strerror(last_error));
104         last_error = 0;
105 
106         return errmsg;
107 }
108 
109 static uc_value_t *
110 uc_uloop_init(uc_vm_t *vm, size_t nargs)
111 {
112         int rv = uloop_init();
113 
114         if (rv == -1)
115                 err_return(errno);
116 
117         ok_return(ucv_boolean_new(true));
118 }
119 
120 static uc_value_t *
121 uc_uloop_run(uc_vm_t *vm, size_t nargs)
122 {
123         uc_value_t *timeout = uc_fn_arg(0);
124         int t, rv;
125 
126         errno = 0;
127         t = timeout ? (int)ucv_int64_get(timeout) : -1;
128 
129         if (errno)
130                 err_return(errno);
131 
132         rv = uloop_run_timeout(t);
133 
134         ok_return(ucv_int64_new(rv));
135 }
136 
137 static uc_value_t *
138 uc_uloop_cancelling(uc_vm_t *vm, size_t nargs)
139 {
140         ok_return(ucv_boolean_new(uloop_cancelling()));
141 }
142 
143 static uc_value_t *
144 uc_uloop_running(uc_vm_t *vm, size_t nargs)
145 {
146         bool prev = uloop_cancelled;
147         bool active;
148 
149         uloop_cancelled = true;
150         active = uloop_cancelling();
151         uloop_cancelled = prev;
152 
153         ok_return(ucv_boolean_new(active));
154 }
155 
156 static uc_value_t *
157 uc_uloop_end(uc_vm_t *vm, size_t nargs)
158 {
159         uloop_end();
160 
161         ok_return(NULL);
162 }
163 
164 static uc_value_t *
165 uc_uloop_done(uc_vm_t *vm, size_t nargs)
166 {
167         uloop_done();
168 
169         ok_return(NULL);
170 }
171 
172 
173 typedef struct {
174         struct uloop_timeout timeout;
175         size_t registry_index;
176         uc_vm_t *vm;
177 } uc_uloop_timer_t;
178 
179 static void
180 uc_uloop_timeout_clear(uc_uloop_timer_t **timer)
181 {
182         /* drop registry entries and clear data to prevent reuse */
183         uc_uloop_reg_remove((*timer)->registry_index);
184         free(*timer);
185         *timer = NULL;
186 }
187 
188 static uc_value_t *
189 uc_uloop_timer_set(uc_vm_t *vm, size_t nargs)
190 {
191         uc_uloop_timer_t **timer = uc_fn_this("uloop.timer");
192         uc_value_t *timeout = uc_fn_arg(0);
193         int t, rv;
194 
195         if (!timer || !*timer)
196                 err_return(EINVAL);
197 
198         errno = 0;
199         t = timeout ? (int)ucv_int64_get(timeout) : -1;
200 
201         if (errno)
202                 err_return(errno);
203 
204         rv = uloop_timeout_set(&(*timer)->timeout, t);
205 
206         ok_return(ucv_boolean_new(rv == 0));
207 }
208 
209 static uc_value_t *
210 uc_uloop_timer_remaining(uc_vm_t *vm, size_t nargs)
211 {
212         uc_uloop_timer_t **timer = uc_fn_this("uloop.timer");
213         int64_t rem;
214 
215         if (!timer || !*timer)
216                 err_return(EINVAL);
217 
218 #ifdef HAVE_ULOOP_TIMEOUT_REMAINING64
219         rem = uloop_timeout_remaining64(&(*timer)->timeout);
220 #else
221         rem = (int64_t)uloop_timeout_remaining(&(*timer)->timeout);
222 #endif
223 
224         ok_return(ucv_int64_new(rem));
225 }
226 
227 static uc_value_t *
228 uc_uloop_timer_cancel(uc_vm_t *vm, size_t nargs)
229 {
230         uc_uloop_timer_t **timer = uc_fn_this("uloop.timer");
231         int rv;
232 
233         if (!timer || !*timer)
234                 err_return(EINVAL);
235 
236         rv = uloop_timeout_cancel(&(*timer)->timeout);
237 
238         uc_uloop_timeout_clear(timer);
239 
240         ok_return(ucv_boolean_new(rv == 0));
241 }
242 
243 static void
244 uc_uloop_timer_cb(struct uloop_timeout *timeout)
245 {
246         uc_uloop_timer_t *timer = (uc_uloop_timer_t *)timeout;
247 
248         uc_uloop_reg_invoke(timer->vm, timer->registry_index, NULL);
249 }
250 
251 static uc_value_t *
252 uc_uloop_timer(uc_vm_t *vm, size_t nargs)
253 {
254         uc_value_t *timeout = uc_fn_arg(0);
255         uc_value_t *callback = uc_fn_arg(1);
256         uc_uloop_timer_t *timer;
257         uc_value_t *res;
258         int t;
259 
260         errno = 0;
261         t = timeout ? ucv_int64_get(timeout) : -1;
262 
263         if (errno)
264                 err_return(errno);
265 
266         if (!ucv_is_callable(callback))
267                 err_return(EINVAL);
268 
269         timer = xalloc(sizeof(*timer));
270         timer->timeout.cb = uc_uloop_timer_cb;
271         timer->vm = vm;
272 
273         if (t >= 0)
274                 uloop_timeout_set(&timer->timeout, t);
275 
276         res = uc_resource_new(timer_type, timer);
277 
278         timer->registry_index = uc_uloop_reg_add(res, callback);
279 
280         ok_return(res);
281 }
282 
283 
284 typedef struct {
285         struct uloop_fd fd;
286         size_t registry_index;
287         uc_value_t *handle;
288         uc_vm_t *vm;
289 } uc_uloop_handle_t;
290 
291 static void
292 uc_uloop_handle_clear(uc_uloop_handle_t **handle)
293 {
294         /* drop registry entries and clear data to prevent reuse */
295         uc_uloop_reg_remove((*handle)->registry_index);
296         ucv_put((*handle)->handle);
297         free(*handle);
298         *handle = NULL;
299 }
300 
301 static uc_value_t *
302 uc_uloop_handle_fileno(uc_vm_t *vm, size_t nargs)
303 {
304         uc_uloop_handle_t **handle = uc_fn_this("uloop.handle");
305 
306         if (!handle || !*handle)
307                 err_return(EINVAL);
308 
309         ok_return(ucv_int64_new((*handle)->fd.fd));
310 }
311 
312 static uc_value_t *
313 uc_uloop_handle_handle(uc_vm_t *vm, size_t nargs)
314 {
315         uc_uloop_handle_t **handle = uc_fn_this("uloop.handle");
316 
317         if (!handle || !*handle)
318                 err_return(EINVAL);
319 
320         ok_return(ucv_get((*handle)->handle));
321 }
322 
323 static uc_value_t *
324 uc_uloop_handle_delete(uc_vm_t *vm, size_t nargs)
325 {
326         uc_uloop_handle_t **handle = uc_fn_this("uloop.handle");
327         int rv;
328 
329         if (!handle || !*handle)
330                 err_return(EINVAL);
331 
332         rv = uloop_fd_delete(&(*handle)->fd);
333 
334         uc_uloop_handle_clear(handle);
335 
336         if (rv != 0)
337                 err_return(errno);
338 
339         ok_return(ucv_boolean_new(true));
340 }
341 
342 static void
343 uc_uloop_handle_cb(struct uloop_fd *fd, unsigned int flags)
344 {
345         uc_uloop_handle_t *handle = (uc_uloop_handle_t *)fd;
346         uc_value_t *f = ucv_uint64_new(flags);
347 
348         uc_uloop_reg_invoke(handle->vm, handle->registry_index, f);
349         ucv_put(f);
350 }
351 
352 static int
353 get_fd(uc_vm_t *vm, uc_value_t *val)
354 {
355         uc_value_t *fn;
356         int64_t n;
357         int fd;
358 
359         fn = ucv_property_get(val, "fileno");
360 
361         if (ucv_is_callable(fn)) {
362                 uc_vm_stack_push(vm, ucv_get(val));
363                 uc_vm_stack_push(vm, ucv_get(fn));
364 
365                 if (uc_vm_call(vm, true, 0) == EXCEPTION_NONE)  {
366                         val = uc_vm_stack_pop(vm);
367                 }
368                 else {
369                         errno = EBADF;
370                         val = NULL;
371                 }
372         }
373         else {
374                 ucv_get(val);
375         }
376 
377         n = ucv_int64_get(val);
378 
379         if (errno) {
380                 fd = -1;
381         }
382         else if (n < 0 || n > (int64_t)INT_MAX) {
383                 errno = EBADF;
384                 fd = -1;
385         }
386         else {
387                 fd = (int)n;
388         }
389 
390         ucv_put(val);
391 
392         return fd;
393 }
394 
395 static uc_value_t *
396 uc_uloop_handle(uc_vm_t *vm, size_t nargs)
397 {
398         uc_value_t *fileno = uc_fn_arg(0);
399         uc_value_t *callback = uc_fn_arg(1);
400         uc_value_t *flags = uc_fn_arg(2);
401         uc_uloop_handle_t *handle;
402         uc_value_t *res;
403         int fd, ret;
404         uint64_t f;
405 
406         fd = get_fd(vm, fileno);
407 
408         if (fd == -1)
409                 err_return(errno);
410 
411         f = ucv_uint64_get(flags);
412 
413         if (errno)
414                 err_return(errno);
415 
416         if (f == 0 || f > (uint64_t)UINT_MAX)
417                 err_return(EINVAL);
418 
419         if (!ucv_is_callable(callback))
420                 err_return(EINVAL);
421 
422         handle = xalloc(sizeof(*handle));
423         handle->fd.fd = fd;
424         handle->fd.cb = uc_uloop_handle_cb;
425         handle->handle = ucv_get(fileno);
426         handle->vm = vm;
427 
428         ret = uloop_fd_add(&handle->fd, (unsigned int)f);
429 
430         if (ret != 0) {
431                 free(handle);
432                 err_return(errno);
433         }
434 
435         res = uc_resource_new(handle_type, handle);
436 
437         handle->registry_index = uc_uloop_reg_add(res, callback);
438 
439         ok_return(res);
440 }
441 
442 
443 typedef struct {
444         struct uloop_process process;
445         size_t registry_index;
446         uc_vm_t *vm;
447 } uc_uloop_process_t;
448 
449 static void
450 uc_uloop_process_clear(uc_uloop_process_t **process)
451 {
452         /* drop registry entries and clear data to prevent reuse */
453         uc_uloop_reg_remove((*process)->registry_index);
454         *process = NULL;
455 }
456 
457 static uc_value_t *
458 uc_uloop_process_pid(uc_vm_t *vm, size_t nargs)
459 {
460         uc_uloop_process_t **process = uc_fn_this("uloop.process");
461 
462         if (!process || !*process)
463                 err_return(EINVAL);
464 
465         ok_return(ucv_int64_new((*process)->process.pid));
466 }
467 
468 static uc_value_t *
469 uc_uloop_process_delete(uc_vm_t *vm, size_t nargs)
470 {
471         uc_uloop_process_t **process = uc_fn_this("uloop.process");
472         int rv;
473 
474         if (!process || !*process)
475                 err_return(EINVAL);
476 
477         rv = uloop_process_delete(&(*process)->process);
478 
479         uc_uloop_process_clear(process);
480 
481         if (rv != 0)
482                 err_return(EINVAL);
483 
484         ok_return(ucv_boolean_new(true));
485 }
486 
487 static void
488 uc_uloop_process_cb(struct uloop_process *proc, int exitcode)
489 {
490         uc_uloop_process_t *process = (uc_uloop_process_t *)proc;
491         uc_value_t *e = ucv_int64_new(exitcode >> 8);
492 
493         uc_uloop_reg_invoke(process->vm, process->registry_index, e);
494         uc_uloop_process_clear(&process);
495         ucv_put(e);
496 }
497 
498 static uc_value_t *
499 uc_uloop_process(uc_vm_t *vm, size_t nargs)
500 {
501         uc_value_t *executable = uc_fn_arg(0);
502         uc_value_t *arguments = uc_fn_arg(1);
503         uc_value_t *env_arg = uc_fn_arg(2);
504         uc_value_t *callback = uc_fn_arg(3);
505         uc_uloop_process_t *process;
506         uc_stringbuf_t *buf;
507         char **argp, **envp;
508         uc_value_t *res;
509         pid_t pid;
510         size_t i;
511 
512         if (ucv_type(executable) != UC_STRING ||
513             (arguments && ucv_type(arguments) != UC_ARRAY) ||
514             (env_arg && ucv_type(env_arg) != UC_OBJECT) ||
515             !ucv_is_callable(callback)) {
516                 err_return(EINVAL);
517         }
518 
519         pid = fork();
520 
521         if (pid == -1)
522                 err_return(errno);
523 
524         if (pid == 0) {
525                 argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *));
526                 envp = calloc(ucv_object_length(env_arg) + 1, sizeof(char *));
527 
528                 if (!argp || !envp)
529                         _exit(-1);
530 
531                 argp[0] = ucv_to_string(vm, executable);
532 
533                 for (i = 0; i < ucv_array_length(arguments); i++)
534                         argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i));
535 
536                 i = 0;
537 
538                 ucv_object_foreach(env_arg, envk, envv) {
539                         buf = xprintbuf_new();
540 
541                         ucv_stringbuf_printf(buf, "%s=", envk);
542                         ucv_to_stringbuf(vm, buf, envv, false);
543 
544                         envp[i++] = buf->buf;
545 
546                         free(buf);
547                 }
548 
549                 execvpe((const char *)ucv_string_get(executable),
550                         (char * const *)argp, (char * const *)envp);
551 
552                 _exit(-1);
553         }
554 
555         process = xalloc(sizeof(*process));
556         process->process.pid = pid;
557         process->process.cb = uc_uloop_process_cb;
558         process->vm = vm;
559 
560         uloop_process_add(&process->process);
561 
562         res = uc_resource_new(process_type, process);
563 
564         process->registry_index = uc_uloop_reg_add(res, callback);
565 
566         ok_return(res);
567 }
568 
569 
570 static bool
571 readall(int fd, void *buf, size_t len)
572 {
573         ssize_t rlen;
574 
575         while (len > 0) {
576                 rlen = read(fd, buf, len);
577 
578                 if (rlen == -1) {
579                         if (errno == EINTR)
580                                 continue;
581 
582                         return false;
583                 }
584 
585                 if (rlen == 0) {
586                         errno = EINTR;
587 
588                         return false;
589                 }
590 
591                 buf += rlen;
592                 len -= rlen;
593         }
594 
595         return true;
596 }
597 
598 static bool
599 writeall(int fd, void *buf, size_t len)
600 {
601         ssize_t wlen;
602 
603         while (len > 0) {
604                 wlen = write(fd, buf, len);
605 
606                 if (wlen == -1) {
607                         if (errno == EINTR)
608                                 continue;
609 
610                         return false;
611                 }
612 
613                 buf += wlen;
614                 len -= wlen;
615         }
616 
617         return true;
618 }
619 
620 typedef struct {
621         int input;
622         int output;
623         bool has_sender;
624         bool has_receiver;
625 } uc_uloop_pipe_t;
626 
627 static uc_value_t *
628 uc_uloop_pipe_send_common(uc_vm_t *vm, uc_value_t *msg, int fd)
629 {
630         uc_stringbuf_t *buf;
631         size_t len;
632         bool rv;
633 
634         buf = xprintbuf_new();
635 
636         printbuf_memset(buf, 0, 0, sizeof(len));
637         ucv_to_stringbuf(vm, buf, msg, true);
638 
639         len = printbuf_length(buf);
640         memcpy(buf->buf, &len, sizeof(len));
641 
642         rv = writeall(fd, buf->buf, len);
643 
644         printbuf_free(buf);
645 
646         if (!rv)
647                 err_return(errno);
648 
649         ok_return(ucv_boolean_new(true));
650 }
651 
652 static uc_value_t *
653 uc_uloop_pipe_send(uc_vm_t *vm, size_t nargs)
654 {
655         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
656         uc_value_t *msg = uc_fn_arg(0);
657 
658         if (!pipe || !*pipe)
659                 err_return(EINVAL);
660 
661         if (!(*pipe)->has_receiver)
662                 err_return(EPIPE);
663 
664         ok_return(uc_uloop_pipe_send_common(vm, msg, (*pipe)->output));
665 }
666 
667 static bool
668 uc_uloop_pipe_receive_common(uc_vm_t *vm, int fd, uc_value_t **res, bool skip)
669 {
670         enum json_tokener_error err = json_tokener_error_parse_eof;
671         json_tokener *tok = NULL;
672         json_object *jso = NULL;
673         char buf[1024];
674         ssize_t rlen;
675         size_t len;
676 
677         *res = NULL;
678 
679         if (!readall(fd, &len, sizeof(len)))
680                 err_return(errno);
681 
682         /* message length 0 is special, means input requested on other pipe */
683         if (len == 0)
684                 err_return(ENODATA);
685 
686         /* valid messages should be at least sizeof(len) plus one byte of payload */
687         if (len <= sizeof(len))
688                 err_return(EINVAL);
689 
690         len -= sizeof(len);
691 
692         while (len > 0) {
693                 rlen = read(fd, buf, len < sizeof(buf) ? len : sizeof(buf));
694 
695                 if (rlen == -1) {
696                         if (errno == EINTR)
697                                 continue;
698 
699                         goto read_fail;
700                 }
701 
702                 /* premature EOF */
703                 if (rlen == 0) {
704                         errno = EPIPE;
705                         goto read_fail;
706                 }
707 
708                 if (!skip) {
709                         if (!tok)
710                                 tok = xjs_new_tokener();
711 
712                         jso = json_tokener_parse_ex(tok, buf, rlen);
713                         err = json_tokener_get_error(tok);
714                 }
715 
716                 len -= rlen;
717         }
718 
719         if (!skip) {
720                 if (err == json_tokener_continue) {
721                         jso = json_tokener_parse_ex(tok, "\0", 1);
722                         err = json_tokener_get_error(tok);
723                 }
724 
725                 json_tokener_free(tok);
726 
727                 if (err != json_tokener_success) {
728                         errno = EINVAL;
729                         goto read_fail;
730                 }
731 
732                 *res = ucv_from_json(vm, jso);
733 
734                 json_object_put(jso);
735         }
736 
737         return true;
738 
739 read_fail:
740         if (tok)
741                 json_tokener_free(tok);
742 
743         json_object_put(jso);
744         err_return(errno);
745 }
746 
747 static uc_value_t *
748 uc_uloop_pipe_receive(uc_vm_t *vm, size_t nargs)
749 {
750         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
751         uc_value_t *rv;
752         size_t len = 0;
753 
754         if (!pipe || !*pipe)
755                 err_return(EINVAL);
756 
757         if (!(*pipe)->has_sender)
758                 err_return(EPIPE);
759 
760         /* send zero-length message to signal input request */
761         writeall((*pipe)->output, &len, sizeof(len));
762 
763         /* receive input message */
764         uc_uloop_pipe_receive_common(vm, (*pipe)->input, &rv, false);
765 
766         return rv;
767 }
768 
769 static uc_value_t *
770 uc_uloop_pipe_sending(uc_vm_t *vm, size_t nargs)
771 {
772         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
773 
774         if (!pipe || !*pipe)
775                 err_return(EINVAL);
776 
777         ok_return(ucv_boolean_new((*pipe)->has_sender));
778 }
779 
780 static uc_value_t *
781 uc_uloop_pipe_receiving(uc_vm_t *vm, size_t nargs)
782 {
783         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
784 
785         if (!pipe || !*pipe)
786                 err_return(EINVAL);
787 
788         ok_return(ucv_boolean_new((*pipe)->has_receiver));
789 }
790 
791 
792 typedef struct {
793         struct uloop_process process;
794         struct uloop_fd output;
795         size_t registry_index;
796         bool finished;
797         int input_fd;
798         uc_vm_t *vm;
799         uc_value_t *input_cb;
800         uc_value_t *output_cb;
801 } uc_uloop_task_t;
802 
803 static int
804 patch_devnull(int fd, bool write)
805 {
806         int devnull = open("/dev/null", write ? O_WRONLY : O_RDONLY);
807 
808         if (devnull != -1) {
809                 dup2(fd, devnull);
810                 close(fd);
811         }
812 
813         return devnull;
814 }
815 
816 static void
817 uloop_fd_close(struct uloop_fd *fd) {
818         if (fd->fd == -1)
819                 return;
820 
821         close(fd->fd);
822         fd->fd = -1;
823 }
824 
825 static void
826 uc_uloop_task_clear(uc_uloop_task_t **task)
827 {
828         /* drop registry entries and clear data to prevent reuse */
829         uc_uloop_reg_remove((*task)->registry_index);
830         *task = NULL;
831 }
832 
833 static uc_value_t *
834 uc_uloop_task_pid(uc_vm_t *vm, size_t nargs)
835 {
836         uc_uloop_task_t **task = uc_fn_this("uloop.task");
837 
838         if (!task || !*task)
839                 err_return(EINVAL);
840 
841         if ((*task)->finished)
842                 err_return(ESRCH);
843 
844         ok_return(ucv_int64_new((*task)->process.pid));
845 }
846 
847 static uc_value_t *
848 uc_uloop_task_kill(uc_vm_t *vm, size_t nargs)
849 {
850         uc_uloop_task_t **task = uc_fn_this("uloop.task");
851         int rv;
852 
853         if (!task || !*task)
854                 err_return(EINVAL);
855 
856         if ((*task)->finished)
857                 err_return(ESRCH);
858 
859         rv = kill((*task)->process.pid, SIGTERM);
860 
861         if (rv == -1)
862                 err_return(errno);
863 
864         ok_return(ucv_boolean_new(true));
865 }
866 
867 static uc_value_t *
868 uc_uloop_task_finished(uc_vm_t *vm, size_t nargs)
869 {
870         uc_uloop_task_t **task = uc_fn_this("uloop.task");
871 
872         if (!task || !*task)
873                 err_return(EINVAL);
874 
875         ok_return(ucv_boolean_new((*task)->finished));
876 }
877 
878 static void
879 uc_uloop_task_output_cb(struct uloop_fd *fd, unsigned int flags)
880 {
881         uc_uloop_task_t *task = container_of(fd, uc_uloop_task_t, output);
882         uc_value_t *obj = ucv_array_get(object_registry, task->registry_index);
883         uc_value_t *msg = NULL;
884 
885         if (flags & ULOOP_READ) {
886                 while (true) {
887                         if (!uc_uloop_pipe_receive_common(task->vm, fd->fd, &msg, !task->output_cb)) {
888                                 /* input requested */
889                                 if (last_error == ENODATA) {
890                                         uc_vm_stack_push(task->vm, ucv_get(obj));
891                                         uc_vm_stack_push(task->vm, ucv_get(task->input_cb));
892 
893                                         if (uc_vm_call(task->vm, true, 0) != EXCEPTION_NONE) {
894                                                 uloop_end();
895 
896                                                 return;
897                                         }
898 
899                                         msg = uc_vm_stack_pop(task->vm);
900                                         uc_uloop_pipe_send_common(task->vm, msg, task->input_fd);
901                                         ucv_put(msg);
902 
903                                         continue;
904                                 }
905 
906                                 /* error */
907                                 break;
908                         }
909 
910                         if (task->output_cb) {
911                                 uc_vm_stack_push(task->vm, ucv_get(obj));
912                                 uc_vm_stack_push(task->vm, ucv_get(task->output_cb));
913                                 uc_vm_stack_push(task->vm, msg);
914 
915                                 if (uc_vm_call(task->vm, true, 1) == EXCEPTION_NONE) {
916                                         ucv_put(uc_vm_stack_pop(task->vm));
917                                 }
918                                 else {
919                                         uloop_end();
920 
921                                         return;
922                                 }
923                         }
924                         else {
925                                 ucv_put(msg);
926                         }
927                 }
928         }
929 
930         if (!fd->registered && task->finished) {
931                 close(task->input_fd);
932                 task->input_fd = -1;
933 
934                 uloop_fd_close(&task->output);
935                 uloop_process_delete(&task->process);
936 
937                 uc_uloop_task_clear(&task);
938         }
939 }
940 
941 static void
942 uc_uloop_task_process_cb(struct uloop_process *proc, int exitcode)
943 {
944         uc_uloop_task_t *task = container_of(proc, uc_uloop_task_t, process);
945 
946         task->finished = true;
947 
948         uc_uloop_task_output_cb(&task->output, ULOOP_READ);
949 }
950 
951 static uc_value_t *
952 uc_uloop_task(uc_vm_t *vm, size_t nargs)
953 {
954         uc_value_t *func = uc_fn_arg(0);
955         uc_value_t *output_cb = uc_fn_arg(1);
956         uc_value_t *input_cb = uc_fn_arg(2);
957         int outpipe[2] = { -1, -1 };
958         int inpipe[2] = { -1, -1 };
959         uc_value_t *res, *cbs, *p;
960         uc_uloop_pipe_t *tpipe;
961         uc_uloop_task_t *task;
962         pid_t pid;
963         int err;
964 
965         if (!ucv_is_callable(func) ||
966             (output_cb && !ucv_is_callable(output_cb)) ||
967             (input_cb && !ucv_is_callable(input_cb)))
968             err_return(EINVAL);
969 
970         if (pipe(outpipe) == -1 || pipe(inpipe) == -1) {
971                 err = errno;
972 
973                 close(outpipe[0]); close(outpipe[1]);
974                 close(inpipe[0]); close(inpipe[1]);
975 
976                 err_return(err);
977         }
978 
979         pid = fork();
980 
981         if (pid == -1)
982                 err_return(errno);
983 
984         if (pid == 0) {
985                 uloop_done();
986 
987                 patch_devnull(0, false);
988                 patch_devnull(1, true);
989                 patch_devnull(2, true);
990 
991                 vm->output = fdopen(1, "w");
992 
993                 close(inpipe[1]);
994                 close(outpipe[0]);
995 
996                 tpipe = xalloc(sizeof(*tpipe));
997                 tpipe->input = inpipe[0];
998                 tpipe->output = outpipe[1];
999                 tpipe->has_sender = input_cb;
1000                 tpipe->has_receiver = output_cb;
1001 
1002                 p = uc_resource_new(pipe_type, tpipe);
1003 
1004                 uc_vm_stack_push(vm, func);
1005                 uc_vm_stack_push(vm, ucv_get(p));
1006 
1007                 if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) {
1008                         res = uc_vm_stack_pop(vm);
1009                         uc_uloop_pipe_send_common(vm, res, tpipe->output);
1010                         ucv_put(res);
1011                 }
1012 
1013                 ucv_put(p);
1014 
1015                 _exit(0);
1016         }
1017 
1018         close(inpipe[0]);
1019         close(outpipe[1]);
1020 
1021         task = xalloc(sizeof(*task));
1022         task->process.pid = pid;
1023         task->process.cb = uc_uloop_task_process_cb;
1024 
1025         task->vm = vm;
1026 
1027         task->output.fd = outpipe[0];
1028         task->output.cb = uc_uloop_task_output_cb;
1029         task->output_cb = output_cb;
1030         uloop_fd_add(&task->output, ULOOP_READ);
1031 
1032         if (input_cb) {
1033                 task->input_fd = inpipe[1];
1034                 task->input_cb = input_cb;
1035         }
1036         else {
1037                 task->input_fd = -1;
1038                 close(inpipe[1]);
1039         }
1040 
1041         uloop_process_add(&task->process);
1042 
1043         res = uc_resource_new(task_type, task);
1044 
1045         cbs = ucv_array_new(NULL);
1046         ucv_array_set(cbs, 0, ucv_get(output_cb));
1047         ucv_array_set(cbs, 1, ucv_get(input_cb));
1048 
1049         task->registry_index = uc_uloop_reg_add(res, cbs);
1050 
1051         ok_return(res);
1052 }
1053 
1054 
1055 #ifdef HAVE_ULOOP_INTERVAL
1056 typedef struct {
1057         struct uloop_interval interval;
1058         size_t registry_index;
1059         uc_vm_t *vm;
1060 } uc_uloop_interval_t;
1061 
1062 static void
1063 uc_uloop_interval_clear(uc_uloop_interval_t **interval)
1064 {
1065         /* drop registry entries and clear data to prevent reuse */
1066         uc_uloop_reg_remove((*interval)->registry_index);
1067         free(*interval);
1068         *interval = NULL;
1069 }
1070 
1071 static uc_value_t *
1072 uc_uloop_interval_set(uc_vm_t *vm, size_t nargs)
1073 {
1074         uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");
1075         uc_value_t *timeout = uc_fn_arg(0);
1076         int t, rv;
1077 
1078         if (!interval || !*interval)
1079                 err_return(EINVAL);
1080 
1081         errno = 0;
1082         t = timeout ? (int)ucv_int64_get(timeout) : -1;
1083 
1084         if (errno)
1085                 err_return(errno);
1086 
1087         rv = uloop_interval_set(&(*interval)->interval, t);
1088 
1089         ok_return(ucv_boolean_new(rv == 0));
1090 }
1091 
1092 static uc_value_t *
1093 uc_uloop_interval_remaining(uc_vm_t *vm, size_t nargs)
1094 {
1095         uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");
1096 
1097         if (!interval || !*interval)
1098                 err_return(EINVAL);
1099 
1100         ok_return(ucv_int64_new(uloop_interval_remaining(&(*interval)->interval)));
1101 }
1102 
1103 static uc_value_t *
1104 uc_uloop_interval_expirations(uc_vm_t *vm, size_t nargs)
1105 {
1106         uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");
1107 
1108         if (!interval || !*interval)
1109                 err_return(EINVAL);
1110 
1111         ok_return(ucv_int64_new((*interval)->interval.expirations));
1112 }
1113 
1114 static uc_value_t *
1115 uc_uloop_interval_cancel(uc_vm_t *vm, size_t nargs)
1116 {
1117         uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");
1118         int rv;
1119 
1120         if (!interval || !*interval)
1121                 err_return(EINVAL);
1122 
1123         rv = uloop_interval_cancel(&(*interval)->interval);
1124 
1125         uc_uloop_interval_clear(interval);
1126 
1127         ok_return(ucv_boolean_new(rv == 0));
1128 }
1129 
1130 static void
1131 uc_uloop_interval_cb(struct uloop_interval *uintv)
1132 {
1133         uc_uloop_interval_t *interval = (uc_uloop_interval_t *)uintv;
1134 
1135         uc_uloop_reg_invoke(interval->vm, interval->registry_index, NULL);
1136 }
1137 
1138 static uc_value_t *
1139 uc_uloop_interval(uc_vm_t *vm, size_t nargs)
1140 {
1141         uc_value_t *timeout = uc_fn_arg(0);
1142         uc_value_t *callback = uc_fn_arg(1);
1143         uc_uloop_interval_t *interval;
1144         uc_value_t *res;
1145         int t;
1146 
1147         errno = 0;
1148         t = timeout ? ucv_int64_get(timeout) : -1;
1149 
1150         if (errno)
1151                 err_return(errno);
1152 
1153         if (!ucv_is_callable(callback))
1154                 err_return(EINVAL);
1155 
1156         interval = xalloc(sizeof(*interval));
1157         interval->interval.cb = uc_uloop_interval_cb;
1158         interval->vm = vm;
1159 
1160         if (t >= 0)
1161                 uloop_interval_set(&interval->interval, t);
1162 
1163         res = uc_resource_new(interval_type, interval);
1164 
1165         interval->registry_index = uc_uloop_reg_add(res, callback);
1166 
1167         ok_return(res);
1168 }
1169 #endif
1170 
1171 #ifdef HAVE_ULOOP_SIGNAL
1172 typedef struct {
1173         struct uloop_signal signal;
1174         size_t registry_index;
1175         uc_vm_t *vm;
1176 } uc_uloop_signal_t;
1177 
1178 static void
1179 uc_uloop_signal_clear(uc_uloop_signal_t **signal)
1180 {
1181         /* drop registry entries and clear data to prevent reuse */
1182         uc_uloop_reg_remove((*signal)->registry_index);
1183         free(*signal);
1184         *signal = NULL;
1185 }
1186 
1187 static uc_value_t *
1188 uc_uloop_signal_signo(uc_vm_t *vm, size_t nargs)
1189 {
1190         uc_uloop_signal_t **signal = uc_fn_this("uloop.signal");
1191 
1192         if (!signal || !*signal)
1193                 err_return(EINVAL);
1194 
1195         ok_return(ucv_int64_new((*signal)->signal.signo));
1196 }
1197 
1198 static uc_value_t *
1199 uc_uloop_signal_delete(uc_vm_t *vm, size_t nargs)
1200 {
1201         uc_uloop_signal_t **signal = uc_fn_this("uloop.signal");
1202         int rv;
1203 
1204         if (!signal || !*signal)
1205                 err_return(EINVAL);
1206 
1207         rv = uloop_signal_delete(&(*signal)->signal);
1208 
1209         uc_uloop_signal_clear(signal);
1210 
1211         if (rv != 0)
1212                 err_return(EINVAL);
1213 
1214         ok_return(ucv_boolean_new(true));
1215 }
1216 
1217 static void
1218 uc_uloop_signal_cb(struct uloop_signal *usig)
1219 {
1220         uc_uloop_signal_t *signal = (uc_uloop_signal_t *)usig;
1221 
1222         uc_uloop_reg_invoke(signal->vm, signal->registry_index, NULL);
1223 }
1224 
1225 static int
1226 parse_signo(uc_value_t *sigspec)
1227 {
1228         if (ucv_type(sigspec) == UC_STRING) {
1229                 const char *signame = ucv_string_get(sigspec);
1230 
1231                 if (!strncasecmp(signame, "SIG", 3))
1232                         signame += 3;
1233 
1234                 for (size_t i = 0; i < UC_SYSTEM_SIGNAL_COUNT; i++) {
1235                         if (!uc_system_signal_names[i])
1236                                 continue;
1237 
1238                         if (strcasecmp(uc_system_signal_names[i], signame))
1239                                 continue;
1240 
1241                         return i;
1242                 }
1243         }
1244 
1245         uc_value_t *signum = ucv_to_number(sigspec);
1246         int64_t signo = ucv_int64_get(signum);
1247         ucv_put(signum);
1248 
1249         if (signo < 1 || signo >= UC_SYSTEM_SIGNAL_COUNT)
1250                 return -1;
1251 
1252         return signo;
1253 }
1254 
1255 static uc_value_t *
1256 uc_uloop_signal(uc_vm_t *vm, size_t nargs)
1257 {
1258         int signo = parse_signo(uc_fn_arg(0));
1259         uc_value_t *callback = uc_fn_arg(1);
1260         uc_uloop_signal_t *signal;
1261         uc_value_t *res;
1262 
1263         if (signo == -1 || !ucv_is_callable(callback))
1264                 err_return(EINVAL);
1265 
1266         signal = xalloc(sizeof(*signal));
1267         signal->signal.signo = signo;
1268         signal->signal.cb = uc_uloop_signal_cb;
1269         signal->vm = vm;
1270 
1271         uloop_signal_add(&signal->signal);
1272 
1273         res = uc_resource_new(signal_type, signal);
1274 
1275         signal->registry_index = uc_uloop_reg_add(res, callback);
1276 
1277         ok_return(res);
1278 }
1279 #endif
1280 
1281 
1282 static const uc_function_list_t timer_fns[] = {
1283         { "set",                uc_uloop_timer_set },
1284         { "remaining",  uc_uloop_timer_remaining },
1285         { "cancel",             uc_uloop_timer_cancel },
1286 };
1287 
1288 static const uc_function_list_t handle_fns[] = {
1289         { "fileno",             uc_uloop_handle_fileno },
1290         { "handle",             uc_uloop_handle_handle },
1291         { "delete",             uc_uloop_handle_delete },
1292 };
1293 
1294 static const uc_function_list_t process_fns[] = {
1295         { "pid",                uc_uloop_process_pid },
1296         { "delete",             uc_uloop_process_delete },
1297 };
1298 
1299 static const uc_function_list_t task_fns[] = {
1300         { "pid",                uc_uloop_task_pid },
1301         { "kill",               uc_uloop_task_kill },
1302         { "finished",   uc_uloop_task_finished },
1303 };
1304 
1305 static const uc_function_list_t pipe_fns[] = {
1306         { "send",               uc_uloop_pipe_send },
1307         { "receive",    uc_uloop_pipe_receive },
1308         { "sending",    uc_uloop_pipe_sending },
1309         { "receiving",  uc_uloop_pipe_receiving },
1310 };
1311 
1312 #ifdef HAVE_ULOOP_INTERVAL
1313 static const uc_function_list_t interval_fns[] = {
1314         { "set",                uc_uloop_interval_set },
1315         { "remaining",  uc_uloop_interval_remaining },
1316         { "expirations",
1317                                         uc_uloop_interval_expirations },
1318         { "cancel",             uc_uloop_interval_cancel },
1319 };
1320 #endif
1321 
1322 #ifdef HAVE_ULOOP_SIGNAL
1323 static const uc_function_list_t signal_fns[] = {
1324         { "signo",              uc_uloop_signal_signo },
1325         { "delete",             uc_uloop_signal_delete },
1326 };
1327 #endif
1328 
1329 static const uc_function_list_t global_fns[] = {
1330         { "error",              uc_uloop_error },
1331         { "init",               uc_uloop_init },
1332         { "run",                uc_uloop_run },
1333         { "timer",              uc_uloop_timer },
1334         { "handle",             uc_uloop_handle },
1335         { "process",    uc_uloop_process },
1336         { "task",               uc_uloop_task },
1337         { "cancelling", uc_uloop_cancelling },
1338         { "running",    uc_uloop_running },
1339         { "done",               uc_uloop_done },
1340         { "end",                uc_uloop_end },
1341 #ifdef HAVE_ULOOP_INTERVAL
1342         { "interval",   uc_uloop_interval },
1343 #endif
1344 #ifdef HAVE_ULOOP_SIGNAL
1345         { "signal",             uc_uloop_signal },
1346 #endif
1347 };
1348 
1349 
1350 static void close_timer(void *ud)
1351 {
1352         uc_uloop_timer_t *timer = ud;
1353 
1354         if (!timer)
1355                 return;
1356 
1357         uloop_timeout_cancel(&timer->timeout);
1358         free(timer);
1359 }
1360 
1361 static void close_handle(void *ud)
1362 {
1363         uc_uloop_handle_t *handle = ud;
1364 
1365         if (!handle)
1366                 return;
1367 
1368         uloop_fd_delete(&handle->fd);
1369         ucv_put(handle->handle);
1370         free(handle);
1371 }
1372 
1373 static void close_process(void *ud)
1374 {
1375         uc_uloop_process_t *process = ud;
1376 
1377         if (!process)
1378                 return;
1379 
1380         uloop_process_delete(&process->process);
1381         free(process);
1382 }
1383 
1384 static void close_task(void *ud)
1385 {
1386         uc_uloop_task_t *task = ud;
1387 
1388         if (!task)
1389                 return;
1390 
1391         uloop_process_delete(&task->process);
1392         uloop_fd_close(&task->output);
1393 
1394         if (task->input_fd != -1)
1395                 close(task->input_fd);
1396 
1397         free(task);
1398 }
1399 
1400 static void close_pipe(void *ud)
1401 {
1402         uc_uloop_pipe_t *pipe = ud;
1403 
1404         if (!pipe)
1405                 return;
1406 
1407         close(pipe->input);
1408         close(pipe->output);
1409 
1410         free(pipe);
1411 }
1412 
1413 #ifdef HAVE_ULOOP_INTERVAL
1414 static void close_interval(void *ud)
1415 {
1416         uc_uloop_interval_t *interval = ud;
1417 
1418         if (!interval)
1419                 return;
1420 
1421         uloop_interval_cancel(&interval->interval);
1422         free(interval);
1423 }
1424 #endif
1425 
1426 #ifdef HAVE_ULOOP_SIGNAL
1427 static void close_signal(void *ud)
1428 {
1429         uc_uloop_signal_t *signal = ud;
1430 
1431         if (!signal)
1432                 return;
1433 
1434         uloop_signal_delete(&signal->signal);
1435         free(signal);
1436 }
1437 #endif
1438 
1439 
1440 static struct {
1441         struct uloop_fd ufd;
1442         uc_vm_t *vm;
1443 } signal_handle;
1444 
1445 static void
1446 uc_uloop_vm_signal_cb(struct uloop_fd *ufd, unsigned int events)
1447 {
1448         if (uc_vm_signal_dispatch(signal_handle.vm) != EXCEPTION_NONE)
1449                 uloop_end();
1450 }
1451 
1452 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1453 {
1454         int signal_fd;
1455 
1456         uc_function_list_register(scope, global_fns);
1457 
1458 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
1459 
1460         ADD_CONST(ULOOP_READ);
1461         ADD_CONST(ULOOP_WRITE);
1462         ADD_CONST(ULOOP_EDGE_TRIGGER);
1463         ADD_CONST(ULOOP_BLOCKING);
1464 
1465         timer_type = uc_type_declare(vm, "uloop.timer", timer_fns, close_timer);
1466         handle_type = uc_type_declare(vm, "uloop.handle", handle_fns, close_handle);
1467         process_type = uc_type_declare(vm, "uloop.process", process_fns, close_process);
1468         task_type = uc_type_declare(vm, "uloop.task", task_fns, close_task);
1469         pipe_type = uc_type_declare(vm, "uloop.pipe", pipe_fns, close_pipe);
1470 
1471 #ifdef HAVE_ULOOP_INTERVAL
1472         interval_type = uc_type_declare(vm, "uloop.interval", interval_fns, close_interval);
1473 #endif
1474 
1475 #ifdef HAVE_ULOOP_SIGNAL
1476         signal_type = uc_type_declare(vm, "uloop.signal", signal_fns, close_signal);
1477 #endif
1478 
1479         object_registry = ucv_array_new(vm);
1480 
1481         uc_vm_registry_set(vm, "uloop.registry", object_registry);
1482 
1483         signal_fd = uc_vm_signal_notifyfd(vm);
1484 
1485         if (signal_fd != -1 && uloop_init() == 0) {
1486                 signal_handle.vm = vm;
1487                 signal_handle.ufd.cb = uc_uloop_vm_signal_cb;
1488                 signal_handle.ufd.fd = signal_fd;
1489 
1490                 uloop_fd_add(&signal_handle.ufd, ULOOP_READ);
1491         }
1492 }
1493 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt