• 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 
 27 #define err_return(err) do { last_error = err; return NULL; } while(0)
 28 
 29 static uc_resource_type_t *timer_type, *handle_type, *process_type, *task_type, *pipe_type;
 30 static uc_value_t *object_registry;
 31 
 32 static int last_error = 0;
 33 
 34 static size_t
 35 uc_uloop_reg_add(uc_value_t *obj, uc_value_t *cb)
 36 {
 37         size_t i = 0;
 38 
 39         while (ucv_array_get(object_registry, i))
 40                 i += 2;
 41 
 42         ucv_array_set(object_registry, i + 0, ucv_get(obj));
 43         ucv_array_set(object_registry, i + 1, ucv_get(cb));
 44 
 45         return i;
 46 }
 47 
 48 static bool
 49 uc_uloop_reg_remove(size_t i)
 50 {
 51         if (i + 1 >= ucv_array_length(object_registry))
 52                 return false;
 53 
 54         ucv_array_set(object_registry, i + 0, NULL);
 55         ucv_array_set(object_registry, i + 1, NULL);
 56 
 57         return true;
 58 }
 59 
 60 static bool
 61 uc_uloop_reg_invoke(uc_vm_t *vm, size_t i, uc_value_t *arg)
 62 {
 63         uc_value_t *obj = ucv_array_get(object_registry, i + 0);
 64         uc_value_t *cb = ucv_array_get(object_registry, i + 1);
 65 
 66         if (!ucv_is_callable(cb))
 67                 return false;
 68 
 69         uc_vm_stack_push(vm, ucv_get(obj));
 70         uc_vm_stack_push(vm, ucv_get(cb));
 71         uc_vm_stack_push(vm, ucv_get(arg));
 72 
 73         if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
 74                 uloop_end();
 75 
 76                 return false;
 77         }
 78 
 79         ucv_put(uc_vm_stack_pop(vm));
 80 
 81         return true;
 82 }
 83 
 84 static uc_value_t *
 85 uc_uloop_error(uc_vm_t *vm, size_t nargs)
 86 {
 87         uc_value_t *errmsg;
 88 
 89         if (last_error == 0)
 90                 return NULL;
 91 
 92         errmsg = ucv_string_new(strerror(last_error));
 93         last_error = 0;
 94 
 95         return errmsg;
 96 }
 97 
 98 static uc_value_t *
 99 uc_uloop_init(uc_vm_t *vm, size_t nargs)
