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