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 /** 18 * # OpenWrt uloop event loop 19 * 20 * The `uloop` binding provides functions for integrating with the OpenWrt 21 * {@link https://github.com/openwrt/libubox/blob/master/uloop.h uloop library}. 22 * 23 * Functions can be individually imported and directly accessed using the 24 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 25 * syntax: 26 * 27 * ```javascript 28 * import { init, handle, timer, interval, process, signal, task, run } from 'uloop'; 29 * 30 * init(); 31 * 32 * handle(…); 33 * timer(…); 34 * interval(…); 35 * process(…); 36 * signal(…); 37 * task(…); 38 * 39 * run(); 40 * ``` 41 * 42 * Alternatively, the module namespace can be imported using a wildcard import 43 * statement: 44 * 45 * ```javascript 46 * import * as uloop from 'uloop'; 47 * 48 * uloop.init(); 49 * 50 * uloop.handle(…); 51 * uloop.timer(…); 52 * uloop.interval(…); 53 * uloop.process(…); 54 * uloop.signal(…); 55 * uloop.task(…); 56 * 57 * uloop.run(); 58 * ``` 59 * 60 * Additionally, the uloop binding namespace may also be imported by invoking 61 * the `ucode` interpreter with the `-luloop` switch. 62 * 63 * @module uloop 64 */ 65 66 #include <errno.h> 67 #include <unistd.h> 68 #include <limits.h> 69 #include <fcntl.h> 70 #include <sys/wait.h> 71 72 #include <libubox/uloop.h> 73 74 #include "ucode/module.h" 75 #include "ucode/internal/platform.h" 76 77 #define ok_return(expr) do { last_error = 0; return (expr); } while(0) 78 #define err_return(err) do { last_error = err; return NULL; } while(0) 79 80 static int last_error = 0; 81 82 typedef struct { 83 uc_vm_t *vm; 84 uc_value_t *obj; 85 } uc_uloop_cb_t; 86 87 static void * 88 uc_uloop_alloc(uc_vm_t *vm, const char *type, size_t size, uc_value_t *func) 89 { 90 uc_uloop_cb_t *cb; 91 uc_value_t *obj; 92 93 obj = ucv_resource_create_ex(vm, type, (void **)&cb, 2, size); 94 if (!obj) 95 return NULL; 96 97 cb->vm = vm; 98 cb->obj = ucv_get(obj); 99 ucv_resource_persistent_set(obj, true); 100 ucv_resource_value_set(obj, 0, ucv_get(func)); 101 102 return cb; 103 } 104 105 static void 106 uc_uloop_cb_free(uc_uloop_cb_t *cb) 107 { 108 uc_value_t *obj = cb->obj; 109 uc_resource_ext_t *ext; 110 111 if (!obj) 112 return; 113 114 cb->obj = NULL; 115 116 ext = (uc_resource_ext_t *)obj; 117 for (size_t i = 0; i < ext->uvcount; i++) 118 ucv_resource_value_set(obj, i, NULL); 119 120 ucv_resource_persistent_set(obj, false); 121 ucv_put(obj); 122 } 123 124 static bool 125 uc_uloop_vm_call(uc_vm_t *vm, bool mcall, size_t nargs) 126 { 127 uc_value_t *exh, *val; 128 129 if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE) 130 return true; 131 132 exh = uc_vm_registry_get(vm, "uloop.ex_handler"); 133 if (!ucv_is_callable(exh)) 134 goto error; 135 136 val = uc_vm_exception_object(vm); 137 uc_vm_stack_push(vm, ucv_get(exh)); 138 uc_vm_stack_push(vm, val); 139 140 if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE) 141 goto error; 142 143 ucv_put(uc_vm_stack_pop(vm)); 144 145 return false; 146 147 error: 148 uloop_end(); 149 return false; 150 } 151 152 static void 153 uc_uloop_cb_invoke(uc_uloop_cb_t *cb, uc_value_t **args, size_t nargs) 154 { 155 uc_vm_t *vm = cb->vm; 156 uc_value_t *func = ucv_resource_value_get(cb->obj, 0); 157 158 if (!ucv_is_callable(func)) 159 return; 160 161 uc_vm_stack_push(vm, ucv_get(cb->obj)); 162 uc_vm_stack_push(vm, ucv_get(func)); 163 for (size_t i = 0; i < nargs; i++) 164 uc_vm_stack_push(vm, ucv_get(args[i])); 165 166 if (uc_uloop_vm_call(vm, true, nargs)) 167 ucv_put(uc_vm_stack_pop(vm)); 168 } 169 170 /** 171 * Retrieves the last error message. 172 * 173 * This function retrieves the last error message generated by the uloop event loop. 174 * If no error occurred, it returns `null`. 175 * 176 * @function module:uloop#error 177 * 178 * @returns {?string} 179 * Returns the last error message as a string, or `null` if no error occurred. 180 * 181 * @example 182 * // Retrieve the last error message 183 * const errorMessage = uloop.error(); 184 * 185 * if (errorMessage) 186 * printf(`Error message: ${errorMessage}\n`); 187 * else 188 * printf("No error occurred\n"); 189 */ 190 static uc_value_t * 191 uc_uloop_error(uc_vm_t *vm, size_t nargs) 192 { 193 uc_value_t *errmsg; 194 195 if (last_error == 0) 196 return NULL; 197 198 errmsg = ucv_string_new(strerror(last_error)); 199 last_error = 0; 200 201 return errmsg; 202 } 203 204 /** 205 * Initializes the uloop event loop. 206 * 207 * This function initializes the uloop event loop, allowing subsequent 208 * usage of uloop functionalities. It takes no arguments. 209 * 210 * Returns `true` on success. 211 * Returns `null` if an error occurred during initialization. 212 * 213 * @function module:uloop#init 214 * 215 * @returns {?boolean} 216 * Returns `true` on success, `null` on error. 217 * 218 * @example 219 * // Initialize the uloop event loop 220 * const success = uloop.init(); 221 * 222 * if (success) 223 * printf("uloop event loop initialized successfully\n"); 224 * else 225 * die(`Initialization failure: ${uloop.error()}\n`); 226 */ 227 static uc_value_t * 228 uc_uloop_init(uc_vm_t *vm, size_t nargs) 229 { 230 int rv = uloop_init(); 231 232 if (rv == -1) 233 err_return(errno); 234 235 ok_return(ucv_boolean_new(true)); 236 } 237 238 /** 239 * Runs the uloop event loop. 240 * 241 * This function starts running the uloop event loop, allowing it to handle 242 * scheduled events and callbacks. If a timeout value is provided and is 243 * non-negative, the event loop will run for that amount of milliseconds 244 * before returning. If the timeout is omitted or negative, the event loop 245 * runs indefinitely until explicitly stopped. 246 * 247 * @function module:uloop#run 248 * 249 * @param {number} [timeout=-1] 250 * Optional. The timeout value in milliseconds for running the event loop. 251 * Defaults to -1, indicating an indefinite run. 252 * 253 * @returns {?number} 254 * Returns a signal number or 0 on success, `null` on error. 255 * 256 * @example 257 * // Run the uloop event loop indefinitely 258 * const success = uloop.run(); 259 * if (rc == null) 260 * die(`Error occurred during uloop execution: ${uloop.error()}\n`); 261 * else if (rc != 0) 262 * printf("uloop event loop was interrupted by a signal: %d\n", rc); 263 * 264 * // Run the uloop event loop for 1000 milliseconds 265 * const success = uloop.run(1000); 266 * if (rc == null) 267 * die(`Error occurred during uloop execution: ${uloop.error()}\n`); 268 * else if (rc != 0) 269 * printf("uloop event loop was interrupted by a signal: %d\n", rc); 270 */ 271 static uc_value_t * 272 uc_uloop_run(uc_vm_t *vm, size_t nargs) 273 { 274 uc_value_t *timeout = uc_fn_arg(0); 275 int t, rv; 276 277 errno = 0; 278 t = timeout ? (int)ucv_int64_get(timeout) : -1; 279 280 if (errno) 281 err_return(errno); 282 283 rv = uloop_run_timeout(t); 284 285 ok_return(ucv_int64_new(rv)); 286 } 287 288 /** 289 * Checks if the uloop event loop is currently shutting down. 290 * 291 * This function checks whether the uloop event loop is currently in the process 292 * of shutting down. 293 * 294 * @function module:uloop#cancelling 295 * 296 * @returns {boolean} 297 * Returns `true` if uloop is currently shutting down, `false` otherwise. 298 * 299 * @example 300 * // Check if the uloop event loop is shutting down 301 * const shuttingDown = uloop.cancelling(); 302 * if (shuttingDown) 303 * printf("uloop event loop is currently shutting down\n"); 304 * else 305 * printf("uloop event loop is not shutting down\n"); 306 */ 307 static uc_value_t * 308 uc_uloop_cancelling(uc_vm_t *vm, size_t nargs) 309 { 310 ok_return(ucv_boolean_new(uloop_cancelling())); 311 } 312 313 /** 314 * Checks if the uloop event loop is currently running. 315 * 316 * This function checks whether the uloop event loop is currently started 317 * and running. 318 * 319 * @function module:uloop#running 320 * 321 * @returns {boolean} 322 * Returns `true` if the event loop is currently running, `false` otherwise. 323 * 324 * @example 325 * // Check if the uloop event loop is running 326 * const isRunning = uloop.running(); 327 * if (isRunning) 328 * printf("uloop event loop is currently running\n"); 329 * else 330 * printf("uloop event loop is not running\n"); 331 */ 332 static uc_value_t * 333 uc_uloop_running(uc_vm_t *vm, size_t nargs) 334 { 335 bool prev = uloop_cancelled; 336 bool active; 337 338 uloop_cancelled = true; 339 active = uloop_cancelling(); 340 uloop_cancelled = prev; 341 342 ok_return(ucv_boolean_new(active)); 343 } 344 345 /** 346 * Halts the uloop event loop. 347 * 348 * This function halts the uloop event loop, stopping its execution and 349 * preventing further processing of scheduled events and callbacks. 350 * 351 * Expired timeouts and already queued event callbacks are still run to 352 * completion. 353 * 354 * @function module:uloop#end 355 * 356 * @returns {void} 357 * This function does not return any value. 358 * 359 * @example 360 * // Halt the uloop event loop 361 * uloop.end(); 362 */ 363 static uc_value_t * 364 uc_uloop_end(uc_vm_t *vm, size_t nargs) 365 { 366 uloop_end(); 367 368 ok_return(NULL); 369 } 370 371 /** 372 * Stops the uloop event loop and cancels pending timeouts and events. 373 * 374 * This function immediately stops the uloop event loop, cancels all pending 375 * timeouts and events, unregisters all handles, and deallocates associated 376 * resources. 377 * 378 * @function module:uloop#done 379 * 380 * @returns {void} 381 * This function does not return any value. 382 * 383 * @example 384 * // Stop the uloop event loop and clean up resources 385 * uloop.done(); 386 */ 387 static uc_value_t * 388 uc_uloop_done(uc_vm_t *vm, size_t nargs) 389 { 390 uloop_done(); 391 392 ok_return(NULL); 393 } 394 395 396 /** 397 * Represents a uloop timer instance as returned by 398 * {@link module:uloop#timer|timer()}. 399 * 400 * @class module:uloop.timer 401 * @hideconstructor 402 * 403 * @see {@link module:uloop#timer|timer()} 404 * 405 * @example 406 * 407 * const timeout = uloop.timer(…); 408 * 409 * timeout.set(…); 410 * timeout.remaining(); 411 * timeout.cancel(); 412 */ 413 typedef struct { 414 uc_uloop_cb_t cb; 415 struct uloop_timeout timeout; 416 } uc_uloop_timer_t; 417 418 static int 419 uc_uloop_timeout_clear(uc_uloop_timer_t *timer) 420 { 421 int rv = uloop_timeout_cancel(&timer->timeout); 422 423 uc_uloop_cb_free(&timer->cb); 424 425 return rv; 426 } 427 428 /** 429 * Rearms the uloop timer with the specified timeout. 430 * 431 * This method rearms the uloop timer with the specified timeout value, 432 * allowing it to trigger after the specified amount of time. If no timeout 433 * value is provided or if the provided value is negative, the timer remains 434 * disabled until rearmed with a positive timeout value. 435 * 436 * @function module:uloop.timer#set 437 * 438 * @param {number} [timeout=-1] 439 * Optional. The timeout value in milliseconds until the timer expires. 440 * Defaults to -1, which disables the timer until rearmed with a positive timeout. 441 * 442 * @returns {?boolean} 443 * Returns `true` on success, `null` on error, such as an invalid timeout argument. 444 * 445 * @example 446 * const timeout = uloop.timer(…); 447 * 448 * // Rearm the uloop timer with a timeout of 1000 milliseconds 449 * timeout.set(1000); 450 * 451 * // Disable the uloop timer 452 * timeout.set(); 453 */ 454 static uc_value_t * 455 uc_uloop_timer_set(uc_vm_t *vm, size_t nargs) 456 { 457 uc_uloop_timer_t *timer = uc_fn_thisval("uloop.timer"); 458 uc_value_t *timeout = uc_fn_arg(0); 459 int t, rv; 460 461 if (!timer) 462 err_return(EINVAL); 463 464 errno = 0; 465 t = timeout ? (int)ucv_int64_get(timeout) : -1; 466 467 if (errno) 468 err_return(errno); 469 470 rv = uloop_timeout_set(&timer->timeout, t); 471 472 ok_return(ucv_boolean_new(rv == 0)); 473 } 474 475 /** 476 * Returns the number of milliseconds until the uloop timer expires. 477 * 478 * This method returns the remaining time until the uloop timer expires. If 479 * the timer is not armed (i.e., disabled), it returns -1. 480 * 481 * @function module:uloop.timer#remaining 482 * 483 * @returns {number} 484 * The number of milliseconds until the timer expires, or -1 if the timer is not armed. 485 * 486 * @example 487 * // Get the remaining time until the uloop timer expires (~500ms) 488 * const remainingTime = timer.remaining(); 489 * if (remainingTime !== -1) 490 * printf("Time remaining until timer expires: %d ms\n", remainingTime); 491 * else 492 * printf("Timer is not armed\n"); 493 */ 494 static uc_value_t * 495 uc_uloop_timer_remaining(uc_vm_t *vm, size_t nargs) 496 { 497 uc_uloop_timer_t *timer = uc_fn_thisval("uloop.timer"); 498 int64_t rem; 499 500 if (!timer) 501 err_return(EINVAL); 502 503 #ifdef HAVE_ULOOP_TIMEOUT_REMAINING64 504 rem = uloop_timeout_remaining64(&timer->timeout); 505 #else 506 rem = (int64_t)uloop_timeout_remaining(&timer->timeout); 507 #endif 508 509 ok_return(ucv_int64_new(rem)); 510 } 511 512 /** 513 * Cancels the uloop timer, disarming it and removing it from the event loop. 514 * 515 * This method destroys the uloop timer and releases its associated resources. 516 * 517 * @function module:uloop.timer#cancel 518 * 519 * @returns {boolean} 520 * Returns `true` on success. 521 * 522 * @example 523 * // Cancel the uloop timer 524 * timer.cancel(); 525 */ 526 static uc_value_t * 527 uc_uloop_timer_cancel(uc_vm_t *vm, size_t nargs) 528 { 529 uc_uloop_timer_t *timer = uc_fn_thisval("uloop.timer"); 530 int rv; 531 532 if (!timer) 533 err_return(EINVAL); 534 535 rv = uc_uloop_timeout_clear(timer); 536 537 ok_return(ucv_boolean_new(rv == 0)); 538 } 539 540 static void 541 uc_uloop_timer_cb(struct uloop_timeout *timeout) 542 { 543 uc_uloop_timer_t *timer = container_of(timeout, uc_uloop_timer_t, timeout); 544 545 uc_uloop_cb_invoke(&timer->cb, NULL, 0); 546 } 547 548 /** 549 * Creates a timer instance for scheduling callbacks. 550 * 551 * This function creates a timer instance for scheduling callbacks to be 552 * executed after a specified timeout duration. It takes an optional timeout 553 * parameter, which defaults to -1, indicating that the timer is initially not 554 * armed and can be enabled later by invoking the `.set(timeout)` method on the 555 * instance. 556 * 557 * A callback function must be provided to be executed when the timer expires. 558 * 559 * @function module:uloop#timer 560 * 561 * @param {number} [timeout=-1] 562 * Optional. The timeout duration in milliseconds. Defaults to -1, indicating 563 * the timer is not initially armed. 564 * 565 * @param {Function} callback 566 * The callback function to be executed when the timer expires. 567 * 568 * @returns {?module:uloop.timer} 569 * Returns a timer instance for scheduling callbacks. 570 * Returns `null` when the timeout or callback arguments are invalid. 571 * 572 * @example 573 * // Create a timer with a callback to be executed after 1000 milliseconds 574 * const myTimer = uloop.timer(1000, () => { 575 * printf("Timer expired!\n"); 576 * }); 577 * 578 * // Later enable the timer with a timeout of 500 milliseconds 579 * myTimer.set(500); 580 */ 581 static uc_value_t * 582 uc_uloop_timer(uc_vm_t *vm, size_t nargs) 583 { 584 uc_value_t *timeout = uc_fn_arg(0); 585 uc_value_t *callback = uc_fn_arg(1); 586 uc_uloop_timer_t *timer; 587 int t; 588 589 errno = 0; 590 t = timeout ? ucv_int64_get(timeout) : -1; 591 592 if (errno) 593 err_return(errno); 594 595 if (!ucv_is_callable(callback)) 596 err_return(EINVAL); 597 598 timer = uc_uloop_alloc(vm, "uloop.timer", sizeof(*timer), callback); 599 timer->timeout.cb = uc_uloop_timer_cb; 600 601 if (t >= 0) 602 uloop_timeout_set(&timer->timeout, t); 603 604 ok_return(timer->cb.obj); 605 } 606 607 608 /** 609 * Represents a uloop handle instance as returned by 610 * {@link module:uloop#handle|handle()}. 611 * 612 * @class module:uloop.handle 613 * @hideconstructor 614 * 615 * @see {@link module:uloop#handle|handle()} 616 * 617 * @example 618 * 619 * const handle = uloop.handle(…); 620 * 621 * handle.fileno(); 622 * handle.handle(); 623 * 624 * handle.delete(); 625 */ 626 typedef struct { 627 uc_uloop_cb_t cb; 628 struct uloop_fd fd; 629 } uc_uloop_handle_t; 630 631 static int 632 uc_uloop_handle_clear(uc_uloop_handle_t *handle) 633 { 634 int rv = uloop_fd_delete(&handle->fd); 635 636 uc_uloop_cb_free(&handle->cb); 637 638 return rv; 639 } 640 641 /** 642 * Returns the file descriptor number. 643 * 644 * This method returns the file descriptor number associated with the underlying 645 * handle, which might refer to a socket or file instance. 646 * 647 * @function module:uloop.handle#fileno 648 * 649 * @returns {number} 650 * The file descriptor number associated with the handle. 651 * 652 * @example 653 * // Get the file descriptor number associated with the uloop handle 654 * const fd = handle.fileno(); 655 * printf("File descriptor number: %d\n", fd); 656 */ 657 static uc_value_t * 658 uc_uloop_handle_fileno(uc_vm_t *vm, size_t nargs) 659 { 660 uc_uloop_handle_t *handle = uc_fn_thisval("uloop.handle"); 661 662 if (!handle) 663 err_return(EINVAL); 664 665 ok_return(ucv_int64_new(handle->fd.fd)); 666 } 667 668 /** 669 * Returns the underlying file or socket instance. 670 * 671 * This method returns the underlying file or socket instance associated with 672 * the uloop handle. 673 * 674 * @function module:uloop.handle#handle 675 * 676 * @returns {module:fs.file|module:fs.proc|module:socket.socket} 677 * The underlying file or socket instance associated with the handle. 678 * 679 * @example 680 * // Get the associated file or socket instance 681 * const fileOrSocket = handle.handle(); 682 * printf("Handle: %s\n", fileOrSocket); // e.g. <socket 0x5> or <fs.proc …> 683 */ 684 static uc_value_t * 685 uc_uloop_handle_handle(uc_vm_t *vm, size_t nargs) 686 { 687 uc_uloop_handle_t *handle = uc_fn_thisval("uloop.handle"); 688 689 if (!handle) 690 err_return(EINVAL); 691 692 ok_return(ucv_get(ucv_resource_value_get(handle->cb.obj, 1))); 693 } 694 695 /** 696 * Unregisters the uloop handle. 697 * 698 * This method unregisters the uloop handle from the uloop event loop and frees 699 * any associated resources. After calling this method, the handle instance 700 * should no longer be used. 701 * 702 * @function module:uloop.handle#delete 703 * 704 * @returns {void} 705 * This function does not return a value. 706 * 707 * @example 708 * // Unregister the uloop handle and free associated resources 709 * handle.delete(); 710 * printf("Handle deleted successfully\n"); 711 */ 712 static uc_value_t * 713 uc_uloop_handle_delete(uc_vm_t *vm, size_t nargs) 714 { 715 uc_uloop_handle_t *handle = uc_fn_thisval("uloop.handle"); 716 int rv; 717 718 if (!handle) 719 err_return(EINVAL); 720 721 rv = uc_uloop_handle_clear(handle); 722 723 if (rv != 0) 724 err_return(errno); 725 726 ok_return(ucv_boolean_new(true)); 727 } 728 729 static void 730 uc_uloop_handle_cb(struct uloop_fd *fd, unsigned int flags) 731 { 732 uc_uloop_handle_t *handle = container_of(fd, uc_uloop_handle_t, fd); 733 uc_value_t *args[3] = { 734 ucv_uint64_new(flags), 735 ucv_boolean_new(fd->eof), 736 ucv_boolean_new(fd->error), 737 }; 738 739 uc_uloop_cb_invoke(&handle->cb, args, 3); 740 ucv_put(args[0]); 741 ucv_put(args[1]); 742 ucv_put(args[2]); 743 } 744 745 static int 746 get_fd(uc_vm_t *vm, uc_value_t *val) 747 { 748 uc_value_t *fn; 749 int64_t n; 750 int fd; 751 752 fn = ucv_property_get(val, "fileno"); 753 754 if (ucv_is_callable(fn)) { 755 uc_vm_stack_push(vm, ucv_get(val)); 756 uc_vm_stack_push(vm, ucv_get(fn)); 757 758 if (uc_vm_call(vm, true, 0) == EXCEPTION_NONE) { 759 val = uc_vm_stack_pop(vm); 760 } 761 else { 762 errno = EBADF; 763 val = NULL; 764 } 765 } 766 else { 767 ucv_get(val); 768 } 769 770 n = ucv_int64_get(val); 771 772 if (errno) { 773 fd = -1; 774 } 775 else if (n < 0 || n > (int64_t)INT_MAX) { 776 errno = EBADF; 777 fd = -1; 778 } 779 else { 780 fd = (int)n; 781 } 782 783 ucv_put(val); 784 785 return fd; 786 } 787 788 /** 789 * Creates a handle instance for monitoring file descriptor events. 790 * 791 * This function creates a handle instance for monitoring events on a file 792 * descriptor, file, or socket. It takes the file or socket handle, a callback 793 * function to be invoked when the specified IO events occur, and bitwise OR-ed 794 * flags of IO events (`ULOOP_READ`, `ULOOP_WRITE`) that the callback should be 795 * invoked for. 796 * 797 * @function module:uloop#handle 798 * 799 * @param {number|module:fs.file|module:fs.proc|module:socket.socket|module:io.handle} handle 800 * The file handle (descriptor number, file or socket instance). 801 * 802 * @param {Function} callback 803 * The callback function to be invoked when the specified IO events occur. 804 * 805 * @param {number} events 806 * Bitwise OR-ed flags of IO events (`ULOOP_READ`, `ULOOP_WRITE`) that the 807 * callback should be invoked for. 808 * 809 * @returns {?module:uloop.handle} 810 * Returns a handle instance for monitoring file descriptor events. 811 * Returns `null` when the handle, callback or signal arguments are invalid. 812 * 813 * @example 814 * // Create a handle for monitoring read events on file descriptor 3 815 * const myHandle = uloop.handle(3, (events) => { 816 * if (events & ULOOP_READ) 817 * printf("Read event occurred!\n"); 818 * }, uloop.ULOOP_READ); 819 * 820 * // Check socket for writability 821 * const sock = socket.connect("example.org", 80); 822 * uloop.handle(sock, (events) => { 823 * sock.send("GET / HTTP/1.0\r\n\r\n"); 824 * }, uloop.ULOOP_WRITE) 825 */ 826 static uc_value_t * 827 uc_uloop_handle(uc_vm_t *vm, size_t nargs) 828 { 829 uc_value_t *fileno = uc_fn_arg(0); 830 uc_value_t *callback = uc_fn_arg(1); 831 uc_value_t *flags = uc_fn_arg(2); 832 uc_uloop_handle_t *handle; 833 int fd, ret; 834 uint64_t f; 835 836 fd = get_fd(vm, fileno); 837 838 if (fd == -1) 839 err_return(errno); 840 841 f = ucv_uint64_get(flags); 842 843 if (errno) 844 err_return(errno); 845 846 if (f == 0 || f > (uint64_t)UINT_MAX) 847 err_return(EINVAL); 848 849 if (!ucv_is_callable(callback)) 850 err_return(EINVAL); 851 852 handle = uc_uloop_alloc(vm, "uloop.handle", sizeof(*handle), callback); 853 handle->fd.fd = fd; 854 handle->fd.cb = uc_uloop_handle_cb; 855 856 ret = uloop_fd_add(&handle->fd, (unsigned int)f); 857 if (ret != 0) { 858 ucv_put(handle->cb.obj); 859 err_return(errno); 860 } 861 862 ucv_resource_value_set(handle->cb.obj, 1, ucv_get(fileno)); 863 ok_return(handle->cb.obj); 864 } 865 866 867 /** 868 * Represents a uloop process instance as returned by 869 * {@link module:uloop#process|process()}. 870 * 871 * @class module:uloop.process 872 * @hideconstructor 873 * 874 * @see {@link module:uloop#process|process()} 875 * 876 * @example 877 * 878 * const proc = uloop.process(…); 879 * 880 * proc.pid(); 881 * 882 * proc.delete(); 883 */ 884 typedef struct { 885 uc_uloop_cb_t cb; 886 struct uloop_process process; 887 } uc_uloop_process_t; 888 889 static int 890 uc_uloop_process_clear(uc_uloop_process_t *process) 891 { 892 int rv = uloop_process_delete(&process->process); 893 894 uc_uloop_cb_free(&process->cb); 895 896 return rv; 897 } 898 899 /** 900 * Returns the process ID. 901 * 902 * This method returns the process ID (PID) of the operating system process 903 * launched by {@link module:uloop#process|process(). 904 * 905 * @function module:uloop.process#pid 906 * 907 * @returns {number} 908 * The process ID (PID) of the associated launched process. 909 * 910 * @example 911 * const proc = uloop.process(…); 912 * 913 * printf("Process ID: %d\n", proc.pid()); 914 */ 915 static uc_value_t * 916 uc_uloop_process_pid(uc_vm_t *vm, size_t nargs) 917 { 918 uc_uloop_process_t *process = uc_fn_thisval("uloop.process"); 919 920 if (!process) 921 err_return(EINVAL); 922 923 ok_return(ucv_int64_new(process->process.pid)); 924 } 925 926 /** 927 * Unregisters the process from uloop. 928 * 929 * This method unregisters the process from the uloop event loop and releases 930 * any associated resources. However, note that the operating system process 931 * itself is not terminated by this method. 932 * 933 * @function module:uloop.process#delete 934 * 935 * @returns {boolean} 936 * Returns `true` on success. 937 * 938 * @example 939 * const proc = uloop.process(…); 940 * 941 * proc.delete(); 942 */ 943 static uc_value_t * 944 uc_uloop_process_delete(uc_vm_t *vm, size_t nargs) 945 { 946 uc_uloop_process_t *process = uc_fn_thisval("uloop.process"); 947 int rv; 948 949 if (!process) 950 err_return(EINVAL); 951 952 rv = uc_uloop_process_clear(process); 953 954 if (rv != 0) 955 err_return(EINVAL); 956 957 ok_return(ucv_boolean_new(true)); 958 } 959 960 static void 961 uc_uloop_process_cb(struct uloop_process *proc, int exitcode) 962 { 963 uc_uloop_process_t *process = container_of(proc, uc_uloop_process_t, process); 964 int status = WIFEXITED(exitcode) ? WEXITSTATUS(exitcode) : 965 WIFSIGNALED(exitcode) ? -WTERMSIG(exitcode) : -1; 966 uc_value_t *e = ucv_int64_new(status); 967 968 uc_uloop_cb_invoke(&process->cb, &e, 1); 969 uc_uloop_process_clear(process); 970 ucv_put(e); 971 } 972 973 /** 974 * Creates a process instance for executing external programs. 975 * 976 * This function creates a process instance for executing external programs. 977 * It takes the executable path string, an optional string array as the argument 978 * vector, an optional dictionary describing environment variables, and a 979 * callback function to be invoked when the invoked process ends. 980 * 981 * @function module:uloop#process 982 * 983 * @param {string} executable 984 * The path to the executable program. 985 * 986 * @param {string[]} [args] 987 * Optional. An array of strings representing the arguments passed to the 988 * executable. 989 * 990 * @param {Object<string, *>} [env] 991 * Optional. A dictionary describing environment variables for the process. 992 * 993 * @param {Function} callback 994 * The callback function to be invoked when the invoked process ends. 995 * Receives the exit code for a normally exited process, or the negative 996 * signal number if the process was terminated by a signal. 997 * 998 * @returns {?module:uloop.process} 999 * Returns a process instance for executing external programs. 1000 * Returns `null` on error, e.g. due to `exec()` failure or invalid arguments. 1001 * 1002 * @example 1003 * // Create a process instance for executing 'ls' command 1004 * const myProcess = uloop.process("/bin/ls", ["-l", "/tmp"], null, (code) => { 1005 * printf(`Process exited with code ${code}\n`); 1006 * }); 1007 */ 1008 static uc_value_t * 1009 uc_uloop_process(uc_vm_t *vm, size_t nargs) 1010 { 1011 uc_value_t *executable = uc_fn_arg(0); 1012 uc_value_t *arguments = uc_fn_arg(1); 1013 uc_value_t *env_arg = uc_fn_arg(2); 1014 uc_value_t *callback = uc_fn_arg(3); 1015 uc_uloop_process_t *process; 1016 uc_stringbuf_t *buf; 1017 char **argp, **envp; 1018 pid_t pid; 1019 size_t i; 1020 1021 if (ucv_type(executable) != UC_STRING || 1022 (arguments && ucv_type(arguments) != UC_ARRAY) || 1023 (env_arg && ucv_type(env_arg) != UC_OBJECT) || 1024 !ucv_is_callable(callback)) { 1025 err_return(EINVAL); 1026 } 1027 1028 pid = fork(); 1029 1030 if (pid == -1) 1031 err_return(errno); 1032 1033 if (pid == 0) { 1034 argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *)); 1035 envp = environ; 1036 1037 if (env_arg) { 1038 envp = calloc(ucv_object_length(env_arg) + 1, sizeof(char *)); 1039 i = 0; 1040 ucv_object_foreach(env_arg, envk, envv) { 1041 buf = xprintbuf_new(); 1042 1043 ucv_stringbuf_printf(buf, "%s=", envk); 1044 ucv_to_stringbuf(vm, buf, envv, false); 1045 1046 envp[i++] = buf->buf; 1047 1048 free(buf); 1049 } 1050 } 1051 1052 if (!argp || !envp) 1053 _exit(-1); 1054 1055 argp[0] = ucv_to_string(vm, executable); 1056 1057 for (i = 0; i < ucv_array_length(arguments); i++) 1058 argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i)); 1059 1060 execvpe((const char *)ucv_string_get(executable), 1061 (char * const *)argp, (char * const *)envp); 1062 1063 _exit(-1); 1064 } 1065 1066 process = uc_uloop_alloc(vm, "uloop.process", sizeof(*process), callback); 1067 process->process.pid = pid; 1068 process->process.cb = uc_uloop_process_cb; 1069 uloop_process_add(&process->process); 1070 1071 ok_return(process->cb.obj); 1072 } 1073 1074 1075 static bool 1076 readall(int fd, void *buf, size_t len) 1077 { 1078 ssize_t rlen; 1079 1080 while (len > 0) { 1081 rlen = read(fd, buf, len); 1082 1083 if (rlen == -1) { 1084 if (errno == EINTR) 1085 continue; 1086 1087 return false; 1088 } 1089 1090 if (rlen == 0) { 1091 errno = EINTR; 1092 1093 return false; 1094 } 1095 1096 buf += rlen; 1097 len -= rlen; 1098 } 1099 1100 return true; 1101 } 1102 1103 static bool 1104 writeall(int fd, void *buf, size_t len) 1105 { 1106 ssize_t wlen; 1107 1108 while (len > 0) { 1109 wlen = write(fd, buf, len); 1110 1111 if (wlen == -1) { 1112 if (errno == EINTR) 1113 continue; 1114 1115 return false; 1116 } 1117 1118 buf += wlen; 1119 len -= wlen; 1120 } 1121 1122 return true; 1123 } 1124 1125 1126 /** 1127 * Represents a uloop task communication pipe instance, passed as sole argument 1128 * to the task function by {@link module:uloop#task|task()}. 1129 * 1130 * @class module:uloop.pipe 1131 * @hideconstructor 1132 * 1133 * @see {@link module:uloop#task|task()} 1134 * 1135 * @example * 1136 * const task = uloop.task((pipe) => { 1137 * … 1138 * pipe.send(); 1139 * … 1140 * pipe.receive(); 1141 * … 1142 * }, …); 1143 */ 1144 typedef struct { 1145 int input; 1146 int output; 1147 bool has_sender; 1148 bool has_receiver; 1149 } uc_uloop_pipe_t; 1150 1151 static uc_value_t * 1152 uc_uloop_pipe_send_common(uc_vm_t *vm, uc_value_t *msg, int fd) 1153 { 1154 uc_stringbuf_t *buf; 1155 size_t len; 1156 bool rv; 1157 1158 buf = xprintbuf_new(); 1159 1160 printbuf_memset(buf, 0, 0, sizeof(len)); 1161 ucv_to_stringbuf(vm, buf, msg, true); 1162 1163 len = printbuf_length(buf); 1164 memcpy(buf->buf, &len, sizeof(len)); 1165 1166 rv = writeall(fd, buf->buf, len); 1167 1168 printbuf_free(buf); 1169 1170 if (!rv) 1171 err_return(errno); 1172 1173 ok_return(ucv_boolean_new(true)); 1174 } 1175 1176 /** 1177 * Sends a serialized message to the task handle. 1178 * 1179 * This method serializes the provided message and sends it over the task 1180 * communication pipe. In the main thread, the message is deserialized and 1181 * passed as an argument to the output callback function registered with the 1182 * task handle. 1183 * 1184 * @function module:uloop.pipe#send 1185 * 1186 * @param {*} msg 1187 * The message to be serialized and sent over the pipe. It can be of arbitrary type. 1188 * 1189 * @returns {?boolean} 1190 * Returns `true` on success, indicating that the message was successfully sent 1191 * over the pipe. Returns `null` on error, such as when there's no output 1192 * callback registered with the task handle. 1193 * 1194 * @example 1195 * // Send a message over the uloop pipe 1196 * const success = pipe.send(message); 1197 * 1198 * if (success) 1199 * printf("Message sent successfully\n"); 1200 * else 1201 * die(`Error sending message: ${uloop.error()}\n`); 1202 */ 1203 static uc_value_t * 1204 uc_uloop_pipe_send(uc_vm_t *vm, size_t nargs) 1205 { 1206 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1207 uc_value_t *msg = uc_fn_arg(0); 1208 1209 if (!pipe) 1210 err_return(EINVAL); 1211 1212 if (!pipe->has_receiver) 1213 err_return(EPIPE); 1214 1215 ok_return(uc_uloop_pipe_send_common(vm, msg, pipe->output)); 1216 } 1217 1218 static bool 1219 uc_uloop_pipe_receive_common(uc_vm_t *vm, int fd, uc_value_t **res, bool skip) 1220 { 1221 enum json_tokener_error err = json_tokener_error_parse_eof; 1222 json_tokener *tok = NULL; 1223 json_object *jso = NULL; 1224 char buf[1024]; 1225 ssize_t rlen; 1226 size_t len; 1227 1228 *res = NULL; 1229 1230 if (!readall(fd, &len, sizeof(len))) 1231 err_return(errno); 1232 1233 /* message length 0 is special, means input requested on other pipe */ 1234 if (len == 0) 1235 err_return(ENODATA); 1236 1237 /* valid messages should be at least sizeof(len) plus one byte of payload */ 1238 if (len <= sizeof(len)) 1239 err_return(EINVAL); 1240 1241 len -= sizeof(len); 1242 1243 while (len > 0) { 1244 rlen = read(fd, buf, len < sizeof(buf) ? len : sizeof(buf)); 1245 1246 if (rlen == -1) { 1247 if (errno == EINTR) 1248 continue; 1249 1250 goto read_fail; 1251 } 1252 1253 /* premature EOF */ 1254 if (rlen == 0) { 1255 errno = EPIPE; 1256 goto read_fail; 1257 } 1258 1259 if (!skip) { 1260 if (!tok) 1261 tok = xjs_new_tokener(); 1262 1263 jso = json_tokener_parse_ex(tok, buf, rlen); 1264 err = json_tokener_get_error(tok); 1265 } 1266 1267 len -= rlen; 1268 } 1269 1270 if (!skip) { 1271 if (err == json_tokener_continue) { 1272 jso = json_tokener_parse_ex(tok, "\0", 1); 1273 err = json_tokener_get_error(tok); 1274 } 1275 1276 json_tokener_free(tok); 1277 1278 if (err != json_tokener_success) { 1279 errno = EINVAL; 1280 goto read_fail; 1281 } 1282 1283 *res = ucv_from_json(vm, jso); 1284 1285 json_object_put(jso); 1286 } 1287 1288 return true; 1289 1290 read_fail: 1291 if (tok) 1292 json_tokener_free(tok); 1293 1294 json_object_put(jso); 1295 err_return(errno); 1296 } 1297 1298 /** 1299 * Reads input from the task handle. 1300 * 1301 * This method reads input from the task communication pipe. The input callback 1302 * function registered with the task handle is invoked to return the input data, 1303 * which is then serialized, sent over the pipe, and deserialized by the receive 1304 * method. 1305 * 1306 * @function module:uloop.pipe#receive 1307 * 1308 * @returns {?*} 1309 * Returns the deserialized message read from the task communication pipe. 1310 * Returns `null` on error, such as when there's no input callback registered 1311 * on the task handle. 1312 * 1313 * @example 1314 * // Read input from the task communication pipe 1315 * const message = pipe.receive(); 1316 * 1317 * if (message !== null) 1318 * printf("Received message: %s\n", message); 1319 * else 1320 * die(`Error receiving message: ${uloop.error()}\n`); 1321 */ 1322 static uc_value_t * 1323 uc_uloop_pipe_receive(uc_vm_t *vm, size_t nargs) 1324 { 1325 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1326 uc_value_t *rv; 1327 size_t len = 0; 1328 1329 if (!pipe) 1330 err_return(EINVAL); 1331 1332 if (!pipe->has_sender) 1333 err_return(EPIPE); 1334 1335 /* send zero-length message to signal input request */ 1336 writeall(pipe->output, &len, sizeof(len)); 1337 1338 /* receive input message */ 1339 uc_uloop_pipe_receive_common(vm, pipe->input, &rv, false); 1340 1341 return rv; 1342 } 1343 1344 /** 1345 * Checks if the task handle provides input. 1346 * 1347 * This method checks if the task handle has an input callback registered. 1348 * It returns a boolean value indicating whether an input callback is present. 1349 * 1350 * @function module:uloop.pipe#sending 1351 * 1352 * @returns {boolean} 1353 * Returns `true` if the remote task handle has an input callback 1354 * registered, otherwise returns `false`. 1355 * 1356 * @example 1357 * // Check if the remote task handle has an input callback 1358 * const hasInputCallback = pipe.sending(); 1359 * 1360 * if (hasInputCallback) 1361 * printf("Input callback is registered on task handle\n"); 1362 * else 1363 * printf("No input callback on the task handle\n"); 1364 */ 1365 static uc_value_t * 1366 uc_uloop_pipe_sending(uc_vm_t *vm, size_t nargs) 1367 { 1368 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1369 1370 if (!pipe) 1371 err_return(EINVAL); 1372 1373 ok_return(ucv_boolean_new(pipe->has_sender)); 1374 } 1375 1376 /** 1377 * Checks if the task handle reads output. 1378 * 1379 * This method checks if the task handle has an output callback registered. 1380 * It returns a boolean value indicating whether an output callback is present. 1381 * 1382 * @function module:uloop.pipe#receiving 1383 * 1384 * @returns {boolean} 1385 * Returns `true` if the task handle has an output callback registered, 1386 * otherwise returns `false`. 1387 * 1388 * @example 1389 * // Check if the task handle has an output callback 1390 * const hasOutputCallback = pipe.receiving(); 1391 * 1392 * if (hasOutputCallback) 1393 * printf("Output callback is registered on task handle\n"); 1394 * else 1395 * printf("No output callback on the task handle\n"); 1396 */ 1397 static uc_value_t * 1398 uc_uloop_pipe_receiving(uc_vm_t *vm, size_t nargs) 1399 { 1400 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1401 1402 if (!pipe) 1403 err_return(EINVAL); 1404 1405 ok_return(ucv_boolean_new(pipe->has_receiver)); 1406 } 1407 1408 1409 /** 1410 * Represents a uloop task instance as returned by 1411 * {@link module:uloop#task|task()}. 1412 * 1413 * @class module:uloop.task 1414 * @hideconstructor 1415 * 1416 * @see {@link module:uloop#task|task()} 1417 * 1418 * @example 1419 * 1420 * const task = uloop.task(…); 1421 * 1422 * task.pid(); 1423 * task.finished(); 1424 * 1425 * task.kill(); 1426 */ 1427 typedef struct { 1428 uc_uloop_cb_t cb; 1429 struct uloop_process process; 1430 struct uloop_fd output; 1431 bool finished; 1432 int input_fd; 1433 uc_value_t *input_cb; 1434 uc_value_t *output_cb; 1435 } uc_uloop_task_t; 1436 1437 static int 1438 patch_devnull(int fd, bool write) 1439 { 1440 int devnull = open("/dev/null", write ? O_WRONLY : O_RDONLY); 1441 1442 if (devnull != -1) { 1443 dup2(fd, devnull); 1444 close(fd); 1445 } 1446 1447 return devnull; 1448 } 1449 1450 static void 1451 uloop_fd_close(struct uloop_fd *fd) { 1452 if (fd->fd == -1) 1453 return; 1454 1455 uloop_fd_delete(fd); 1456 close(fd->fd); 1457 fd->fd = -1; 1458 } 1459 1460 static void 1461 uc_uloop_task_clear(uc_uloop_task_t *task) 1462 { 1463 if (task->input_fd >= 0) { 1464 close(task->input_fd); 1465 task->input_fd = -1; 1466 1467 uloop_fd_close(&task->output); 1468 uloop_process_delete(&task->process); 1469 } 1470 1471 uc_uloop_cb_free(&task->cb); 1472 } 1473 1474 /** 1475 * Returns the process ID. 1476 * 1477 * This method returns the process ID (PID) of the underlying forked process 1478 * launched by {@link module:uloop#task|task(). 1479 * 1480 * @function module:uloop.task#pid 1481 * 1482 * @returns {number} 1483 * The process ID (PID) of the forked task process. 1484 * 1485 * @example 1486 * const task = uloop.task(…); 1487 * 1488 * printf("Process ID: %d\n", task.pid()); 1489 */ 1490 static uc_value_t * 1491 uc_uloop_task_pid(uc_vm_t *vm, size_t nargs) 1492 { 1493 uc_uloop_task_t *task = uc_fn_thisval("uloop.task"); 1494 1495 if (!task) 1496 err_return(EINVAL); 1497 1498 if (task->finished) 1499 err_return(ESRCH); 1500 1501 ok_return(ucv_int64_new(task->process.pid)); 1502 } 1503 1504 /** 1505 * Terminates the task process. 1506 * 1507 * This method terminates the task process. It sends a termination signal to 1508 * the task process, causing it to exit. Returns `true` on success, indicating 1509 * that the task process was successfully terminated. Returns `null` on error, 1510 * such as when the task process has already terminated. 1511 * 1512 * @function module:uloop.task#kill 1513 * 1514 * @returns {?boolean} 1515 * Returns `true` when the task process was successfully terminated. 1516 * Returns `null` on error, such as when the process has already terminated. 1517 * 1518 * @example 1519 * // Terminate the task process 1520 * const success = task.kill(); 1521 * 1522 * if (success) 1523 * printf("Task process terminated successfully\n"); 1524 * else 1525 * die(`Error terminating task process: ${uloop.error()}\n`); 1526 */ 1527 static uc_value_t * 1528 uc_uloop_task_kill(uc_vm_t *vm, size_t nargs) 1529 { 1530 uc_uloop_task_t *task = uc_fn_thisval("uloop.task"); 1531 int rv; 1532 1533 if (!task) 1534 err_return(EINVAL); 1535 1536 if (task->finished) 1537 err_return(ESRCH); 1538 1539 rv = kill(task->process.pid, SIGTERM); 1540 1541 if (rv == -1) 1542 err_return(errno); 1543 1544 ok_return(ucv_boolean_new(true)); 1545 } 1546 1547 /** 1548 * Checks if the task ran to completion. 1549 * 1550 * This method checks if the task function has already run to completion. 1551 * It returns a boolean value indicating whether the task function has finished 1552 * executing. 1553 * 1554 * @function module:uloop.task#finished 1555 * 1556 * @returns {boolean} 1557 * Returns `true` if the task function has already run to completion, otherwise 1558 * returns `false`. 1559 * 1560 * @example 1561 * // Check if the task function has finished executing 1562 * const isFinished = task.finished(); 1563 * 1564 * if (isFinished) 1565 * printf("Task function has finished executing\n"); 1566 * else 1567 * printf("Task function is still running\n"); 1568 */ 1569 static uc_value_t * 1570 uc_uloop_task_finished(uc_vm_t *vm, size_t nargs) 1571 { 1572 uc_uloop_task_t *task = uc_fn_thisval("uloop.task"); 1573 1574 if (!task) 1575 err_return(EINVAL); 1576 1577 ok_return(ucv_boolean_new(task->finished)); 1578 } 1579 1580 static void 1581 uc_uloop_task_output_cb(struct uloop_fd *fd, unsigned int flags) 1582 { 1583 uc_uloop_task_t *task = container_of(fd, uc_uloop_task_t, output); 1584 uc_value_t *obj = task->cb.obj; 1585 uc_vm_t *vm = task->cb.vm; 1586 uc_value_t *msg = NULL; 1587 1588 if (flags & ULOOP_READ) { 1589 while (true) { 1590 if (!uc_uloop_pipe_receive_common(vm, fd->fd, &msg, !task->output_cb)) { 1591 /* input requested */ 1592 if (last_error == ENODATA) { 1593 uc_vm_stack_push(vm, ucv_get(obj)); 1594 uc_vm_stack_push(vm, ucv_get(task->input_cb)); 1595 1596 if (!uc_uloop_vm_call(vm, true, 0)) 1597 return; 1598 1599 msg = uc_vm_stack_pop(vm); 1600 uc_uloop_pipe_send_common(vm, msg, task->input_fd); 1601 ucv_put(msg); 1602 1603 continue; 1604 } 1605 1606 /* error */ 1607 break; 1608 } 1609 1610 if (task->output_cb) { 1611 uc_vm_stack_push(vm, ucv_get(obj)); 1612 uc_vm_stack_push(vm, ucv_get(task->output_cb)); 1613 uc_vm_stack_push(vm, msg); 1614 1615 if (!uc_uloop_vm_call(vm, true, 1)) 1616 return; 1617 1618 ucv_put(uc_vm_stack_pop(vm)); 1619 } 1620 else { 1621 ucv_put(msg); 1622 } 1623 } 1624 } 1625 1626 if (!fd->registered && task->finished) 1627 uc_uloop_task_clear(task); 1628 } 1629 1630 static void 1631 uc_uloop_task_process_cb(struct uloop_process *proc, int exitcode) 1632 { 1633 uc_uloop_task_t *task = container_of(proc, uc_uloop_task_t, process); 1634 1635 task->finished = true; 1636 1637 uc_uloop_task_output_cb(&task->output, ULOOP_READ); 1638 } 1639 1640 /** 1641 * Creates a task instance for executing background tasks. 1642 * 1643 * This function creates a task instance for executing background tasks. 1644 * It takes the task function to be invoked as a background process, 1645 * an optional output callback function to be invoked when output is received 1646 * from the task, and an optional input callback function to be invoked 1647 * when input is required by the task. 1648 * 1649 * @function module:uloop#task 1650 * 1651 * @param {Function} taskFunction 1652 * The task function to be invoked as a background process. 1653 * 1654 * @param {Function} [outputCallback] 1655 * Optional. The output callback function to be invoked when output is received 1656 * from the task. It is invoked with the output data as the argument. 1657 * 1658 * @param {Function} [inputCallback] 1659 * Optional. The input callback function to be invoked when input is required 1660 * by the task. It is invoked with a function to send input to the task 1661 * as the argument. 1662 * 1663 * @returns {?module:uloop.task} 1664 * Returns a task instance for executing background tasks. 1665 * Returns `null` on error, e.g. due to fork failure or invalid arguments. 1666 * 1667 * @example 1668 * // Create a task instance for executing a background task 1669 * const myTask = uloop.task( 1670 * (pipe) => { 1671 * // Task logic 1672 * pipe.send("Hello from the task\n"); 1673 * const input = pipe.receive(); 1674 * printf(`Received input from main thread: ${input}\n`); 1675 * }, 1676 * (output) => { 1677 * // Output callback, invoked when task function calls pipe.send() 1678 * printf(`Received output from task: ${output}\n`); 1679 * }, 1680 * () => { 1681 * // Input callback, invoked when task function calls pipe.receive() 1682 * return "Input from main thread\n"; 1683 * } 1684 * ); 1685 */ 1686 static uc_value_t * 1687 uc_uloop_task(uc_vm_t *vm, size_t nargs) 1688 { 1689 uc_value_t *func = uc_fn_arg(0); 1690 uc_value_t *output_cb = uc_fn_arg(1); 1691 uc_value_t *input_cb = uc_fn_arg(2); 1692 int outpipe[2] = { -1, -1 }; 1693 int inpipe[2] = { -1, -1 }; 1694 uc_value_t *res, *cbs, *p; 1695 uc_uloop_pipe_t *tpipe; 1696 uc_uloop_task_t *task; 1697 pid_t pid; 1698 int err; 1699 1700 if (!ucv_is_callable(func) || 1701 (output_cb && !ucv_is_callable(output_cb)) || 1702 (input_cb && !ucv_is_callable(input_cb))) 1703 err_return(EINVAL); 1704 1705 if (pipe(outpipe) == -1 || pipe(inpipe) == -1) { 1706 err = errno; 1707 1708 close(outpipe[0]); close(outpipe[1]); 1709 close(inpipe[0]); close(inpipe[1]); 1710 1711 err_return(err); 1712 } 1713 1714 pid = fork(); 1715 1716 if (pid == -1) 1717 err_return(errno); 1718 1719 if (pid == 0) { 1720 uloop_done(); 1721 1722 patch_devnull(0, false); 1723 patch_devnull(1, true); 1724 patch_devnull(2, true); 1725 1726 vm->output = fdopen(1, "w"); 1727 1728 close(inpipe[1]); 1729 close(outpipe[0]); 1730 1731 tpipe = xalloc(sizeof(*tpipe)); 1732 tpipe->input = inpipe[0]; 1733 tpipe->output = outpipe[1]; 1734 tpipe->has_sender = input_cb; 1735 tpipe->has_receiver = output_cb; 1736 1737 p = ucv_resource_create(vm, "uloop.pipe", tpipe); 1738 1739 uc_vm_stack_push(vm, func); 1740 uc_vm_stack_push(vm, ucv_get(p)); 1741 1742 if (uc_uloop_vm_call(vm, false, 1)) { 1743 res = uc_vm_stack_pop(vm); 1744 uc_uloop_pipe_send_common(vm, res, tpipe->output); 1745 ucv_put(res); 1746 } 1747 1748 ucv_put(p); 1749 1750 _exit(0); 1751 } 1752 1753 close(inpipe[0]); 1754 close(outpipe[1]); 1755 1756 task = uc_uloop_alloc(vm, "uloop.task", sizeof(*task), func); 1757 task->process.pid = pid; 1758 task->process.cb = uc_uloop_task_process_cb; 1759 1760 task->output.fd = outpipe[0]; 1761 task->output.cb = uc_uloop_task_output_cb; 1762 task->output_cb = output_cb; 1763 uloop_fd_add(&task->output, ULOOP_READ); 1764 1765 if (input_cb) { 1766 task->input_fd = inpipe[1]; 1767 task->input_cb = input_cb; 1768 } 1769 else { 1770 task->input_fd = -1; 1771 close(inpipe[1]); 1772 } 1773 1774 uloop_process_add(&task->process); 1775 1776 cbs = ucv_array_new(NULL); 1777 ucv_array_set(cbs, 0, ucv_get(output_cb)); 1778 ucv_array_set(cbs, 1, ucv_get(input_cb)); 1779 ucv_resource_value_set(task->cb.obj, 1, cbs); 1780 1781 ok_return(task->cb.obj); 1782 } 1783 1784 1785 /** 1786 * Represents a uloop interval timer instance as returned by 1787 * {@link module:uloop#interval|interval()}. 1788 * 1789 * @class module:uloop.interval 1790 * @hideconstructor 1791 * 1792 * @see {@link module:uloop#interval|interval()} 1793 * 1794 * @example 1795 * 1796 * const intv = uloop.interval(…); 1797 * 1798 * intv.set(…); 1799 * intv.remaining(); 1800 * intv.expirations(); 1801 * intv.cancel(); 1802 */ 1803 #ifdef HAVE_ULOOP_INTERVAL 1804 typedef struct { 1805 uc_uloop_cb_t cb; 1806 struct uloop_interval interval; 1807 } uc_uloop_interval_t; 1808 1809 static int 1810 uc_uloop_interval_clear(uc_uloop_interval_t *interval) 1811 { 1812 int rv = uloop_interval_cancel(&interval->interval); 1813 1814 uc_uloop_cb_free(&interval->cb); 1815 1816 return rv; 1817 } 1818 1819 /** 1820 * Rearms the uloop interval timer with the specified interval. 1821 * 1822 * This method rearms the interval timer with the specified interval value, 1823 * allowing it to trigger repeatedly after the specified amount of time. If no 1824 * interval value is provided or if the provided value is negative, the interval 1825 * remains disabled until rearmed with a positive interval value. 1826 * 1827 * @function module:uloop.interval#set 1828 * 1829 * @param {number} [interval=-1] 1830 * Optional. The interval value in milliseconds specifying when the interval 1831 * triggers again. Defaults to -1, which disables the interval until rearmed 1832 * with a positive interval value. 1833 * 1834 * @returns {?boolean} 1835 * Returns `true` on success, `null` on error, such as an invalid interval argument. 1836 * 1837 * @example 1838 * // Rearm the uloop interval with a interval of 1000 milliseconds 1839 * const success = interval.set(1000); 1840 * 1841 * if (success) 1842 * printf("Interval rearmed successfully\n"); 1843 * else 1844 * printf("Error occurred while rearming interval: ${uloop.error()}\n"); 1845 * 1846 * // Disable the uloop interval 1847 * const success = interval.set(); 1848 * 1849 * if (success) 1850 * printf("Interval disabled successfully\n"); 1851 * else 1852 * printf("Error occurred while disabling interval: ${uloop.error()}\n"); 1853 */ 1854 static uc_value_t * 1855 uc_uloop_interval_set(uc_vm_t *vm, size_t nargs) 1856 { 1857 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1858 uc_value_t *timeout = uc_fn_arg(0); 1859 int t, rv; 1860 1861 if (!interval) 1862 err_return(EINVAL); 1863 1864 errno = 0; 1865 t = timeout ? (int)ucv_int64_get(timeout) : -1; 1866 1867 if (errno) 1868 err_return(errno); 1869 1870 rv = uloop_interval_set(&interval->interval, t); 1871 1872 ok_return(ucv_boolean_new(rv == 0)); 1873 } 1874 1875 /** 1876 * Returns the milliseconds until the next expiration. 1877 * 1878 * This method returns the remaining time until the uloop interval expires 1879 * and triggers again. If the interval is not armed (i.e., disabled), 1880 * it returns -1. 1881 * 1882 * @function module:uloop.interval#remaining 1883 * 1884 * @returns {number} 1885 * The milliseconds until the next expiration of the uloop interval, or -1 if 1886 * the interval is not armed. 1887 * 1888 * @example 1889 * // Get the milliseconds until the next expiration of the uloop interval 1890 * const remainingTime = interval.remaining(); 1891 * 1892 * if (remainingTime !== -1) 1893 * printf("Milliseconds until next expiration: %d\n", remainingTime); 1894 * else 1895 * printf("Interval is not armed\n"); 1896 */ 1897 static uc_value_t * 1898 uc_uloop_interval_remaining(uc_vm_t *vm, size_t nargs) 1899 { 1900 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1901 1902 if (!interval) 1903 err_return(EINVAL); 1904 1905 ok_return(ucv_int64_new(uloop_interval_remaining(&interval->interval))); 1906 } 1907 1908 /** 1909 * Returns number of times the interval timer fired. 1910 * 1911 * This method returns the number of times the uloop interval timer has expired 1912 * (fired) since it was instantiated. 1913 * 1914 * @function module:uloop.interval#expirations 1915 * 1916 * @returns {number} 1917 * The number of times the uloop interval timer has expired (fired). 1918 * 1919 * @example 1920 * // Get the number of times the uloop interval timer has expired 1921 * const expirations = interval.expirations(); 1922 * printf("Number of expirations: %d\n", expirations); 1923 */ 1924 static uc_value_t * 1925 uc_uloop_interval_expirations(uc_vm_t *vm, size_t nargs) 1926 { 1927 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1928 1929 if (!interval) 1930 err_return(EINVAL); 1931 1932 ok_return(ucv_int64_new(interval->interval.expirations)); 1933 } 1934 1935 /** 1936 * Cancels the uloop interval. 1937 * 1938 * This method cancels the uloop interval, disarming it and removing it from the 1939 * event loop. Associated resources are released. 1940 * 1941 * @function module:uloop.interval#cancel 1942 * 1943 * @returns {boolean} 1944 * Returns `true` on success. 1945 * 1946 * @example 1947 * // Cancel the uloop interval 1948 * interval.cancel(); 1949 */ 1950 static uc_value_t * 1951 uc_uloop_interval_cancel(uc_vm_t *vm, size_t nargs) 1952 { 1953 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1954 int rv; 1955 1956 if (!interval) 1957 err_return(EINVAL); 1958 1959 rv = uc_uloop_interval_clear(interval); 1960 1961 ok_return(ucv_boolean_new(rv == 0)); 1962 } 1963 1964 static void 1965 uc_uloop_interval_cb(struct uloop_interval *uintv) 1966 { 1967 uc_uloop_interval_t *interval = container_of(uintv, uc_uloop_interval_t, interval); 1968 1969 uc_uloop_cb_invoke(&interval->cb, NULL, 0); 1970 } 1971 1972 /** 1973 * Creates an interval instance for scheduling repeated callbacks. 1974 * 1975 * This function creates an interval instance for scheduling repeated callbacks 1976 * to be executed at regular intervals. It takes an optional timeout parameter, 1977 * which defaults to -1, indicating that the interval is initially not armed 1978 * and can be armed later with the `.set(timeout)` method. A callback function 1979 * must be provided to be executed when the interval expires. 1980 * 1981 * @function module:uloop#interval 1982 * 1983 * @param {number} [timeout=-1] 1984 * Optional. The interval duration in milliseconds. Defaults to -1, indicating 1985 * the interval is not initially armed. 1986 * 1987 * @param {Function} callback 1988 * The callback function to be executed when the interval expires. 1989 * 1990 * @returns {?module:uloop.interval} 1991 * Returns an interval instance for scheduling repeated callbacks. 1992 * Returns `null` when the timeout or callback arguments are invalid. 1993 * 1994 * @example 1995 * // Create an interval with a callback to be executed every 1000 milliseconds 1996 * const myInterval = uloop.interval(1000, () => { 1997 * printf("Interval callback executed!\n"); 1998 * }); 1999 * 2000 * // Later arm the interval to start executing the callback every 500 milliseconds 2001 * myInterval.set(500); 2002 */ 2003 static uc_value_t * 2004 uc_uloop_interval(uc_vm_t *vm, size_t nargs) 2005 { 2006 uc_value_t *timeout = uc_fn_arg(0); 2007 uc_value_t *callback = uc_fn_arg(1); 2008 uc_uloop_interval_t *interval; 2009 int t; 2010 2011 errno = 0; 2012 t = timeout ? ucv_int64_get(timeout) : -1; 2013 2014 if (errno) 2015 err_return(errno); 2016 2017 if (!ucv_is_callable(callback)) 2018 err_return(EINVAL); 2019 2020 interval = uc_uloop_alloc(vm, "uloop.interval", sizeof(*interval), callback); 2021 interval->interval.cb = uc_uloop_interval_cb; 2022 if (t >= 0) 2023 uloop_interval_set(&interval->interval, t); 2024 2025 ok_return(interval->cb.obj); 2026 } 2027 #endif 2028 2029 2030 /** 2031 * Represents a uloop signal Unix process signal handler as returned by 2032 * {@link module:uloop#signal|signal()}. 2033 * 2034 * @class module:uloop.signal 2035 * @hideconstructor 2036 * 2037 * @see {@link module:uloop#signal|signal()} 2038 * 2039 * @example 2040 * 2041 * const sighandler = uloop.signal(…); 2042 * 2043 * sighandler.signo(); 2044 * sighandler.delete(); 2045 */ 2046 #ifdef HAVE_ULOOP_SIGNAL 2047 typedef struct { 2048 uc_uloop_cb_t cb; 2049 struct uloop_signal signal; 2050 } uc_uloop_signal_t; 2051 2052 static int 2053 uc_uloop_signal_clear(uc_uloop_signal_t *signal) 2054 { 2055 int rv = uloop_signal_delete(&signal->signal); 2056 2057 uc_uloop_cb_free(&signal->cb); 2058 2059 return rv; 2060 } 2061 2062 /** 2063 * Returns the associated signal number. 2064 * 2065 * This method returns the signal number that this uloop signal handler is 2066 * configured to respond to. 2067 * 2068 * @function module:uloop.signal#signo 2069 * 2070 * @returns {number} 2071 * The signal number that this handler is responding to. 2072 * 2073 * @example 2074 * // Get the signal number that the uloop signal handler is responding to 2075 * const sighandler = uloop.signal("SIGINT", () => printf("Cought INT\n")); 2076 * printf("Signal number: %d\n", sighandler.signo()); 2077 */ 2078 static uc_value_t * 2079 uc_uloop_signal_signo(uc_vm_t *vm, size_t nargs) 2080 { 2081 uc_uloop_signal_t *signal = uc_fn_thisval("uloop.signal"); 2082 2083 if (!signal) 2084 err_return(EINVAL); 2085 2086 ok_return(ucv_int64_new(signal->signal.signo)); 2087 } 2088 2089 /** 2090 * Uninstalls the signal handler. 2091 * 2092 * This method uninstalls the signal handler, restoring the previous or default 2093 * handler for the signal, and releasing any associated resources. 2094 * 2095 * @function module:uloop.signal#delete 2096 * 2097 * @returns {boolean} 2098 * Returns `true` on success. 2099 * 2100 * @example 2101 * // Uninstall the signal handler and restore the previous/default handler 2102 * const sighandler = uloop.signal(…); 2103 * sighandler.delete(); 2104 */ 2105 static uc_value_t * 2106 uc_uloop_signal_delete(uc_vm_t *vm, size_t nargs) 2107 { 2108 uc_uloop_signal_t *signal = uc_fn_thisval("uloop.signal"); 2109 int rv; 2110 2111 if (!signal) 2112 err_return(EINVAL); 2113 2114 rv = uc_uloop_signal_clear(signal); 2115 2116 if (rv != 0) 2117 err_return(EINVAL); 2118 2119 ok_return(ucv_boolean_new(true)); 2120 } 2121 2122 static void 2123 uc_uloop_signal_cb(struct uloop_signal *usig) 2124 { 2125 uc_uloop_signal_t *signal = container_of(usig, uc_uloop_signal_t, signal); 2126 2127 uc_uloop_cb_invoke(&signal->cb, NULL, 0); 2128 } 2129 2130 static int 2131 parse_signo(uc_value_t *sigspec) 2132 { 2133 if (ucv_type(sigspec) == UC_STRING) { 2134 const char *signame = ucv_string_get(sigspec); 2135 2136 if (!strncasecmp(signame, "SIG", 3)) 2137 signame += 3; 2138 2139 for (size_t i = 0; i < UC_SYSTEM_SIGNAL_COUNT; i++) { 2140 if (!uc_system_signal_names[i]) 2141 continue; 2142 2143 if (strcasecmp(uc_system_signal_names[i], signame)) 2144 continue; 2145 2146 return i; 2147 } 2148 } 2149 2150 uc_value_t *signum = ucv_to_number(sigspec); 2151 int64_t signo = ucv_int64_get(signum); 2152 ucv_put(signum); 2153 2154 if (signo < 1 || signo >= UC_SYSTEM_SIGNAL_COUNT) 2155 return -1; 2156 2157 return signo; 2158 } 2159 2160 /** 2161 * Creates a signal instance for handling Unix signals. 2162 * 2163 * This function creates a signal instance for handling Unix signals. 2164 * It takes the signal name string (with or without "SIG" prefix) or signal 2165 * number, and a callback function to be invoked when the specified Unix signal 2166 * is caught. 2167 * 2168 * @function module:uloop#signal 2169 * 2170 * @param {string|number} signal 2171 * The signal name string (with or without "SIG" prefix) or signal number. 2172 * 2173 * @param {Function} callback 2174 * The callback function to be invoked when the specified Unix signal is caught. 2175 * 2176 * @returns {?module:uloop.signal} 2177 * Returns a signal instance representing the installed signal handler. 2178 * Returns `null` when the signal or callback arguments are invalid. 2179 * 2180 * @example 2181 * // Create a signal instance for handling SIGINT 2182 * const mySignal = uloop.signal("SIGINT", () => { 2183 * printf("SIGINT caught!\n"); 2184 * }); 2185 */ 2186 static uc_value_t * 2187 uc_uloop_signal(uc_vm_t *vm, size_t nargs) 2188 { 2189 int signo = parse_signo(uc_fn_arg(0)); 2190 uc_value_t *callback = uc_fn_arg(1); 2191 uc_uloop_signal_t *signal; 2192 2193 if (signo == -1 || !ucv_is_callable(callback)) 2194 err_return(EINVAL); 2195 2196 signal = uc_uloop_alloc(vm, "uloop.signal", sizeof(*signal), callback); 2197 signal->signal.signo = signo; 2198 signal->signal.cb = uc_uloop_signal_cb; 2199 2200 uloop_signal_add(&signal->signal); 2201 2202 ok_return(signal->cb.obj); 2203 } 2204 #endif 2205 2206 static uc_value_t * 2207 uc_uloop_guard(uc_vm_t *vm, size_t nargs) 2208 { 2209 uc_value_t *arg = uc_fn_arg(0); 2210 2211 if (!nargs) 2212 return ucv_get(uc_vm_registry_get(vm, "uloop.ex_handler")); 2213 2214 if (arg && !ucv_is_callable(arg)) 2215 return NULL; 2216 2217 uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg)); 2218 2219 return ucv_boolean_new(true); 2220 } 2221 2222 2223 static const uc_function_list_t timer_fns[] = { 2224 { "set", uc_uloop_timer_set }, 2225 { "remaining", uc_uloop_timer_remaining }, 2226 { "cancel", uc_uloop_timer_cancel }, 2227 }; 2228 2229 static const uc_function_list_t handle_fns[] = { 2230 { "fileno", uc_uloop_handle_fileno }, 2231 { "handle", uc_uloop_handle_handle }, 2232 { "delete", uc_uloop_handle_delete }, 2233 }; 2234 2235 static const uc_function_list_t process_fns[] = { 2236 { "pid", uc_uloop_process_pid }, 2237 { "delete", uc_uloop_process_delete }, 2238 }; 2239 2240 static const uc_function_list_t task_fns[] = { 2241 { "pid", uc_uloop_task_pid }, 2242 { "kill", uc_uloop_task_kill }, 2243 { "finished", uc_uloop_task_finished }, 2244 }; 2245 2246 static const uc_function_list_t pipe_fns[] = { 2247 { "send", uc_uloop_pipe_send }, 2248 { "receive", uc_uloop_pipe_receive }, 2249 { "sending", uc_uloop_pipe_sending }, 2250 { "receiving", uc_uloop_pipe_receiving }, 2251 }; 2252 2253 #ifdef HAVE_ULOOP_INTERVAL 2254 static const uc_function_list_t interval_fns[] = { 2255 { "set", uc_uloop_interval_set }, 2256 { "remaining", uc_uloop_interval_remaining }, 2257 { "expirations", 2258 uc_uloop_interval_expirations }, 2259 { "cancel", uc_uloop_interval_cancel }, 2260 }; 2261 #endif 2262 2263 #ifdef HAVE_ULOOP_SIGNAL 2264 static const uc_function_list_t signal_fns[] = { 2265 { "signo", uc_uloop_signal_signo }, 2266 { "delete", uc_uloop_signal_delete }, 2267 }; 2268 #endif 2269 2270 static const uc_function_list_t global_fns[] = { 2271 { "error", uc_uloop_error }, 2272 { "init", uc_uloop_init }, 2273 { "run", uc_uloop_run }, 2274 { "timer", uc_uloop_timer }, 2275 { "handle", uc_uloop_handle }, 2276 { "process", uc_uloop_process }, 2277 { "task", uc_uloop_task }, 2278 { "cancelling", uc_uloop_cancelling }, 2279 { "running", uc_uloop_running }, 2280 { "done", uc_uloop_done }, 2281 { "end", uc_uloop_end }, 2282 #ifdef HAVE_ULOOP_INTERVAL 2283 { "interval", uc_uloop_interval }, 2284 #endif 2285 #ifdef HAVE_ULOOP_SIGNAL 2286 { "signal", uc_uloop_signal }, 2287 #endif 2288 { "guard", uc_uloop_guard }, 2289 }; 2290 2291 2292 static void close_timer(void *ud) 2293 { 2294 uc_uloop_timeout_clear(ud); 2295 } 2296 2297 static void close_handle(void *ud) 2298 { 2299 uc_uloop_handle_clear(ud); 2300 } 2301 2302 static void close_process(void *ud) 2303 { 2304 uc_uloop_process_clear(ud); 2305 } 2306 2307 static void close_task(void *ud) 2308 { 2309 uc_uloop_task_clear(ud); 2310 } 2311 2312 static void close_pipe(void *ud) 2313 { 2314 uc_uloop_pipe_t *pipe = ud; 2315 2316 if (!pipe) 2317 return; 2318 2319 close(pipe->input); 2320 close(pipe->output); 2321 2322 free(pipe); 2323 } 2324 2325 #ifdef HAVE_ULOOP_INTERVAL 2326 static void close_interval(void *ud) 2327 { 2328 uc_uloop_interval_clear(ud); 2329 } 2330 #endif 2331 2332 #ifdef HAVE_ULOOP_SIGNAL 2333 static void close_signal(void *ud) 2334 { 2335 uc_uloop_signal_clear(ud); 2336 } 2337 #endif 2338 2339 2340 static struct { 2341 struct uloop_fd ufd; 2342 uc_vm_t *vm; 2343 } signal_handle; 2344 2345 static void 2346 uc_uloop_vm_signal_cb(struct uloop_fd *ufd, unsigned int events) 2347 { 2348 if (uc_vm_signal_dispatch(signal_handle.vm) != EXCEPTION_NONE) 2349 uloop_end(); 2350 } 2351 2352 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 2353 { 2354 int signal_fd; 2355 2356 uc_function_list_register(scope, global_fns); 2357 2358 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x)) 2359 2360 /** 2361 * @typedef 2362 * @name Event Mode Constants 2363 * @description 2364 * The `ULOOP_*` constants are passed as bitwise OR-ed number to the 2365 * {@link module:uloop.handle#handle|handle()} function to specify the IO 2366 * events that should be monitored on the given handle. 2367 * @property {number} ULOOP_READ - File or socket is readable. 2368 * @property {number} ULOOP_WRITE - File or socket is writable. 2369 * @property {number} ULOOP_EDGE_TRIGGER - Enable edge-triggered event mode. 2370 * @property {number} ULOOP_BLOCKING - Do not make descriptor non-blocking. 2371 */ 2372 ADD_CONST(ULOOP_READ); 2373 ADD_CONST(ULOOP_WRITE); 2374 ADD_CONST(ULOOP_EDGE_TRIGGER); 2375 ADD_CONST(ULOOP_BLOCKING); 2376 2377 uc_type_declare(vm, "uloop.timer", timer_fns, close_timer); 2378 uc_type_declare(vm, "uloop.handle", handle_fns, close_handle); 2379 uc_type_declare(vm, "uloop.process", process_fns, close_process); 2380 uc_type_declare(vm, "uloop.task", task_fns, close_task); 2381 uc_type_declare(vm, "uloop.pipe", pipe_fns, close_pipe); 2382 2383 #ifdef HAVE_ULOOP_INTERVAL 2384 uc_type_declare(vm, "uloop.interval", interval_fns, close_interval); 2385 #endif 2386 2387 #ifdef HAVE_ULOOP_SIGNAL 2388 uc_type_declare(vm, "uloop.signal", signal_fns, close_signal); 2389 #endif 2390 2391 signal_fd = uc_vm_signal_notifyfd(vm); 2392 2393 if (signal_fd != -1 && uloop_init() == 0) { 2394 signal_handle.vm = vm; 2395 signal_handle.ufd.cb = uc_uloop_vm_signal_cb; 2396 signal_handle.ufd.fd = signal_fd; 2397 2398 uloop_fd_add(&signal_handle.ufd, ULOOP_READ); 2399 } 2400 } 2401
This page was automatically generated by LXR 0.3.1. • OpenWrt