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