100 {
101         int rv = uloop_init();
102 
103         if (rv == -1)
104                 err_return(errno);
105 
106         return ucv_boolean_new(true);
107 }
108 
109 static uc_value_t *
110 uc_uloop_run(uc_vm_t *vm, size_t nargs)
111 {
112         uc_value_t *timeout = uc_fn_arg(0);
113         int t, rv;
114 
115         errno = 0;
116         t = timeout ? (int)ucv_int64_get(timeout) : -1;
117 
118         if (errno)
119                 err_return(errno);
120 
121         rv = uloop_run_timeout(t);
122 
123         return ucv_int64_new(rv);
124 }
125 
126 static uc_value_t *
127 uc_uloop_cancelling(uc_vm_t *vm, size_t nargs)
128 {
129         return ucv_boolean_new(uloop_cancelling());
130 }
131 
132 static uc_value_t *
133 uc_uloop_running(uc_vm_t *vm, size_t nargs)
134 {
135         bool prev = uloop_cancelled;
136         bool active;
137 
138         uloop_cancelled = true;
139         active = uloop_cancelling();
140         uloop_cancelled = prev;
141 
142         return ucv_boolean_new(active);
143 }
144 
145 static uc_value_t *
146 uc_uloop_end(uc_vm_t *vm, size_t nargs)
147 {
148         uloop_end();
149 
150         return NULL;
151 }
152 
153 static uc_value_t *
154 uc_uloop_done(uc_vm_t *vm, size_t nargs)
155 {
156         uloop_done();
157 
158         return NULL;
159 }
160 
161 
162 typedef struct {
163         struct uloop_timeout timeout;
164         size_t registry_index;
165         uc_vm_t *vm;
166 } uc_uloop_timer_t;
167 
168 static void
169 uc_uloop_timeout_clear(uc_uloop_timer_t **timer)
170 {
171         /* drop registry entries and clear data to prevent reuse */
172         uc_uloop_reg_remove((*timer)->registry_index);
173         free(*timer);
174         *timer = NULL;
175 }
176 
177 static uc_value_t *
178 uc_uloop_timer_set(uc_vm_t *vm, size_t nargs)
179 {
180         uc_uloop_timer_t **timer = uc_fn_this("uloop.timer");
181         uc_value_t *timeout = uc_fn_arg(0);
182         int t, rv;
183 
184         if (!timer || !*timer)
185                 err_return(EINVAL);
186 
187         errno = 0;
188         t = timeout ? (int)ucv_int64_get(timeout) : -1;
189 
190         if (errno)
191                 err_return(errno);
192 
193         rv = uloop_timeout_set(&(*timer)->timeout, t);
194 
195         return ucv_boolean_new(rv == 0);
196 }
197 
198 static uc_value_t *
199 uc_uloop_timer_remaining(uc_vm_t *vm, size_t nargs)
200 {
201         uc_uloop_timer_t **timer = uc_fn_this("uloop.timer");
202         int64_t rem;
203 
204         if (!timer || !*timer)
205                 err_return(EINVAL);
206 
207 #ifdef HAVE_ULOOP_TIMEOUT_REMAINING64
208         rem = uloop_timeout_remaining64(&(*timer)->timeout);
209 #else
210         rem = (int64_t)uloop_timeout_remaining(&(*timer)->timeout);
211 #endif
212 
213         return ucv_int64_new(rem);
214 }
215 
216 static uc_value_t *
217 uc_uloop_timer_cancel(uc_vm_t *vm, size_t nargs)
218 {
219         uc_uloop_timer_t **timer = uc_fn_this("uloop.timer");
220         int rv;
221 
222         if (!timer || !*timer)
223                 err_return(EINVAL);
224 
225         rv = uloop_timeout_cancel(&(*timer)->timeout);
226 
227         uc_uloop_timeout_clear(timer);
228 
229         return ucv_boolean_new(rv == 0);
230 }
231 
232 static void
233 uc_uloop_timer_cb(struct uloop_timeout *timeout)
234 {
235         uc_uloop_timer_t *timer = (uc_uloop_timer_t *)timeout;
236 
237         uc_uloop_reg_invoke(timer->vm, timer->registry_index, NULL);
238 }
239 
240 static uc_value_t *
241 uc_uloop_timer(uc_vm_t *vm, size_t nargs)
242 {
243         uc_value_t *timeout = uc_fn_arg(0);
244         uc_value_t *callback = uc_fn_arg(1);
245         uc_uloop_timer_t *timer;
246         uc_value_t *res;
247         int t;
248 
249         errno = 0;
250         t = timeout ? ucv_int64_get(timeout) : -1;
251 
252         if (errno)
253                 err_return(errno);
254 
255         if (!ucv_is_callable(callback))
256                 err_return(EINVAL);
257 
258         timer = xalloc(sizeof(*timer));
259         timer->timeout.cb = uc_uloop_timer_cb;
260         timer->vm = vm;
261 
262         if (t >= 0)
263                 uloop_timeout_set(&timer->timeout, t);
264 
265         res = uc_resource_new(timer_type, timer);
266 
267         timer->registry_index = uc_uloop_reg_add(res, callback);
268 
269         return res;
270 }
271 
272 
273 typedef struct {
274         struct uloop_fd fd;
275         size_t registry_index;
276         uc_value_t *handle;
277         uc_vm_t *vm;
278 } uc_uloop_handle_t;
279 
280 static void
281 uc_uloop_handle_clear(uc_uloop_handle_t **handle)
282 {
283         /* drop registry entries and clear data to prevent reuse */
284         uc_uloop_reg_remove((*handle)->registry_index);
285         ucv_put((*handle)->handle);
286         free(*handle);
287         *handle = NULL;
288 }
289 
290 static uc_value_t *
291 uc_uloop_handle_fileno(uc_vm_t *vm, size_t nargs)
292 {
293         uc_uloop_handle_t **handle = uc_fn_this("uloop.handle");
294 
295         if (!handle || !*handle)
296                 err_return(EINVAL);
297 
298         return ucv_int64_new((*handle)->fd.fd);
299 }
300 
301 static uc_value_t *
302 uc_uloop_handle_handle(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         return ucv_get((*handle)->handle);
310 }
311 
312 static uc_value_t *
313 uc_uloop_handle_delete(uc_vm_t *vm, size_t nargs)
314 {
315         uc_uloop_handle_t **handle = uc_fn_this("uloop.handle");
316         int rv;
317 
318         if (!handle || !*handle)
319                 err_return(EINVAL);
320 
321         rv = uloop_fd_delete(&(*handle)->fd);
322 
323         uc_uloop_handle_clear(handle);
324 
325         if (rv != 0)
326                 err_return(errno);
327 
328         return ucv_boolean_new(true);
329 }
330 
331 static void
332 uc_uloop_handle_cb(struct uloop_fd *fd, unsigned int flags)
333 {
334         uc_uloop_handle_t *handle = (uc_uloop_handle_t *)fd;
335         uc_value_t *f = ucv_uint64_new(flags);
336 
337         uc_uloop_reg_invoke(handle->vm, handle->registry_index, f);
338         ucv_put(f);
339 }
340 
341 static int
342 get_fd(uc_vm_t *vm, uc_value_t *val)
343 {
344         uc_value_t *fn;
345         int64_t n;
346         int fd;
347 
348         fn = ucv_property_get(val, "fileno");
349 
350         if (ucv_is_callable(fn)) {
351                 uc_vm_stack_push(vm, ucv_get(val));
352                 uc_vm_stack_push(vm, ucv_get(fn));
353 
354                 if (uc_vm_call(vm, true, 0) == EXCEPTION_NONE)  {
355                         val = uc_vm_stack_pop(vm);
356                 }
357                 else {
358                         errno = EBADF;
359                         val = NULL;
360                 }
361         }
362         else {
363                 ucv_get(val);
364         }
365 
366         n = ucv_int64_get(val);
367 
368         if (errno) {
369                 fd = -1;
370         }
371         else if (n < 0 || n > (int64_t)INT_MAX) {
372                 errno = EBADF;
373                 fd = -1;
374         }
375         else {
376                 fd = (int)n;
377         }
378 
379         ucv_put(val);
380 
381         return fd;
382 }
383 
384 static uc_value_t *
385 uc_uloop_handle(uc_vm_t *vm, size_t nargs)
386 {
387         uc_value_t *fileno = uc_fn_arg(0);
388         uc_value_t *callback = uc_fn_arg(1);
389         uc_value_t *flags = uc_fn_arg(2);
390         uc_uloop_handle_t *handle;
391         uc_value_t *res;
392         int fd, ret;
393         uint64_t f;
394 
395         fd = get_fd(vm, fileno);
396 
397         if (fd == -1)
398                 err_return(errno);
399 
400         f = ucv_uint64_get(flags);
401 
402         if (errno)
403                 err_return(errno);
404 
405         if (f == 0 || f > (uint64_t)UINT_MAX)
406                 err_return(EINVAL);
407 
408         if (!ucv_is_callable(callback))
409                 err_return(EINVAL);
410 
411         handle = xalloc(sizeof(*handle));
412         handle->fd.fd = fd;
413         handle->fd.cb = uc_uloop_handle_cb;
414         handle->handle = ucv_get(fileno);
415         handle->vm = vm;
416 
417         ret = uloop_fd_add(&handle->fd, (unsigned int)f);
418 
419         if (ret != 0) {
420                 free(handle);
421                 err_return(errno);
422         }
423 
424         res = uc_resource_new(handle_type, handle);
425 
426         handle->registry_index = uc_uloop_reg_add(res, callback);
427 
428         return res;
429 }
430 
431 
432 typedef struct {
433         struct uloop_process process;
434         size_t registry_index;
435         uc_vm_t *vm;
436 } uc_uloop_process_t;
437 
438 static void
439 uc_uloop_process_clear(uc_uloop_process_t **process)
440 {
441         /* drop registry entries and clear data to prevent reuse */
442         uc_uloop_reg_remove((*process)->registry_index);
443         *process = NULL;
444 }
445 
446 static uc_value_t *
447 uc_uloop_process_pid(uc_vm_t *vm, size_t nargs)
448 {
449         uc_uloop_process_t **process = uc_fn_this("uloop.process");
450 
451         if (!process || !*process)
452                 err_return(EINVAL);
453 
454         return ucv_int64_new((*process)->process.pid);
455 }
456 
457 static uc_value_t *
458 uc_uloop_process_delete(uc_vm_t *vm, size_t nargs)
459 {
460         uc_uloop_process_t **process = uc_fn_this("uloop.process");
461         int rv;
462 
463         if (!process || !*process)
464                 err_return(EINVAL);
465 
466         rv = uloop_process_delete(&(*process)->process);
467 
468         uc_uloop_process_clear(process);
469 
470         if (rv != 0)
471                 err_return(EINVAL);
472 
473         return ucv_boolean_new(true);
474 }
475 
476 static void
477 uc_uloop_process_cb(struct uloop_process *proc, int exitcode)
478 {
479         uc_uloop_process_t *process = (uc_uloop_process_t *)proc;
480         uc_value_t *e = ucv_int64_new(exitcode >> 8);
481 
482         uc_uloop_reg_invoke(process->vm, process->registry_index, e);
483         uc_uloop_process_clear(&process);
484         ucv_put(e);
485 }
486 
487 static uc_value_t *
488 uc_uloop_process(uc_vm_t *vm, size_t nargs)
489 {
490         uc_value_t *executable = uc_fn_arg(0);
491         uc_value_t *arguments = uc_fn_arg(1);
492         uc_value_t *environ = uc_fn_arg(2);
493         uc_value_t *callback = uc_fn_arg(3);
494         uc_uloop_process_t *process;
495         uc_stringbuf_t *buf;
496         char **argp, **envp;
497         uc_value_t *res;
498         pid_t pid;
499         size_t i;
500 
501         if (ucv_type(executable) != UC_STRING ||
502             (arguments && ucv_type(arguments) != UC_ARRAY) ||
503             (environ && ucv_type(environ) != UC_OBJECT) ||
504             !ucv_is_callable(callback)) {
505                 err_return(EINVAL);
506         }
507 
508         pid = fork();
509 
510         if (pid == -1)
511                 err_return(errno);
512 
513         if (pid == 0) {
514                 argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *));
515                 envp = calloc(ucv_object_length(environ) + 1, sizeof(char *));
516 
517                 if (!argp || !envp)
518                         _exit(-1);
519 
520                 argp[0] = ucv_to_string(vm, executable);
521 
522                 for (i = 0; i < ucv_array_length(arguments); i++)
523                         argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i));
524 
525                 i = 0;
526 
527                 ucv_object_foreach(environ, envk, envv) {
528                         buf = xprintbuf_new();
529 
530                         ucv_stringbuf_printf(buf, "%s=", envk);
531                         ucv_to_stringbuf(vm, buf, envv, false);
532 
533                         envp[i++] = buf->buf;
534 
535                         free(buf);
536                 }
537 
538 #ifdef __APPLE__
539                 execve((const char *)ucv_string_get(executable),
540                        (char * const *)argp, (char * const *)envp);
541 #else
542                 execvpe((const char *)ucv_string_get(executable),
543                         (char * const *)argp, (char * const *)envp);
544 #endif
545 
546                 _exit(-1);
547         }
548 
549         process = xalloc(sizeof(*process));
550         process->process.pid = pid;
551         process->process.cb = uc_uloop_process_cb;
552         process->vm = vm;
553 
554         uloop_process_add(&process->process);
555 
556         res = uc_resource_new(process_type, process);
557 
558         process->registry_index = uc_uloop_reg_add(res, callback);
559 
560         return res;
561 }
562 
563 
564 static bool
565 readall(int fd, void *buf, size_t len)
566 {
567         ssize_t rlen;
568 
569         while (len > 0) {
570                 rlen = read(fd, buf, len);
571 
572                 if (rlen == -1) {
573                         if (errno == EINTR)
574                                 continue;
575 
576                         return false;
577                 }
578 
579                 if (rlen == 0) {
580                         errno = EINTR;
581 
582                         return false;
583                 }
584 
585                 buf += rlen;
586                 len -= rlen;
587         }
588 
589         return true;
590 }
591 
592 static bool
593 writeall(int fd, void *buf, size_t len)
594 {
595         ssize_t wlen;
596 
597         while (len > 0) {
598                 wlen = write(fd, buf, len);
599 
600                 if (wlen == -1) {
601                         if (errno == EINTR)
602                                 continue;
603 
604                         return false;
605                 }
606 
607                 buf += wlen;
608                 len -= wlen;
609         }
610 
611         return true;
612 }
613 
614 typedef struct {
615         int input;
616         int output;
617         bool has_sender;
618         bool has_receiver;
619 } uc_uloop_pipe_t;
620 
621 static uc_value_t *
622 uc_uloop_pipe_send_common(uc_vm_t *vm, uc_value_t *msg, int fd)
623 {
624         uc_stringbuf_t *buf;
625         size_t len;
626         bool rv;
627 
628         buf = xprintbuf_new();
629 
630         printbuf_memset(buf, 0, 0, sizeof(len));
631         ucv_to_stringbuf(vm, buf, msg, true);
632 
633         len = printbuf_length(buf);
634         memcpy(buf->buf, &len, sizeof(len));
635 
636         rv = writeall(fd, buf->buf, len);
637 
638         printbuf_free(buf);
639 
640         if (!rv)
641                 err_return(errno);
642 
643         return ucv_boolean_new(true);
644 }
645 
646 static uc_value_t *
647 uc_uloop_pipe_send(uc_vm_t *vm, size_t nargs)
648 {
649         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
650         uc_value_t *msg = uc_fn_arg(0);
651 
652         if (!pipe || !*pipe)
653                 err_return(EINVAL);
654 
655         if (!(*pipe)->has_receiver)
656                 err_return(EPIPE);
657 
658         return uc_uloop_pipe_send_common(vm, msg, (*pipe)->output);
659 }
660 
661 static bool
662 uc_uloop_pipe_receive_common(uc_vm_t *vm, int fd, uc_value_t **res, bool skip)
663 {
664         enum json_tokener_error err = json_tokener_error_parse_eof;
665         json_tokener *tok = NULL;
666         json_object *jso = NULL;
667         char buf[1024];
668         ssize_t rlen;
669         size_t len;
670 
671         *res = NULL;
672 
673         if (!readall(fd, &len, sizeof(len)))
674                 err_return(errno);
675 
676         /* message length 0 is special, means input requested on other pipe */
677         if (len == 0)
678                 err_return(ENODATA);
679 
680         /* valid messages should be at least sizeof(len) plus one byte of payload */
681         if (len <= sizeof(len))
682                 err_return(EINVAL);
683 
684         len -= sizeof(len);
685 
686         while (len > 0) {
687                 rlen = read(fd, buf, len < sizeof(buf) ? len : sizeof(buf));
688 
689                 if (rlen == -1) {
690                         if (errno == EINTR)
691                                 continue;
692 
693                         goto read_fail;
694                 }
695 
696                 /* premature EOF */
697                 if (rlen == 0) {
698                         errno = EPIPE;
699                         goto read_fail;
700                 }
701 
702                 if (!skip) {
703                         if (!tok)
704                                 tok = xjs_new_tokener();
705 
706                         jso = json_tokener_parse_ex(tok, buf, rlen);
707                         err = json_tokener_get_error(tok);
708                 }
709 
710                 len -= rlen;
711         }
712 
713         if (!skip) {
714                 if (err == json_tokener_continue) {
715                         jso = json_tokener_parse_ex(tok, "\0", 1);
716                         err = json_tokener_get_error(tok);
717                 }
718 
719                 json_tokener_free(tok);
720 
721                 if (err != json_tokener_success) {
722                         errno = EINVAL;
723                         goto read_fail;
724                 }
725 
726                 *res = ucv_from_json(vm, jso);
727 
728                 json_object_put(jso);
729         }
730 
731         return true;
732 
733 read_fail:
734         if (tok)
735                 json_tokener_free(tok);
736 
737         json_object_put(jso);
738         err_return(errno);
739 }
740 
741 static uc_value_t *
742 uc_uloop_pipe_receive(uc_vm_t *vm, size_t nargs)
743 {
744         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
745         uc_value_t *rv;
746         size_t len = 0;
747 
748         if (!pipe || !*pipe)
749                 err_return(EINVAL);
750 
751         if (!(*pipe)->has_sender)
752                 err_return(EPIPE);
753 
754         /* send zero-length message to signal input request */
755         writeall((*pipe)->output, &len, sizeof(len));
756 
757         /* receive input message */
758         uc_uloop_pipe_receive_common(vm, (*pipe)->input, &rv, false);
759 
760         return rv;
761 }
762 
763 static uc_value_t *
764 uc_uloop_pipe_sending(uc_vm_t *vm, size_t nargs)
765 {
766         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
767 
768         if (!pipe || !*pipe)
769                 err_return(EINVAL);
770 
771         return ucv_boolean_new((*pipe)->has_sender);
772 }
773 
774 static uc_value_t *
775 uc_uloop_pipe_receiving(uc_vm_t *vm, size_t nargs)
776 {
777         uc_uloop_pipe_t **pipe = uc_fn_this("uloop.pipe");
778 
779         if (!pipe || !*pipe)
780                 err_return(EINVAL);
781 
782         return ucv_boolean_new((*pipe)->has_receiver);
783 }
784 
785 
786 typedef struct {
787         struct uloop_process process;
788         struct uloop_fd output;
789         size_t registry_index;
790         bool finished;
791         int input_fd;
792         uc_vm_t *vm;
793         uc_value_t *input_cb;
794         uc_value_t *output_cb;
795 } uc_uloop_task_t;
796 
797 static int
798 patch_devnull(int fd, bool write)
799 {
800         int devnull = open("/dev/null", write ? O_WRONLY : O_RDONLY);
801 
802         if (devnull != -1) {
803                 dup2(fd, devnull);
804                 close(fd);
805         }
806 
807         return devnull;
808 }
809 
810 static void
811 uloop_fd_close(struct uloop_fd *fd) {
812         if (fd->fd == -1)
813                 return;
814 
815         close(fd->fd);
816         fd->fd = -1;
817 }
818 
819 static void
820 uc_uloop_task_clear(uc_uloop_task_t **task)
821 {
822         /* drop registry entries and clear data to prevent reuse */
823         uc_uloop_reg_remove((*task)->registry_index);
824         *task = NULL;
825 }
826 
827 static uc_value_t *
828 uc_uloop_task_pid(uc_vm_t *vm, size_t nargs)
829 {
830         uc_uloop_task_t **task = uc_fn_this("uloop.task");
831 
832         if (!task || !*task)
833                 err_return(EINVAL);
834 
835         if ((*task)->finished)
836                 err_return(ESRCH);
837 
838         return ucv_int64_new((*task)->process.pid);
839 }
840 
841 static uc_value_t *
842 uc_uloop_task_kill(uc_vm_t *vm, size_t nargs)
843 {
844         uc_uloop_task_t **task = uc_fn_this("uloop.task");
845         int rv;
846 
847         if (!task || !*task)
848                 err_return(EINVAL);
849 
850         if ((*task)->finished)
851                 err_return(ESRCH);
852 
853         rv = kill((*task)->process.pid, SIGTERM);
854 
855         if (rv == -1)
856                 err_return(errno);
857 
858         return ucv_boolean_new(true);
859 }
860 
861 static uc_value_t *
862 uc_uloop_task_finished(uc_vm_t *vm, size_t nargs)
863 {
864         uc_uloop_task_t **task = uc_fn_this("uloop.task");
865 
866         if (!task || !*task)
867                 err_return(EINVAL);
868 
869         return ucv_boolean_new((*task)->finished);
870 }
871 
872 static void
873 uc_uloop_task_output_cb(struct uloop_fd *fd, unsigned int flags)
874 {
875         uc_uloop_task_t *task = container_of(fd, uc_uloop_task_t, output);
876         uc_value_t *obj = ucv_array_get(object_registry, task->registry_index);
877         uc_value_t *msg = NULL;
878 
879         if (flags & ULOOP_READ) {
880                 while (true) {
881                         if (!uc_uloop_pipe_receive_common(task->vm, fd->fd, &msg, !task->output_cb)) {
882                                 /* input requested */
883                                 if (last_error == ENODATA) {
884                                         uc_vm_stack_push(task->vm, ucv_get(obj));
885                                         uc_vm_stack_push(task->vm, ucv_get(task->input_cb));
886 
887                                         if (uc_vm_call(task->vm, true, 0) != EXCEPTION_NONE) {
888                                                 uloop_end();
889 
890                                                 return;
891                                         }
892 
893                                         msg = uc_vm_stack_pop(task->vm);
894                                         uc_uloop_pipe_send_common(task->vm, msg, task->input_fd);
895                                         ucv_put(msg);
896 
897                                         continue;
898                                 }
899 
900                                 /* error */
901                                 break;
902                         }
903 
904                         if (task->output_cb) {
905                                 uc_vm_stack_push(task->vm, ucv_get(obj));
906                                 uc_vm_stack_push(task->vm, ucv_get(task->output_cb));
907                                 uc_vm_stack_push(task->vm, msg);
908 
909                                 if (uc_vm_call(task->vm, true, 1) == EXCEPTION_NONE) {
910                                         ucv_put(uc_vm_stack_pop(task->vm));
911                                 }
912                                 else {
913                                         uloop_end();
914 
915                                         return;
916                                 }
917                         }
918                         else {
919                                 ucv_put(msg);
920                         }
921                 }
922         }
923 
924         if (!fd->registered && task->finished) {
925                 close(task->input_fd);
926                 task->input_fd = -1;
927 
928                 uloop_fd_close(&task->output);
929                 uloop_process_delete(&task->process);
930 
931                 uc_uloop_task_clear(&task);
932         }
933 }
934 
935 static void
936 uc_uloop_task_process_cb(struct uloop_process *proc, int exitcode)
937 {
938         uc_uloop_task_t *task = container_of(proc, uc_uloop_task_t, process);
939 
940         task->finished = true;
941 
942         uc_uloop_task_output_cb(&task->output, ULOOP_READ);
943 }
944 
945 static uc_value_t *
946 uc_uloop_task(uc_vm_t *vm, size_t nargs)
947 {
948         uc_value_t *func = uc_fn_arg(0);
949         uc_value_t *output_cb = uc_fn_arg(1);
950         uc_value_t *input_cb = uc_fn_arg(2);
951         int outpipe[2] = { -1, -1 };
952         int inpipe[2] = { -1, -1 };
953         uc_value_t *res, *cbs, *p;
954         uc_uloop_pipe_t *tpipe;
955         uc_uloop_task_t *task;
956         pid_t pid;
957         int err;
958 
959         if (!ucv_is_callable(func) ||
960             (output_cb && !ucv_is_callable(output_cb)) ||
961             (input_cb && !ucv_is_callable(input_cb)))
962             err_return(EINVAL);
963 
964         if (pipe(outpipe) == -1 || pipe(inpipe) == -1) {
965                 err = errno;
966 
967                 close(outpipe[0]); close(outpipe[1]);
968                 close(inpipe[0]); close(inpipe[1]);
969 
970                 err_return(err);
971         }
972 
973         pid = fork();
974 
975         if (pid == -1)
976                 err_return(errno);
977 
978         if (pid == 0) {
979                 uloop_done();
980 
981                 patch_devnull(0, false);
982                 patch_devnull(1, true);
983                 patch_devnull(2, true);
984 
985                 vm->output = fdopen(1, "w");
986 
987                 close(inpipe[1]);
988                 close(outpipe[0]);
989 
990                 tpipe = xalloc(sizeof(*tpipe));
991                 tpipe->input = inpipe[0];
992                 tpipe->output = outpipe[1];
993                 tpipe->has_sender = input_cb;
994                 tpipe->has_receiver = output_cb;
995 
996                 p = uc_resource_new(pipe_type, tpipe);
997 
998                 uc_vm_stack_push(vm, func);
999                 uc_vm_stack_push(vm, ucv_get(p));
1000 
1001                 if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) {
1002                         res = uc_vm_stack_pop(vm);
1003                         uc_uloop_pipe_send_common(vm, res, tpipe->output);
1004                         ucv_put(res);
1005                 }
1006 
1007                 ucv_put(p);
1008 
1009                 _exit(0);
1010         }
1011 
1012         close(inpipe[0]);
1013         close(outpipe[1]);
1014 
1015         task = xalloc(sizeof(*task));
1016         task->process.pid = pid;
1017         task->process.cb = uc_uloop_task_process_cb;
1018 
1019         task->vm = vm;
1020 
1021         task->output.fd = outpipe[0];
1022         task->output.cb = uc_uloop_task_output_cb;
1023         task->output_cb = output_cb;
1024         uloop_fd_add(&task->output, ULOOP_READ);
1025 
1026         if (input_cb) {
1027                 task->input_fd = inpipe[1];
1028                 task->input_cb = input_cb;
1029         }
1030         else {
1031                 task->input_fd = -1;
1032                 close(inpipe[1]);
1033         }
1034 
1035         uloop_process_add(&task->process);
1036 
1037         res = uc_resource_new(task_type, task);
1038 
1039         cbs = ucv_array_new(NULL);
1040         ucv_array_set(cbs, 0, ucv_get(output_cb));
1041         ucv_array_set(cbs, 1, ucv_get(input_cb));
1042 
1043         task->registry_index = uc_uloop_reg_add(res, cbs);
1044 
1045         return res;
1046 }
1047 
1048 
1049 static const uc_function_list_t timer_fns[] = {
1050         { "set",                uc_uloop_timer_set },
1051         { "remaining",  uc_uloop_timer_remaining },
1052         { "cancel",             uc_uloop_timer_cancel },
1053 };
1054 
1055 static const uc_function_list_t handle_fns[] = {
1056         { "fileno",             uc_uloop_handle_fileno },
1057         { "handle",             uc_uloop_handle_handle },
1058         { "delete",             uc_uloop_handle_delete },
1059 };
1060 
1061 static const uc_function_list_t process_fns[] = {
1062         { "pid",                uc_uloop_process_pid },
1063         { "delete",             uc_uloop_process_delete },
1064 };
1065 
1066 static const uc_function_list_t task_fns[] = {
1067         { "pid",                uc_uloop_task_pid },
1068         { "kill",               uc_uloop_task_kill },
1069         { "finished",   uc_uloop_task_finished },
1070 };
1071 
1072 static const uc_function_list_t pipe_fns[] = {
1073         { "send",               uc_uloop_pipe_send },
1074         { "receive",    uc_uloop_pipe_receive },
1075         { "sending",    uc_uloop_pipe_sending },
1076         { "receiving",  uc_uloop_pipe_receiving },
1077 };
1078 
1079 static const uc_function_list_t global_fns[] = {
1080         { "error",              uc_uloop_error },
1081         { "init",               uc_uloop_init },
1082         { "run",                uc_uloop_run },
1083         { "timer",              uc_uloop_timer },
1084         { "handle",             uc_uloop_handle },
1085         { "process",    uc_uloop_process },
1086         { "task",               uc_uloop_task },
1087         { "cancelling", uc_uloop_cancelling },
1088         { "running",    uc_uloop_running },
1089         { "done",               uc_uloop_done },
1090         { "end",                uc_uloop_end },
1091 };
1092 
1093 
1094 static void close_timer(void *ud)
1095 {
1096         uc_uloop_timer_t *timer = ud;
1097 
1098         if (!timer)
1099                 return;
1100 
1101         uloop_timeout_cancel(&timer->timeout);
1102         free(timer);
1103 }
1104 
1105 static void close_handle(void *ud)
1106 {
1107         uc_uloop_handle_t *handle = ud;
1108 
1109         if (!handle)
1110                 return;
1111 
1112         uloop_fd_delete(&handle->fd);
1113         ucv_put(handle->handle);
1114         free(handle);
1115 }
1116 
1117 static void close_process(void *ud)
1118 {
1119         uc_uloop_process_t *process = ud;
1120 
1121         if (!process)
1122                 return;
1123 
1124         uloop_process_delete(&process->process);
1125         free(process);
1126 }
1127 
1128 static void close_task(void *ud)
1129 {
1130         uc_uloop_task_t *task = ud;
1131 
1132         if (!task)
1133                 return;
1134 
1135         uloop_process_delete(&task->process);
1136         uloop_fd_close(&task->output);
1137 
1138         if (task->input_fd != -1)
1139                 close(task->input_fd);
1140 
1141         free(task);
1142 }
1143 
1144 static void close_pipe(void *ud)
1145 {
1146         uc_uloop_pipe_t *pipe = ud;
1147 
1148         if (!pipe)
1149                 return;
1150 
1151         close(pipe->input);
1152         close(pipe->output);
1153 
1154         free(pipe);
1155 }
1156 
1157 
1158 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1159 {
1160         uc_function_list_register(scope, global_fns);
1161 
1162 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
1163 
1164         ADD_CONST(ULOOP_READ);
1165         ADD_CONST(ULOOP_WRITE);
1166         ADD_CONST(ULOOP_EDGE_TRIGGER);
1167         ADD_CONST(ULOOP_BLOCKING);
1168 
1169         timer_type = uc_type_declare(vm, "uloop.timer", timer_fns, close_timer);
1170         handle_type = uc_type_declare(vm, "uloop.handle", handle_fns, close_handle);
1171         process_type = uc_type_declare(vm, "uloop.process", process_fns, close_process);
1172         task_type = uc_type_declare(vm, "uloop.task", task_fns, close_task);
1173         pipe_type = uc_type_declare(vm, "uloop.pipe", pipe_fns, close_pipe);
1174 
1175         object_registry = ucv_array_new(vm);
1176 
1177         uc_vm_registry_set(vm, "uloop.registry", object_registry);
1178 }
1179 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt