1 /* 2 * Copyright (C) 2020-2021 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 * # Ubus IPC 19 * 20 * The `ubus` module provides functions for OpenWrt inter-process 21 * communication, including access to ubus registered modules and their 22 * methods, as well as monitoring and publish/subscribe activity on the 23 * ubus message bus. 24 * 25 * Functions can be individually imported using named import syntax: 26 * 27 * ```js 28 * import { connect } from 'ubus'; 29 * 30 * const ubus = connect(); 31 * const result = ubus.call("session", "get", { key: "value" }); 32 * ``` 33 * 34 * Alternatively, the module namespace can be imported using a wildcard 35 * import: 36 * 37 * ```js 38 * import * as ubus from 'ubus'; 39 * 40 * const ctx = ubus.connect(); 41 * ``` 42 * 43 * The `ubus` module may also be loaded via the `-lubus` interpreter switch. 44 * 45 * ## Architecture 46 * 47 * Ubus uses a broker pattern architecture with three main components: 48 * - **`ubusd`**: The central message router/broker that manages 49 * registrations and forwards messages between objects 50 * - **Server objects**: Interfaces/daemons that register methods for 51 * clients to call 52 * - **Client objects**: Callers that invoke server object methods 53 * 54 * All connections go through `ubusd`, significantly reducing the number 55 * of IPC connections compared to traditional client-server models. 56 * 57 * ## Communication Schemes 58 * 59 * Ubus provides three delivery schemes for IPC: 60 * 61 * 1. **Invoke** (one-to-one): Direct method calls to a specific object 62 * by ID 63 * 2. **Subscribe/Notify** (one-to-many, group by object): Notifications 64 * sent to all subscribers of a particular object 65 * 3. **Event Broadcast** (one-to-many, group by event): Events broadcast 66 * to all listeners registered for a matching event pattern 67 * 68 * ## Roles in Ubus 69 * 70 * - **Object**: Process registered to `ubusd`, including services and 71 * service callers 72 * - **Method**: Procedures provided by objects; servers can provide 73 * multiple methods 74 * - **Data**: Information in JSON format carried by requests or replies 75 * - **Subscriber**: Object subscribed to a target service; notified when 76 * the target sends notifications 77 * - **Event**: Identified by a string event pattern; objects can register 78 * to events and send data with matching patterns 79 * - **Event Registrant**: Object registered to an event pattern; receives 80 * forwarded data when matching messages are received 81 * 82 * ## Data Format 83 * 84 * All data is transferred in JSON format via `blobmsg`. Method calls, 85 * requests, and replies all use JSON for data serialization. 86 * 87 * ## Usage Examples 88 * 89 * ### Basic connection and method call 90 * ```js 91 * const ubus = require("ubus"); 92 * 93 * // Connect to ubus and call a method 94 * const conn = ubus.connect(); 95 * if (conn) { 96 * const result = conn.call("network.interface", "status", {}); 97 * printf("Interface status: %.J\n", result); 98 * conn.disconnect(); 99 * } 100 * ``` 101 * 102 * ### Asynchronous method invocation with callback 103 * ```js 104 * const ubus = require("ubus"); 105 * 106 * // Typical pattern: async call with callback 107 * const conn = ubus.connect(); 108 * 109 * conn.defer("some.object", "some_method", {}, (rc, result) => { 110 * if (rc == 0) { 111 * printf("Result: %.J\n", result); 112 * } 113 * }); 114 * ``` 115 * 116 * ### Persistent connection pattern 117 * ```js 118 * const ubus = require("ubus"); 119 * 120 * // Keep connection alive to prevent GC 121 * const ubus_conn = ubus.connect(); 122 * 123 * function handle_request(request) { 124 * ubus_conn.defer("some.object", "some_method", {}, (rc, data) => { 125 * request.reply({ result: data }); 126 * }); 127 * } 128 * ``` 129 * 130 * ### Publishing an object 131 * ```js 132 * const ubus = require("ubus"); 133 * 134 * const conn = ubus.connect(); 135 * const obj = conn.publish("my.service", { 136 * "hello": (req, msg) => { 137 * req.reply({ message: "Hello from " + msg.name }); 138 * } 139 * }); 140 * ``` 141 * 142 * ### Event broadcasting 143 * ```js 144 * const ubus = require("ubus"); 145 * 146 * const conn = ubus.connect(); 147 * 148 * // Register as event listener 149 * const listener = conn.listener("my.event.*", (pattern, data) => { 150 * printf("Received event: %s %.J\n", pattern, data); 151 * }); 152 * 153 * // Send an event 154 * conn.event("my.event.test", { data: "test payload" }); 155 * ``` 156 * 157 * @module ubus 158 * @see https://openwrt.org/docs/techref/ubus 159 */ 160 161 /** 162 * Represents a connection to the ubus bus. 163 * 164 * A connection is established via 165 * {@link module:ubus#connect|connect()} and serves as the primary 166 * interface for all ubus operations. Through a connection, you can: 167 * 168 * - Discover and invoke methods on remote objects 169 * ({@link module:ubus#list|list()}, 170 * {@link module:ubus#call|call()}) 171 * - Publish your own objects to the bus 172 * ({@link module:ubus#publish|publish()}) 173 * - Subscribe to notifications from other objects 174 * ({@link module:ubus#subscriber|subscriber()}) 175 * - Register event listeners for pattern-based events 176 * ({@link module:ubus#listener|listener()}) 177 * - Send broadcast events to other listeners 178 * ({@link module:ubus#event|event()}) 179 * 180 * The connection uses the broker pattern, routing all communication 181 * through ubusd. It supports both synchronous (call) and asynchronous 182 * (defer) method invocations. 183 * 184 * @class module:ubus.connection 185 * @hideconstructor 186 * 187 * @borrows module:ubus#error as module:ubus#error 188 * 189 * @see {@link module:ubus#connect|connect()} 190 * @see {@link module:ubus#open_channel|open_channel()} 191 * 192 * @example 193 * 194 * const conn = connect(); 195 * 196 * conn.list(); 197 * conn.call(…); 198 * conn.defer(…); 199 * conn.publish(…); 200 * conn.remove(…); 201 * conn.listener(…); 202 * conn.subscriber(…); 203 * conn.event(…); 204 * conn.disconnect(); 205 * 206 * conn.error(); 207 */ 208 209 /** 210 * Represents a channel connection to the ubus bus. 211 * 212 * Channels provide bidirectional communication between two ubus objects 213 * through file descriptors. They are created via 214 * {@link module:ubus#open_channel|open_channel()} or from an incoming 215 * request via 216 * {@link module:ubus.request#new_channel|new_channel()}. 217 * 218 * Channels are useful for: 219 * - Establishing dedicated communication paths between specific objects 220 * - Streaming data or multiple requests over a single connection 221 * - File descriptor passing between processes 222 * 223 * @class module:ubus.channel 224 * @hideconstructor 225 * 226 * @borrows module:ubus#error as module:ubus.channel#error 227 * 228 * @see {@link module:ubus#open_channel|open_channel()} 229 * @see {@link module:ubus.request#new_channel|new_channel()} 230 * 231 * @example 232 * 233 * const chan = open_channel(…); 234 * 235 * chan.request(…); 236 * chan.defer(…); 237 * chan.disconnect(); 238 * 239 * chan.error(); 240 */ 241 242 /** 243 * Represents a deferred ubus request. 244 * 245 * A deferred request is created when invoking a method asynchronously 246 * using {@link module:ubus#defer|defer()} or 247 * {@link module:ubus.channel#defer|defer()}. Instead of 248 * blocking and waiting for the result, the operation returns immediately 249 * with a deferred object that can be used to: 250 * 251 * - Check if the request has completed 252 * ({@link module:ubus.deferred#completed|completed()}) 253 * - Wait synchronously for completion 254 * ({@link module:ubus.deferred#await|await()}) 255 * - Abort the pending request if no longer needed 256 * ({@link module:ubus.deferred#abort|abort()}) 257 * 258 * This pattern is useful for: 259 * - Non-blocking operations in event-driven applications 260 * - Timeout handling and request cancellation 261 * - Concurrent execution of multiple ubus method calls 262 * 263 * @class module:ubus.deferred 264 * @hideconstructor 265 * 266 * @see {@link module:ubus#defer|defer()} 267 * @see {@link module:ubus.channel#defer|defer()} 268 * 269 * @example 270 * 271 * const req = defer(…); 272 * 273 * req.await(); 274 * req.completed(); 275 * req.abort(); 276 * 277 * @example 278 * // Typical async pattern with callback 279 * const req = conn.defer("system", "info", {}, (rc, data) => { 280 * if (rc == 0) 281 * printf("Info: %.J\n", data); 282 * }); 283 */ 284 285 /** 286 * Represents a ubus object published on the bus. 287 * 288 * A published object is a service registered with ubusd that provides 289 * methods for other processes to call. Objects are created via 290 * {@link module:ubus#publish|publish()} and can: 291 * 292 * - Expose multiple methods for remote invocation 293 * - Receive notifications from subscribers 294 * ({@link module:ubus.object#subscribed|subscribed()}, 295 * {@link module:ubus.object#notify|notify()}) 296 * - Be removed from the bus when no longer needed 297 * ({@link module:ubus.object#remove|remove()}) 298 * 299 * Objects are identified by their path (e.g., `system`, 300 * `network.interface`) and can be discovered by other processes using 301 * {@link module:ubus#list|list()}. 302 * 303 * @class module:ubus.object 304 * @hideconstructor 305 * 306 * @see {@link module:ubus#publish|publish()} 307 * @see {@link module:ubus#subscriber|subscriber()} 308 * 309 * @example 310 * 311 * const obj = publish(…, { … }); 312 * 313 * obj.subscribed(); 314 * obj.notify(…); 315 * obj.remove(); 316 */ 317 318 /** 319 * Represents a deferred ubus method call context. 320 * 321 * A request object is created when a published object method is invoked 322 * asynchronously. It provides the server-side interface for handling 323 * incoming method calls and sending responses. 324 * 325 * The request context allows the method handler to: 326 * - Send a successful reply with data 327 * ({@link module:ubus.request#reply|reply()}) 328 * - Report an error condition 329 * ({@link module:ubus.request#error|error()}) 330 * - Defer completion for asynchronous processing 331 * ({@link module:ubus.request#defer|defer()}) 332 * - Exchange file descriptors with the caller 333 * ({@link module:ubus.request#get_fd|get_fd()}, 334 * {@link module:ubus.request#set_fd|set_fd()}) 335 * - Establish channel-based communication 336 * ({@link module:ubus.request#new_channel|new_channel()}) 337 * 338 * This is used internally by the ubus module when a published object's 339 * method is called by a client. 340 * 341 * @class module:ubus.request 342 * @hideconstructor 343 * 344 * @see {@link module:ubus#publish|publish()} 345 * 346 * @example 347 * 348 * // Method handler receives request as second argument 349 * const obj = publish("my.service", { 350 * hello: (req, msg) => { 351 * req.reply({ message: "Hello" }); 352 * } 353 * }); 354 */ 355 356 /** 357 * Represents an asynchronous notification request. 358 * 359 * A notify object is created when sending a notification to subscribers 360 * via {@link module:ubus.object#notify|notify()}. It allows 361 * tracking the delivery status of the notification and provides the 362 * ability to abort if needed. 363 * 364 * Notifications are delivered to all subscribers of an object in the 365 * subscribe/notify communication scheme. The notify object provides 366 * non-blocking status checking and cancellation capabilities. 367 * 368 * @class module:ubus.notify 369 * @hideconstructor 370 * 371 * @see {@link module:ubus.object#notify|notify()} 372 * @see {@link module:ubus#subscriber|subscriber()} 373 * 374 * @example 375 * 376 * const n = notify(…); 377 * 378 * n.completed(); 379 * n.abort(); 380 */ 381 382 /** 383 * Represents an event listener for pattern-based events. 384 * 385 * A listener is registered via 386 * {@link module:ubus#listener|listener()} to receive events 387 * matching a specific pattern. Listeners are part of the event broadcast 388 * communication scheme, where events are sent to all registered listeners 389 * with matching event patterns. 390 * 391 * Event patterns support wildcards (e.g., `` `system.*` ``, 392 * `` `network.interface.*` ``) allowing flexible event routing and 393 * subscription. 394 * 395 * @class module:ubus.listener 396 * @hideconstructor 397 * 398 * @see {@link module:ubus#listener|listener()} 399 * @see {@link module:ubus#event|event()} 400 * 401 * @example 402 * 403 * const listener = listener("event.*", (pattern, data) => { … }); 404 * 405 * listener.remove(); 406 */ 407 408 /** 409 * Represents a subscriber to an object's notifications. 410 * 411 * A subscriber is registered via 412 * {@link module:ubus#subscriber|subscriber()} to receive 413 * notifications from a specific object. Subscribers are part of the 414 * subscribe/notify communication scheme, where the target object can send 415 * notifications that are delivered to all registered subscribers. 416 * 417 * When a subscriber is registered, the target object receives a 418 * notification about the new subscription. Similarly, when a subscriber 419 * is removed, the target object is notified of the unsubscription. 420 * 421 * @class module:ubus.subscriber 422 * @hideconstructor 423 * 424 * @see {@link module:ubus#subscriber|subscriber()} 425 * @see {@link module:ubus.object#notify|notify()} 426 * @see {@link module:ubus.object#subscribed|subscribed()} 427 * 428 * @example 429 * 430 * const sub = subscriber(objid, (method, data) => { … }); 431 * 432 * sub.subscribe(); 433 * sub.unsubscribe(); 434 * sub.remove(); 435 */ 436 437 #include <unistd.h> 438 #include <limits.h> 439 #include <fnmatch.h> 440 #include <libubus.h> 441 #include <libubox/blobmsg.h> 442 443 #include "ucode/module.h" 444 445 #define ok_return(expr) do { set_error(0, NULL); return (expr); } while(0) 446 #define err_return(err, ...) do { set_error(err, __VA_ARGS__); return NULL; } while(0) 447 #define errval_return(err, ...) do { set_error(err, __VA_ARGS__); return err; } while(0) 448 449 #define REQUIRED 0 450 #define OPTIONAL 1 451 #define NAMED 2 452 453 static struct { 454 enum ubus_msg_status code; 455 char *msg; 456 } last_error; 457 458 __attribute__((format(printf, 2, 3))) static void 459 set_error(int errcode, const char *fmt, ...) 460 { 461 va_list ap; 462 463 free(last_error.msg); 464 465 last_error.code = errcode; 466 last_error.msg = NULL; 467 468 if (fmt) { 469 va_start(ap, fmt); 470 xvasprintf(&last_error.msg, fmt, ap); 471 va_end(ap); 472 } 473 } 474 475 static char * 476 _arg_type(uc_type_t type) 477 { 478 switch (type) { 479 case UC_INTEGER: return "an integer value"; 480 case UC_BOOLEAN: return "a boolean value"; 481 case UC_STRING: return "a string value"; 482 case UC_DOUBLE: return "a double value"; 483 case UC_ARRAY: return "an array"; 484 case UC_OBJECT: return "an object"; 485 case UC_REGEXP: return "a regular expression"; 486 case UC_CLOSURE: return "a function"; 487 default: return "the expected type"; 488 } 489 } 490 491 static bool 492 _args_get(uc_vm_t *vm, bool named, size_t nargs, ...) 493 { 494 uc_value_t **ptr, *arg, *obj = NULL; 495 uc_type_t type, t; 496 const char *name; 497 size_t index = 0; 498 va_list ap; 499 int opt; 500 501 if (named) { 502 obj = uc_fn_arg(0); 503 504 if (nargs != 1 || ucv_type(obj) != UC_OBJECT) 505 named = false; 506 } 507 508 va_start(ap, nargs); 509 510 while (true) { 511 name = va_arg(ap, const char *); 512 513 if (!name) 514 break; 515 516 type = va_arg(ap, uc_type_t); 517 opt = va_arg(ap, int); 518 ptr = va_arg(ap, uc_value_t **); 519 520 if (named) 521 arg = ucv_object_get(obj, name, NULL); 522 else if (opt != NAMED) 523 arg = uc_fn_arg(index++); 524 else 525 arg = NULL; 526 527 if (opt == REQUIRED && !arg) 528 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Argument %s is required", name); 529 530 t = ucv_type(arg); 531 532 if (t == UC_CFUNCTION) 533 t = UC_CLOSURE; 534 535 if (arg && type && t != type) 536 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Argument %s is not %s", name, _arg_type(type)); 537 538 *ptr = arg; 539 } 540 541 va_end(ap); 542 543 ok_return(true); 544 } 545 546 #define args_get_named(vm, nargs, ...) do { if (!_args_get(vm, true, nargs, __VA_ARGS__, NULL)) return NULL; } while(0) 547 #define args_get(vm, nargs, ...) do { if (!_args_get(vm, false, nargs, __VA_ARGS__, NULL)) return NULL; } while(0) 548 549 static struct blob_buf buf; 550 551 typedef struct { 552 struct ubus_context ctx; 553 struct blob_buf buf; 554 int timeout; 555 bool fd_handle; 556 557 uc_vm_t *vm; 558 uc_value_t *res; 559 } uc_ubus_connection_t; 560 561 typedef struct { 562 struct ubus_request request; 563 struct uloop_timeout timeout; 564 struct ubus_context *ctx; 565 bool complete; 566 uc_vm_t *vm; 567 uc_value_t *res; 568 uc_value_t *fd_callback; 569 uc_value_t *response; 570 } uc_ubus_deferred_t; 571 572 typedef struct { 573 struct ubus_object obj; 574 struct ubus_object_type type; 575 struct ubus_context *ctx; 576 uc_vm_t *vm; 577 uc_value_t *res; 578 struct ubus_method methods[]; 579 } uc_ubus_object_t; 580 581 typedef struct { 582 struct ubus_request_data req; 583 struct uloop_timeout timeout; 584 struct ubus_context *ctx; 585 uc_value_t *res; 586 uc_vm_t *vm; 587 bool deferred; 588 bool replied; 589 } uc_ubus_request_t; 590 591 typedef struct { 592 struct ubus_notify_request req; 593 struct ubus_context *ctx; 594 uc_vm_t *vm; 595 uc_value_t *res; 596 bool complete; 597 } uc_ubus_notify_t; 598 599 typedef struct { 600 struct ubus_event_handler ev; 601 struct ubus_context *ctx; 602 uc_vm_t *vm; 603 uc_value_t *res; 604 } uc_ubus_listener_t; 605 606 typedef struct { 607 struct ubus_subscriber sub; 608 struct ubus_context *ctx; 609 uc_vm_t *vm; 610 uc_value_t *res; 611 } uc_ubus_subscriber_t; 612 613 typedef struct { 614 bool mret; 615 uc_value_t *res; 616 } uc_ubus_call_res_t; 617 618 /** 619 * Query ubus error information. 620 * 621 * Returns a string containing a description of the last ubus error when 622 * the *numeric* argument is absent or false. 623 * 624 * Returns a ubus status code number when the *numeric* argument is `true`. 625 * 626 * Returns `null` if there is no error information. 627 * 628 * @function module:ubus#error 629 * 630 * @param {boolean} [numeric] 631 * Whether to return a numeric status code (`true`) or a human readable 632 * error message (false). 633 * 634 * @returns {?string|?number} 635 */ 636 static uc_value_t * 637 uc_ubus_error(uc_vm_t *vm, size_t nargs) 638 { 639 uc_value_t *numeric = uc_fn_arg(0), *rv; 640 uc_stringbuf_t *buf; 641 const char *s; 642 643 if (last_error.code == 0) 644 return NULL; 645 646 if (ucv_is_truish(numeric)) { 647 rv = ucv_int64_new(last_error.code); 648 } 649 else { 650 buf = ucv_stringbuf_new(); 651 652 if (last_error.code == UBUS_STATUS_UNKNOWN_ERROR && last_error.msg) { 653 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg)); 654 } 655 else { 656 s = ubus_strerror(last_error.code); 657 658 ucv_stringbuf_addstr(buf, s, strlen(s)); 659 660 if (last_error.msg) 661 ucv_stringbuf_printf(buf, ": %s", last_error.msg); 662 } 663 664 rv = ucv_stringbuf_finish(buf); 665 } 666 667 set_error(0, NULL); 668 669 return rv; 670 } 671 672 static void 673 uc_ubus_put_res(uc_value_t **rp) 674 { 675 uc_value_t *res = *rp; 676 677 *rp = NULL; 678 ucv_resource_persistent_set(res, false); 679 ucv_put(res); 680 } 681 682 enum { 683 CONN_RES_FD, 684 CONN_RES_CB, 685 CONN_RES_DISCONNECT_CB, 686 __CONN_RES_MAX 687 }; 688 689 enum { 690 DEFER_RES_CONN, 691 DEFER_RES_CB, 692 DEFER_RES_DATA_CB, 693 DEFER_RES_FD_CB, 694 DEFER_RES_FD, 695 DEFER_RES_RESPONSE, 696 __DEFER_RES_MAX 697 }; 698 699 enum { 700 OBJ_RES_CONN, 701 OBJ_RES_METHODS, 702 OBJ_RES_SUB_CB, 703 __OBJ_RES_MAX 704 }; 705 706 enum { 707 NOTIFY_RES_CONN, 708 NOTIFY_RES_CB, 709 NOTIFY_RES_DATA_CB, 710 NOTIFY_RES_STATUS_CB, 711 __NOTIFY_RES_MAX, 712 }; 713 714 enum { 715 SUB_RES_NOTIFY_CB, 716 SUB_RES_REMOVE_CB, 717 SUB_RES_PATTERNS, 718 __SUB_RES_MAX, 719 }; 720 721 static uc_value_t * 722 blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name); 723 724 static uc_value_t * 725 blob_array_to_ucv(uc_vm_t *vm, struct blob_attr *attr, size_t len, bool table) 726 { 727 uc_value_t *o = table ? ucv_object_new(vm) : ucv_array_new(vm); 728 uc_value_t *v; 729 struct blob_attr *pos; 730 size_t rem = len; 731 const char *name; 732 733 if (!o) 734 return NULL; 735 736 __blob_for_each_attr(pos, attr, rem) { 737 name = NULL; 738 v = blob_to_ucv(vm, pos, table, &name); 739 740 if (table && name) 741 ucv_object_add(o, name, v); 742 else if (!table) 743 ucv_array_push(o, v); 744 else 745 ucv_put(v); 746 } 747 748 return o; 749 } 750 751 static uc_value_t * 752 blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name) 753 { 754 void *data; 755 int len; 756 757 if (!blobmsg_check_attr(attr, false)) 758 return NULL; 759 760 if (table && blobmsg_name(attr)[0]) 761 *name = blobmsg_name(attr); 762 763 data = blobmsg_data(attr); 764 len = blobmsg_data_len(attr); 765 766 switch (blob_id(attr)) { 767 case BLOBMSG_TYPE_BOOL: 768 return ucv_boolean_new(*(uint8_t *)data); 769 770 case BLOBMSG_TYPE_INT16: 771 return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data)); 772 773 case BLOBMSG_TYPE_INT32: 774 return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data)); 775 776 case BLOBMSG_TYPE_INT64: 777 return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data)); 778 779 case BLOBMSG_TYPE_DOUBLE: 780 ; 781 union { 782 double d; 783 uint64_t u64; 784 } v; 785 786 v.u64 = be64_to_cpu(*(uint64_t *)data); 787 788 return ucv_double_new(v.d); 789 790 case BLOBMSG_TYPE_STRING: 791 return ucv_string_new_length(data, len - 1); 792 793 case BLOBMSG_TYPE_ARRAY: 794 return blob_array_to_ucv(vm, data, len, false); 795 796 case BLOBMSG_TYPE_TABLE: 797 return blob_array_to_ucv(vm, data, len, true); 798 799 default: 800 return NULL; 801 } 802 } 803 804 static void 805 ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob); 806 807 static void 808 ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob); 809 810 static void 811 ucv_to_blob(const char *name, uc_value_t *val, struct blob_buf *blob) 812 { 813 int64_t n; 814 void *c; 815 816 switch (ucv_type(val)) { 817 case UC_NULL: 818 blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0); 819 break; 820 821 case UC_BOOLEAN: 822 blobmsg_add_u8(blob, name, ucv_boolean_get(val)); 823 break; 824 825 case UC_INTEGER: 826 n = ucv_int64_get(val); 827 828 if (errno == ERANGE) 829 blobmsg_add_u64(blob, name, ucv_uint64_get(val)); 830 else if (n >= INT32_MIN && n <= INT32_MAX) 831 blobmsg_add_u32(blob, name, n); 832 else 833 blobmsg_add_u64(blob, name, n); 834 835 break; 836 837 case UC_DOUBLE: 838 blobmsg_add_double(blob, name, ucv_double_get(val)); 839 break; 840 841 case UC_STRING: 842 blobmsg_add_field(blob, BLOBMSG_TYPE_STRING, name, 843 ucv_string_get(val), ucv_string_length(val) + 1); 844 break; 845 846 case UC_ARRAY: 847 c = blobmsg_open_array(blob, name); 848 ucv_array_to_blob(val, blob); 849 blobmsg_close_array(blob, c); 850 break; 851 852 case UC_OBJECT: 853 c = blobmsg_open_table(blob, name); 854 ucv_object_to_blob(val, blob); 855 blobmsg_close_table(blob, c); 856 break; 857 858 default: 859 break; 860 } 861 } 862 863 static void 864 ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob) 865 { 866 size_t i; 867 868 for (i = 0; i < ucv_array_length(val); i++) 869 ucv_to_blob(NULL, ucv_array_get(val, i), blob); 870 } 871 872 static void 873 ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob) 874 { 875 ucv_object_foreach(val, k, v) 876 ucv_to_blob(k, v, blob); 877 } 878 879 880 static uc_ubus_connection_t * 881 uc_ubus_conn_alloc(uc_vm_t *vm, uc_value_t *timeout, const char *type) 882 { 883 uc_ubus_connection_t *c = NULL; 884 uc_value_t *res; 885 886 res = ucv_resource_create_ex(vm, type, (void **)&c, __CONN_RES_MAX, sizeof(*c)); 887 if (!c) 888 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Out of memory"); 889 890 c->vm = vm; 891 c->res = res; 892 c->timeout = timeout ? ucv_int64_get(timeout) : 30; 893 if (c->timeout < 0) 894 c->timeout = 30; 895 896 return c; 897 } 898 899 /** 900 * Establish a connection to the ubus bus. 901 * 902 * Connects to the specified ubus socket path or the default socket if none 903 * is provided. 904 * 905 * Returns a connection resource on success. 906 * 907 * Returns `null` if the connection could not be established. 908 * 909 * @function module:ubus#connect 910 * 911 * @param {string} [socket] 912 * The path to the ubus socket to connect to. If omitted, the default socket 913 * path is used. 914 * 915 * @param {number} [timeout=30] 916 * The timeout in seconds to use for subsequent ubus operations. 917 * 918 * @returns {?module:ubus.connection} 919 */ 920 static uc_value_t * 921 uc_ubus_connect(uc_vm_t *vm, size_t nargs) 922 { 923 uc_value_t *socket, *timeout; 924 uc_ubus_connection_t *c; 925 926 args_get(vm, nargs, 927 "socket", UC_STRING, true, &socket, 928 "timeout", UC_INTEGER, true, &timeout); 929 930 c = uc_ubus_conn_alloc(vm, timeout, "ubus.connection"); 931 932 if (!c) 933 return NULL; 934 935 if (ubus_connect_ctx(&c->ctx, socket ? ucv_string_get(socket) : NULL)) { 936 ucv_put(c->res); 937 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Unable to connect to ubus socket"); 938 } 939 940 if (c->timeout < 0) 941 c->timeout = 30; 942 943 ubus_add_uloop(&c->ctx); 944 945 ok_return(ucv_get(c->res)); 946 } 947 948 static void 949 uc_ubus_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) 950 { 951 uc_value_t *arr = p; 952 uc_value_t *sig; 953 954 if (!o->signature) 955 return; 956 957 sig = blob_array_to_ucv(NULL, blob_data(o->signature), blob_len(o->signature), true); 958 959 if (sig) 960 ucv_array_push(arr, sig); 961 } 962 963 static void 964 uc_ubus_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) 965 { 966 uc_value_t *arr = p; 967 968 ucv_array_push(arr, ucv_string_new(o->path)); 969 } 970 971 static bool 972 _conn_get(uc_vm_t *vm, uc_ubus_connection_t **conn) 973 { 974 uc_ubus_connection_t *c; 975 uc_value_t *res; 976 977 if (ucv_type(_uc_fn_this_res(vm)) == UC_OBJECT) { 978 res = uc_vm_registry_get(vm, "ubus.connection"); 979 c = ucv_resource_data(res, "ubus.connection"); 980 981 if (c && c->ctx.sock.fd >= 0) 982 goto out; 983 984 c = uc_ubus_conn_alloc(vm, NULL, "ubus.connection"); 985 if (!c) 986 return NULL; 987 988 if (ubus_connect_ctx(&c->ctx, NULL)) { 989 ucv_put(c->res); 990 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Unable to connect to ubus socket"); 991 } 992 993 ubus_add_uloop(&c->ctx); 994 995 uc_vm_registry_set(vm, "ubus.connection", ucv_get(c->res)); 996 } 997 else { 998 c = uc_fn_thisval("ubus.connection"); 999 if (!c) 1000 c = uc_fn_thisval("ubus.channel"); 1001 1002 if (!c) 1003 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid connection context"); 1004 1005 if (c->ctx.sock.fd < 0) 1006 err_return(UBUS_STATUS_CONNECTION_FAILED, "Connection is closed"); 1007 } 1008 1009 out: 1010 *conn = c; 1011 1012 ok_return(true); 1013 } 1014 1015 #define conn_get(vm, ptr) do { if (!_conn_get(vm, ptr)) return NULL; } while(0) 1016 1017 /** 1018 * List available ubus objects. 1019 * 1020 * Queries the ubus bus for registered objects. If an object name pattern 1021 * is provided, returns signatures for matching objects. Otherwise, returns 1022 * a list of all registered object paths. 1023 * 1024 * Returns an array of object paths or object signatures. 1025 * 1026 * Returns `null` if the list operation failed. 1027 * 1028 * @function module:ubus.connection#list 1029 * 1030 * @param {string} [object_name] 1031 * Optional object name pattern to filter results. When provided, returns 1032 * signatures for matching objects; otherwise returns all object paths. 1033 * 1034 * @returns {?string[]} 1035 */ 1036 static uc_value_t * 1037 uc_ubus_list(uc_vm_t *vm, size_t nargs) 1038 { 1039 uc_ubus_connection_t *c; 1040 uc_value_t *objname, *res = NULL; 1041 enum ubus_msg_status rv; 1042 1043 conn_get(vm, &c); 1044 1045 args_get(vm, nargs, 1046 "object name", UC_STRING, true, &objname); 1047 1048 res = ucv_array_new(vm); 1049 1050 rv = ubus_lookup(&c->ctx, 1051 objname ? ucv_string_get(objname) : NULL, 1052 objname ? uc_ubus_signatures_cb : uc_ubus_objects_cb, 1053 res); 1054 1055 if (rv != UBUS_STATUS_OK) { 1056 ucv_put(res); 1057 err_return(rv, NULL); 1058 } 1059 1060 ok_return(res); 1061 } 1062 1063 static void 1064 uc_ubus_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) 1065 { 1066 uc_ubus_call_res_t *res = req->priv; 1067 uc_value_t *val; 1068 1069 val = msg ? blob_array_to_ucv(NULL, blob_data(msg), blob_len(msg), true) : NULL; 1070 1071 if (res->mret) { 1072 if (!res->res) 1073 res->res = ucv_array_new(NULL); 1074 1075 ucv_array_push(res->res, val); 1076 } 1077 else if (!res->res) { 1078 res->res = val; 1079 } 1080 } 1081 1082 static void 1083 uc_ubus_vm_handle_exception(uc_vm_t *vm) 1084 { 1085 uc_value_t *exh, *val; 1086 1087 exh = uc_vm_registry_get(vm, "ubus.ex_handler"); 1088 if (!ucv_is_callable(exh)) 1089 goto error; 1090 1091 val = uc_vm_exception_object(vm); 1092 uc_vm_stack_push(vm, ucv_get(exh)); 1093 uc_vm_stack_push(vm, val); 1094 1095 if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE) 1096 goto error; 1097 1098 ucv_put(uc_vm_stack_pop(vm)); 1099 return; 1100 1101 error: 1102 uloop_end(); 1103 } 1104 1105 static bool 1106 uc_ubus_vm_call(uc_vm_t *vm, bool mcall, size_t nargs) 1107 { 1108 if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE) 1109 return true; 1110 1111 uc_ubus_vm_handle_exception(vm); 1112 1113 return false; 1114 } 1115 1116 static void 1117 uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply) 1118 { 1119 uc_value_t *this = ucv_get(defer->res); 1120 uc_vm_t *vm = defer->vm; 1121 uc_value_t *func; 1122 1123 func = ucv_resource_value_get(this, DEFER_RES_CB); 1124 1125 if (ucv_is_callable(func)) { 1126 uc_vm_stack_push(vm, ucv_get(this)); 1127 uc_vm_stack_push(vm, ucv_get(func)); 1128 uc_vm_stack_push(vm, ucv_int64_new(ret)); 1129 uc_vm_stack_push(vm, ucv_get(reply)); 1130 1131 if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) 1132 ucv_put(uc_vm_stack_pop(vm)); 1133 } 1134 1135 uc_ubus_put_res(&defer->res); 1136 ucv_put(this); 1137 } 1138 1139 static void 1140 uc_ubus_call_data_cb(struct ubus_request *req, int type, struct blob_attr *msg) 1141 { 1142 uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); 1143 1144 if (defer->response != NULL) 1145 return; 1146 1147 defer->response = blob_array_to_ucv(defer->vm, blob_data(msg), blob_len(msg), true); 1148 ucv_resource_value_set(defer->res, DEFER_RES_RESPONSE, defer->response); 1149 } 1150 1151 static void 1152 uc_ubus_call_data_user_cb(struct ubus_request *req, int type, struct blob_attr *msg) 1153 { 1154 uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); 1155 uc_vm_t *vm = defer->vm; 1156 uc_value_t *func, *reply; 1157 1158 func = ucv_resource_value_get(defer->res, DEFER_RES_DATA_CB); 1159 1160 if (ucv_is_callable(func)) { 1161 reply = blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true); 1162 1163 uc_vm_stack_push(vm, ucv_get(defer->res)); 1164 uc_vm_stack_push(vm, ucv_get(func)); 1165 uc_vm_stack_push(vm, ucv_get(reply)); 1166 1167 if (uc_ubus_vm_call(vm, true, 1)) 1168 ucv_put(uc_vm_stack_pop(vm)); 1169 } 1170 } 1171 1172 static void 1173 uc_ubus_call_fd_cb(struct ubus_request *req, int fd) 1174 { 1175 uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); 1176 uc_value_t *func = defer->fd_callback; 1177 uc_vm_t *vm = defer->vm; 1178 1179 if (defer->complete) 1180 return; 1181 1182 if (ucv_is_callable(func)) { 1183 uc_vm_stack_push(vm, ucv_get(defer->res)); 1184 uc_vm_stack_push(vm, ucv_get(func)); 1185 uc_vm_stack_push(vm, ucv_int64_new(fd)); 1186 1187 if (uc_ubus_vm_call(vm, true, 1)) 1188 ucv_put(uc_vm_stack_pop(vm)); 1189 } 1190 } 1191 1192 static void 1193 uc_ubus_call_done_cb(struct ubus_request *req, int ret) 1194 { 1195 uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); 1196 1197 if (defer->complete) 1198 return; 1199 1200 defer->complete = true; 1201 uloop_timeout_cancel(&defer->timeout); 1202 1203 uc_ubus_call_user_cb(defer, ret, defer->response); 1204 } 1205 1206 static void 1207 uc_ubus_call_timeout_cb(struct uloop_timeout *timeout) 1208 { 1209 uc_ubus_deferred_t *defer = container_of(timeout, uc_ubus_deferred_t, timeout); 1210 1211 if (defer->complete) 1212 return; 1213 1214 defer->complete = true; 1215 ubus_abort_request(defer->ctx, &defer->request); 1216 1217 uc_ubus_call_user_cb(defer, UBUS_STATUS_TIMEOUT, NULL); 1218 } 1219 1220 static int 1221 get_fd(uc_vm_t *vm, uc_value_t *val, bool *handle) 1222 { 1223 uc_value_t *fn; 1224 int64_t n; 1225 1226 fn = ucv_property_get(val, "fileno"); 1227 1228 if (ucv_is_callable(fn)) { 1229 if (handle) 1230 *handle = true; 1231 1232 uc_vm_stack_push(vm, ucv_get(val)); 1233 uc_vm_stack_push(vm, ucv_get(fn)); 1234 1235 if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) 1236 return -1; 1237 1238 val = uc_vm_stack_pop(vm); 1239 n = ucv_int64_get(val); 1240 ucv_put(val); 1241 } 1242 else { 1243 n = ucv_int64_get(val); 1244 } 1245 1246 if (errno || n < 0 || n > (int64_t)INT_MAX) 1247 return -1; 1248 1249 return (int)n; 1250 } 1251 1252 static int 1253 uc_ubus_call_common(uc_vm_t *vm, uc_ubus_connection_t *c, uc_ubus_call_res_t *res, 1254 uint32_t id, uc_value_t *funname, uc_value_t *funargs, 1255 uc_value_t *fd, uc_value_t *fdcb, uc_value_t *mret) 1256 { 1257 uc_ubus_deferred_t defer = {}; 1258 enum ubus_msg_status rv; 1259 int fd_val = -1; 1260 1261 enum { 1262 RET_MODE_SINGLE, 1263 RET_MODE_MULTIPLE, 1264 RET_MODE_IGNORE, 1265 } ret_mode = RET_MODE_SINGLE; 1266 1267 const char * const ret_modes[] = { 1268 [RET_MODE_SINGLE] = "single", 1269 [RET_MODE_MULTIPLE] = "multiple", 1270 [RET_MODE_IGNORE] = "ignore", 1271 }; 1272 1273 if (ucv_type(mret) == UC_STRING) { 1274 const char *str = ucv_string_get(mret); 1275 size_t i; 1276 1277 for (i = 0; i < ARRAY_SIZE(ret_modes); i++) 1278 if (!strcmp(str, ret_modes[i])) 1279 break; 1280 1281 if (i == ARRAY_SIZE(ret_modes)) 1282 errval_return(UBUS_STATUS_INVALID_ARGUMENT, 1283 "Invalid return mode argument"); 1284 1285 ret_mode = i; 1286 } 1287 else if (ucv_type(mret) == UC_BOOLEAN) { 1288 ret_mode = ucv_boolean_get(mret); 1289 } 1290 else if (ret_mode) { 1291 errval_return(UBUS_STATUS_INVALID_ARGUMENT, 1292 "Invalid return mode argument"); 1293 } 1294 1295 blob_buf_init(&c->buf, 0); 1296 1297 if (funargs) 1298 ucv_object_to_blob(funargs, &c->buf); 1299 1300 if (fd) { 1301 fd_val = get_fd(vm, fd, NULL); 1302 1303 if (fd_val < 0) 1304 errval_return(UBUS_STATUS_INVALID_ARGUMENT, 1305 "Invalid file descriptor argument"); 1306 } 1307 1308 res->mret = (ret_mode == RET_MODE_MULTIPLE); 1309 1310 rv = ubus_invoke_async_fd(&c->ctx, id, ucv_string_get(funname), 1311 c->buf.head, &defer.request, fd_val); 1312 1313 defer.vm = vm; 1314 defer.ctx = &c->ctx; 1315 defer.request.data_cb = uc_ubus_call_cb; 1316 defer.request.priv = res; 1317 1318 if (ucv_is_callable(fdcb)) { 1319 defer.request.fd_cb = uc_ubus_call_fd_cb; 1320 defer.fd_callback = fdcb; 1321 } 1322 1323 if (rv == UBUS_STATUS_OK) { 1324 if (ret_mode == RET_MODE_IGNORE) 1325 ubus_abort_request(&c->ctx, &defer.request); 1326 else 1327 rv = ubus_complete_request(&c->ctx, &defer.request, c->timeout * 1000); 1328 } 1329 1330 return rv; 1331 } 1332 1333 /** 1334 * Invoke a ubus method synchronously. 1335 * 1336 * Calls the specified method on a ubus object and waits for the response. 1337 * 1338 * Returns the method response data, or an array of responses if multiple 1339 * replies were received. 1340 * 1341 * Returns `null` if the call failed. 1342 * 1343 * @function module:ubus.connection#call 1344 * 1345 * @param {string|number} object 1346 * The object name (string) or object ID (number) to call the method on. 1347 * 1348 * @param {string} method 1349 * The name of the method to invoke. 1350 * 1351 * @param {Object} [data] 1352 * Optional method arguments as an object with field names and values. 1353 * 1354 * @param {string|boolean} [return="single"] 1355 * Controls how multiple responses are handled: `"single"` returns only 1356 * the first response, `"multiple"` returns an array of all responses, 1357 * `"ignore"` discards the response. 1358 * 1359 * @param {number} [fd] 1360 * Optional file descriptor to send along with the call. 1361 * 1362 * @param {function} [fd_cb] 1363 * Optional callback function invoked when a file descriptor is received. 1364 * 1365 * @returns {?*} 1366 */ 1367 static uc_value_t * 1368 uc_ubus_call(uc_vm_t *vm, size_t nargs) 1369 { 1370 uc_value_t *obj, *funname, *funargs, *fd, *fdcb, *mret = NULL; 1371 uc_ubus_call_res_t res = { 0 }; 1372 uc_ubus_connection_t *c; 1373 enum ubus_msg_status rv; 1374 uint32_t id; 1375 1376 args_get_named(vm, nargs, 1377 "object", 0, REQUIRED, &obj, 1378 "method", UC_STRING, REQUIRED, &funname, 1379 "data", UC_OBJECT, OPTIONAL, &funargs, 1380 "return", 0, OPTIONAL, &mret, 1381 "fd", 0, NAMED, &fd, 1382 "fd_cb", UC_CLOSURE, NAMED, &fdcb); 1383 1384 conn_get(vm, &c); 1385 1386 if (ucv_type(obj) == UC_INTEGER) { 1387 id = ucv_int64_get(obj); 1388 } 1389 else if (ucv_type(obj) == UC_STRING) { 1390 rv = ubus_lookup_id(&c->ctx, ucv_string_get(obj), &id); 1391 1392 if (rv != UBUS_STATUS_OK) 1393 err_return(rv, "Failed to resolve object name '%s'", 1394 ucv_string_get(obj)); 1395 } 1396 else { 1397 err_return(UBUS_STATUS_INVALID_ARGUMENT, 1398 "Argument object is not string or integer"); 1399 } 1400 1401 rv = uc_ubus_call_common(vm, c, &res, id, funname, funargs, fd, fdcb, mret); 1402 1403 if (rv != UBUS_STATUS_OK) { 1404 if (ucv_type(obj) == UC_STRING) 1405 err_return(rv, "Failed to invoke function '%s' on object '%s'", 1406 ucv_string_get(funname), ucv_string_get(obj)); 1407 else 1408 err_return(rv, "Failed to invoke function '%s' on system object %d", 1409 ucv_string_get(funname), (int)ucv_int64_get(obj)); 1410 } 1411 1412 ok_return(res.res); 1413 } 1414 1415 /** 1416 * Send a request on a channel connection. 1417 * 1418 * Similar to call() but uses object ID 0 for channel-based communication. 1419 * 1420 * Returns the method response data. 1421 * 1422 * Returns `null` if the request failed. 1423 * 1424 * @function module:ubus.channel#request 1425 * 1426 * @param {string} method 1427 * The name of the method to invoke. 1428 * 1429 * @param {Object} [data] 1430 * Optional method arguments as an object with field names and values. 1431 * 1432 * @param {string|boolean} [return="single"] 1433 * Controls how multiple responses are handled. 1434 * 1435 * @param {number} [fd] 1436 * Optional file descriptor to send along with the request. 1437 * 1438 * @param {function} [fd_cb] 1439 * Optional callback function invoked when a file descriptor is received. 1440 * 1441 * @returns {?*} 1442 * 1443 * @example 1444 * const chan = open_channel(…); 1445 * const result = chan.request("method_name", { arg: "value" }); 1446 */ 1447 static uc_value_t * 1448 uc_ubus_chan_request(uc_vm_t *vm, size_t nargs) 1449 { 1450 uc_value_t *funname, *funargs, *fd, *fdcb, *mret = NULL; 1451 uc_ubus_call_res_t res = { 0 }; 1452 uc_ubus_connection_t *c; 1453 enum ubus_msg_status rv; 1454 1455 args_get_named(vm, nargs, 1456 "method", UC_STRING, REQUIRED, &funname, 1457 "data", UC_OBJECT, OPTIONAL, &funargs, 1458 "return", 0, OPTIONAL, &mret, 1459 "fd", 0, NAMED, &fd, 1460 "fd_cb", UC_CLOSURE, NAMED, &fdcb); 1461 1462 conn_get(vm, &c); 1463 1464 rv = uc_ubus_call_common(vm, c, &res, 0, funname, funargs, fd, fdcb, mret); 1465 1466 if (rv != UBUS_STATUS_OK) 1467 err_return(rv, "Failed to send request '%s' on channel", 1468 ucv_string_get(funname)); 1469 1470 ok_return(res.res); 1471 } 1472 1473 static int 1474 uc_ubus_defer_common(uc_vm_t *vm, uc_ubus_connection_t *c, uc_ubus_call_res_t *res, 1475 uint32_t id, uc_value_t *funname, uc_value_t *funargs, 1476 uc_value_t *fd, uc_value_t *fdcb, uc_value_t *replycb, 1477 uc_value_t *datacb) 1478 { 1479 uc_ubus_deferred_t *defer = NULL; 1480 enum ubus_msg_status rv; 1481 int fd_val = -1; 1482 1483 blob_buf_init(&c->buf, 0); 1484 1485 if (funargs) 1486 ucv_object_to_blob(funargs, &c->buf); 1487 1488 if (fd) { 1489 fd_val = get_fd(vm, fd, NULL); 1490 1491 if (fd_val < 0) 1492 errval_return(UBUS_STATUS_INVALID_ARGUMENT, 1493 "Invalid file descriptor argument"); 1494 } 1495 1496 res->res = ucv_resource_create_ex(vm, "ubus.deferred", (void **)&defer, __DEFER_RES_MAX, sizeof(*defer)); 1497 1498 if (!defer) 1499 errval_return(UBUS_STATUS_UNKNOWN_ERROR, "Out of memory"); 1500 1501 rv = ubus_invoke_async_fd(&c->ctx, id, ucv_string_get(funname), 1502 c->buf.head, &defer->request, fd_val); 1503 1504 if (rv == UBUS_STATUS_OK) { 1505 defer->vm = vm; 1506 defer->ctx = &c->ctx; 1507 defer->res = ucv_get(res->res); 1508 ucv_resource_persistent_set(defer->res, true); 1509 ucv_resource_value_set(defer->res, DEFER_RES_CONN, ucv_get(c->res)); 1510 ucv_resource_value_set(defer->res, DEFER_RES_CB, ucv_get(replycb)); 1511 ucv_resource_value_set(defer->res, DEFER_RES_FD, ucv_get(fd)); 1512 ucv_resource_value_set(defer->res, DEFER_RES_DATA_CB, ucv_get(datacb)); 1513 1514 if (ucv_is_callable(datacb)) 1515 defer->request.data_cb = uc_ubus_call_data_user_cb; 1516 else 1517 defer->request.data_cb = uc_ubus_call_data_cb; 1518 1519 if (ucv_is_callable(fdcb)) { 1520 defer->request.fd_cb = uc_ubus_call_fd_cb; 1521 defer->fd_callback = fdcb; 1522 ucv_resource_value_set(defer->res, DEFER_RES_FD_CB, ucv_get(fdcb)); 1523 } 1524 1525 defer->request.complete_cb = uc_ubus_call_done_cb; 1526 1527 ubus_complete_request_async(&c->ctx, &defer->request); 1528 1529 defer->timeout.cb = uc_ubus_call_timeout_cb; 1530 uloop_timeout_set(&defer->timeout, c->timeout * 1000); 1531 } 1532 else { 1533 uc_vm_stack_push(vm, ucv_get(replycb)); 1534 uc_vm_stack_push(vm, ucv_int64_new(rv)); 1535 1536 if (uc_ubus_vm_call(vm, false, 1)) 1537 ucv_put(uc_vm_stack_pop(vm)); 1538 1539 ucv_put(res->res); 1540 } 1541 1542 return rv; 1543 } 1544 1545 /** 1546 * Invoke a ubus method asynchronously. 1547 * 1548 * Initiates a non-blocking call to the specified method on a ubus object. 1549 * The provided callback will be invoked when the response is received or on 1550 * timeout. 1551 * 1552 * Returns a deferred request resource representing the pending operation. 1553 * 1554 * Returns `null` if the deferred call could not be initiated. 1555 * 1556 * @function module:ubus.connection#defer 1557 * 1558 * @param {string} object 1559 * The object name to call the method on. 1560 * 1561 * @param {string} method 1562 * The name of the method to invoke. 1563 * 1564 * @param {Object} [data] 1565 * Optional method arguments as an object with field names and values. 1566 * 1567 * @param {function} [cb] 1568 * Callback function invoked when the operation completes. Receives status 1569 * code and response data as arguments. 1570 * 1571 * @param {function} [data_cb] 1572 * Optional callback invoked for intermediate data notifications. 1573 * 1574 * @param {number} [fd] 1575 * Optional file descriptor to send along with the call. 1576 * 1577 * @param {function} [fd_cb] 1578 * Optional callback function invoked when a file descriptor is received. 1579 * 1580 * @returns {?module:ubus.deferred} 1581 * 1582 * @example 1583 * // Asynchronous call with callback - typical pattern for RPC handlers 1584 * const conn = connect(); 1585 * 1586 * const req = conn.defer("some.object", "some_method", {}, (rc, data) => { 1587 * if (rc == 0) 1588 * printf("Result: %.J\n", data); 1589 * }); 1590 * 1591 * @example 1592 * // Persistent connection pattern - avoid GC by keeping reference 1593 * const ubus = connect(); 1594 * 1595 * function get_status(req) { 1596 * // Use persistent ubus connection for async calls 1597 * return ubus.defer("some.object", "some_method", {}, (rc, data) => { 1598 * req.reply({ result: data }); 1599 * }); 1600 * } 1601 */ 1602 static uc_value_t * 1603 uc_ubus_defer(uc_vm_t *vm, size_t nargs) 1604 { 1605 uc_value_t *objname, *funname, *funargs, *replycb, *datacb, *fd, *fdcb = NULL; 1606 uc_ubus_call_res_t res = { 0 }; 1607 uc_ubus_connection_t *c; 1608 uint32_t id; 1609 int rv; 1610 1611 conn_get(vm, &c); 1612 1613 args_get_named(vm, nargs, 1614 "object", UC_STRING, REQUIRED, &objname, 1615 "method", UC_STRING, REQUIRED, &funname, 1616 "data", UC_OBJECT, OPTIONAL, &funargs, 1617 "cb", UC_CLOSURE, OPTIONAL, &replycb, 1618 "data_cb", UC_CLOSURE, OPTIONAL, &datacb, 1619 "fd", 0, NAMED, &fd, 1620 "fd_cb", UC_CLOSURE, NAMED, &fdcb); 1621 1622 rv = ubus_lookup_id(&c->ctx, ucv_string_get(objname), &id); 1623 1624 if (rv != UBUS_STATUS_OK) 1625 err_return(rv, "Failed to resolve object name '%s'", 1626 ucv_string_get(objname)); 1627 1628 rv = uc_ubus_defer_common(vm, c, &res, id, funname, funargs, fd, fdcb, replycb, datacb); 1629 1630 if (rv != UBUS_STATUS_OK) 1631 err_return(rv, "Failed to invoke function '%s' on object '%s'", 1632 ucv_string_get(funname), ucv_string_get(objname)); 1633 1634 ok_return(res.res); 1635 } 1636 1637 /** 1638 * Send an asynchronous request on a channel connection. 1639 * 1640 * Similar to defer() but uses object ID 0 for channel-based communication. 1641 * 1642 * Returns a deferred request resource representing the pending operation. 1643 * 1644 * Returns `null` if the deferred call could not be initiated. 1645 * 1646 * @function module:ubus.channel#defer 1647 * 1648 * @param {string} method 1649 * The name of the method to invoke. 1650 * 1651 * @param {Object} [data] 1652 * Optional method arguments as an object with field names and values. 1653 * 1654 * @param {function} [cb] 1655 * Callback function invoked when the operation completes. 1656 * 1657 * @param {function} [data_cb] 1658 * Optional callback invoked for intermediate data notifications. 1659 * 1660 * @param {number} [fd] 1661 * Optional file descriptor to send along with the request. 1662 * 1663 * @param {function} [fd_cb] 1664 * Optional callback function invoked when a file descriptor is received. 1665 * 1666 * @returns {?module:ubus.deferred} 1667 * 1668 * @example 1669 * const chan = open_channel(…); 1670 * const req = chan.defer("method_name", { arg: "value" }, 1671 * (status, data) => { 1672 * printf("Status: %d\n", status); 1673 * }); 1674 */ 1675 static uc_value_t * 1676 uc_ubus_chan_defer(uc_vm_t *vm, size_t nargs) 1677 { 1678 uc_value_t *funname, *funargs, *replycb, *datacb, *fd, *fdcb = NULL; 1679 uc_ubus_call_res_t res = { 0 }; 1680 uc_ubus_connection_t *c; 1681 int rv; 1682 1683 conn_get(vm, &c); 1684 1685 args_get_named(vm, nargs, 1686 "method", UC_STRING, REQUIRED, &funname, 1687 "data", UC_OBJECT, OPTIONAL, &funargs, 1688 "cb", UC_CLOSURE, OPTIONAL, &replycb, 1689 "data_cb", UC_CLOSURE, OPTIONAL, &datacb, 1690 "fd", 0, NAMED, &fd, 1691 "fd_cb", UC_CLOSURE, NAMED, &fdcb); 1692 1693 rv = uc_ubus_defer_common(vm, c, &res, 0, funname, funargs, fd, fdcb, replycb, datacb); 1694 1695 if (rv != UBUS_STATUS_OK) 1696 err_return(rv, "Failed to invoke function '%s' on channel", 1697 ucv_string_get(funname)); 1698 1699 ok_return(res.res); 1700 } 1701 1702 1703 /* 1704 * ubus object request context functions 1705 * -------------------------------------------------------------------------- 1706 */ 1707 1708 static void 1709 uc_ubus_request_finish_common(uc_ubus_request_t *callctx, int code) 1710 { 1711 int fd; 1712 1713 fd = ubus_request_get_caller_fd(&callctx->req); 1714 1715 if (fd >= 0) 1716 close(fd); 1717 1718 callctx->replied = true; 1719 uloop_timeout_cancel(&callctx->timeout); 1720 ubus_complete_deferred_request(callctx->ctx, &callctx->req, code); 1721 } 1722 1723 static void 1724 uc_ubus_request_send_reply(uc_ubus_request_t *callctx, uc_value_t *reply) 1725 { 1726 if (!reply) 1727 return; 1728 1729 blob_buf_init(&buf, 0); 1730 ucv_object_to_blob(reply, &buf); 1731 ubus_send_reply(callctx->ctx, &callctx->req, buf.head); 1732 } 1733 1734 static void 1735 uc_ubus_request_finish(uc_ubus_request_t *callctx, int code) 1736 { 1737 if (callctx->replied) 1738 return; 1739 1740 uc_ubus_request_finish_common(callctx, code); 1741 uc_ubus_put_res(&callctx->res); 1742 } 1743 1744 static void 1745 uc_ubus_request_timeout(struct uloop_timeout *timeout) 1746 { 1747 uc_ubus_request_t *callctx = container_of(timeout, uc_ubus_request_t, timeout); 1748 1749 uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT); 1750 } 1751 1752 /** 1753 * Send a reply to a deferred ubus method call. 1754 * 1755 * Sends the specified reply data to the caller of a deferred method 1756 * request. After sending the reply, the request context is finished unless 1757 * `more` is set to `true`. 1758 * 1759 * Returns `true` on success. 1760 * 1761 * Returns `null` if an error occurred or the reply was already sent. 1762 * 1763 * @function module:ubus.request#reply 1764 * 1765 * @param {Object} [reply] 1766 * The reply data to send as an object with field names and values. 1767 * 1768 * @param {number} [rcode=0] 1769 * Optional status code to return. Use negative values to indicate more 1770 * replies will follow. 1771 * 1772 * @returns {?boolean} 1773 * 1774 * @example 1775 * // In a published object method handler 1776 * publish("my.service", { 1777 * "hello": (req, msg) => { 1778 * req.reply({ message: "Hello, " + msg.name }); 1779 * } 1780 * }); 1781 */ 1782 static uc_value_t * 1783 uc_ubus_request_reply(uc_vm_t *vm, size_t nargs) 1784 { 1785 uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request"); 1786 int64_t code = UBUS_STATUS_OK; 1787 uc_value_t *reply, *rcode; 1788 bool more = false; 1789 1790 if (!callctx) 1791 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid call context"); 1792 1793 args_get(vm, nargs, 1794 "reply", UC_OBJECT, true, &reply, 1795 "rcode", UC_INTEGER, true, &rcode); 1796 1797 if (callctx->replied) 1798 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Reply has already been sent"); 1799 1800 if (rcode) { 1801 code = ucv_int64_get(rcode); 1802 1803 if (errno == ERANGE || code < -1 || code > __UBUS_STATUS_LAST) 1804 code = UBUS_STATUS_UNKNOWN_ERROR; 1805 1806 if (code < 0) 1807 more = true; 1808 } 1809 1810 uc_ubus_request_send_reply(callctx, reply); 1811 1812 if (!more) 1813 uc_ubus_request_finish(callctx, code); 1814 1815 ok_return(ucv_boolean_new(true)); 1816 } 1817 1818 /** 1819 * Defer completion of a ubus method call. 1820 * 1821 * Marks the current request as deferred, allowing the handler to complete 1822 * the request asynchronously at a later time by calling reply(). 1823 * 1824 * Returns `true` on success. 1825 * 1826 * Returns `null` if an error occurred. 1827 * 1828 * @function module:ubus.request#defer 1829 * 1830 * @returns {?boolean} 1831 * 1832 * @example 1833 * // In a published object method handler 1834 * publish("my.service", { 1835 * "async_method": (req, msg) => { 1836 * req.defer(); 1837 * // Do async work... 1838 * req.reply({ result: "done" }); 1839 * } 1840 * }); 1841 */ 1842 static uc_value_t * 1843 uc_ubus_request_defer(uc_vm_t *vm, size_t nargs) 1844 { 1845 uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request"); 1846 1847 if (!callctx) 1848 return NULL; 1849 1850 callctx->deferred = true; 1851 return ucv_boolean_new(true); 1852 } 1853 1854 /** 1855 * Get the caller's file descriptor from a ubus method call. 1856 * 1857 * Returns the UNIX file descriptor number that was passed by the caller, 1858 * or -1 if no file descriptor was sent. 1859 * 1860 * @function module:ubus.request#get_fd 1861 * 1862 * @returns {number} 1863 * The file descriptor number, or -1 if none was provided. 1864 */ 1865 static uc_value_t * 1866 uc_ubus_request_get_fd(uc_vm_t *vm, size_t nargs) 1867 { 1868 uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request"); 1869 1870 if (!callctx) 1871 return NULL; 1872 1873 return ucv_int64_new(ubus_request_get_caller_fd(&callctx->req)); 1874 } 1875 1876 /** 1877 * Set a file descriptor to send with a ubus method reply. 1878 * 1879 * Associates a file descriptor with the current request to be sent back to 1880 * the caller along with the reply. 1881 * 1882 * Returns `true` on success. 1883 * 1884 * Returns `null` if an error occurred. 1885 * 1886 * @function module:ubus.request#set_fd 1887 * 1888 * @param {number} fd 1889 * The file descriptor number to send. 1890 * 1891 * @returns {?boolean} 1892 * 1893 * @example 1894 * // In a published object method handler 1895 * publish("my.service", { 1896 * "get_fd": (req, msg) => { 1897 * let fd = some_file_descriptor; 1898 * req.set_fd(fd); 1899 * req.reply({ info: "fd sent" }); 1900 * } 1901 * }); 1902 */ 1903 static uc_value_t * 1904 uc_ubus_request_set_fd(uc_vm_t *vm, size_t nargs) 1905 { 1906 uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request"); 1907 int fd; 1908 1909 if (!callctx) 1910 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid call context"); 1911 1912 fd = get_fd(vm, uc_fn_arg(0), NULL); 1913 1914 if (fd < 0) 1915 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid file descriptor"); 1916 1917 ubus_request_set_fd(callctx->ctx, &callctx->req, fd); 1918 1919 return ucv_boolean_new(true); 1920 } 1921 1922 /** 1923 * Finish a ubus method call with an error status. 1924 * 1925 * Completes the current deferred request with the specified error status 1926 * code without sending any reply data. 1927 * 1928 * Returns `true` on success. 1929 * 1930 * Returns `null` if an error occurred or the reply was already sent. 1931 * 1932 * @function module:ubus.request#error 1933 * 1934 * @param {number} [rcode=UBUS_STATUS_UNKNOWN_ERROR] 1935 * The error status code to return. 1936 * 1937 * @returns {?boolean} 1938 * 1939 * @example 1940 * // In a published object method handler 1941 * publish("my.service", { 1942 * "process": (req, msg) => { 1943 * if (!msg.input) { 1944 * req.error(UBUS_STATUS_INVALID_ARGUMENT); 1945 * return; 1946 * } 1947 * req.reply({ result: "ok" }); 1948 * } 1949 * }); 1950 */ 1951 static uc_value_t * 1952 uc_ubus_request_error(uc_vm_t *vm, size_t nargs) 1953 { 1954 uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request"); 1955 uc_value_t *rcode = uc_fn_arg(0); 1956 int64_t code; 1957 1958 if (!callctx) 1959 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid call context"); 1960 1961 args_get(vm, nargs, 1962 "rcode", UC_INTEGER, false, &rcode); 1963 1964 if (callctx->replied) 1965 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Reply has already been sent"); 1966 1967 code = ucv_int64_get(rcode); 1968 1969 if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) 1970 code = UBUS_STATUS_UNKNOWN_ERROR; 1971 1972 uc_ubus_request_finish(callctx, code); 1973 1974 ok_return(ucv_boolean_new(true)); 1975 } 1976 1977 1978 /* 1979 * ubus object notify 1980 * -------------------------------------------------------------------------- 1981 */ 1982 1983 /** 1984 * Check if a notification request has completed. 1985 * 1986 * Returns `true` if the notification request has finished. 1987 * 1988 * Returns `false` if the request is still pending. 1989 * 1990 * @function module:ubus.notify#completed 1991 * 1992 * @returns {boolean} 1993 * 1994 * @example 1995 * const n = obj.notify("method", { data: "value" }); 1996 * if (n.completed()) { 1997 * printf("Notification sent\n"); 1998 * } 1999 */ 2000 static uc_value_t * 2001 uc_ubus_notify_completed(uc_vm_t *vm, size_t nargs) 2002 { 2003 uc_ubus_notify_t *notifyctx = uc_fn_thisval("ubus.notify"); 2004 2005 ok_return(ucv_boolean_new(notifyctx->complete)); 2006 } 2007 2008 /** 2009 * Abort a pending notification request. 2010 * 2011 * Cancels an asynchronous notification request that has not yet completed. 2012 * 2013 * Returns `true` if the request was aborted. 2014 * 2015 * Returns `false` if the request was already completed. 2016 * 2017 * @function module:ubus.notify#abort 2018 * 2019 * @returns {boolean} 2020 * 2021 * @example 2022 * const n = obj.notify("method", { data: "value" }); 2023 * if (!n.completed()) { 2024 * n.abort(); 2025 * } 2026 */ 2027 static uc_value_t * 2028 uc_ubus_notify_abort(uc_vm_t *vm, size_t nargs) 2029 { 2030 uc_ubus_notify_t *notifyctx = uc_fn_thisval("ubus.notify"); 2031 2032 if (notifyctx->complete) 2033 ok_return(ucv_boolean_new(false)); 2034 2035 ubus_abort_request(notifyctx->ctx, ¬ifyctx->req.req); 2036 notifyctx->complete = true; 2037 uc_ubus_put_res(¬ifyctx->res); 2038 2039 ok_return(ucv_boolean_new(true)); 2040 } 2041 2042 static void 2043 uc_ubus_object_notify_data_cb(struct ubus_notify_request *req, int type, struct blob_attr *msg) 2044 { 2045 uc_ubus_notify_t *notifyctx = (uc_ubus_notify_t *)req; 2046 uc_vm_t *vm = notifyctx->vm; 2047 uc_value_t *this, *func; 2048 2049 this = notifyctx->res; 2050 func = ucv_resource_value_get(this, NOTIFY_RES_DATA_CB); 2051 2052 if (ucv_is_callable(func)) { 2053 uc_vm_stack_push(vm, ucv_get(this)); 2054 uc_vm_stack_push(vm, ucv_get(func)); 2055 uc_vm_stack_push(vm, ucv_int64_new(type)); 2056 uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true)); 2057 2058 if (uc_ubus_vm_call(vm, true, 2)) 2059 ucv_put(uc_vm_stack_pop(vm)); 2060 } 2061 } 2062 2063 static void 2064 uc_ubus_object_notify_status_cb(struct ubus_notify_request *req, int idx, int ret) 2065 { 2066 uc_ubus_notify_t *notifyctx = (uc_ubus_notify_t *)req; 2067 uc_vm_t *vm = notifyctx->vm; 2068 uc_value_t *this, *func; 2069 2070 this = notifyctx->res; 2071 func = ucv_resource_value_get(this, NOTIFY_RES_STATUS_CB); 2072 2073 if (ucv_is_callable(func)) { 2074 uc_vm_stack_push(vm, ucv_get(this)); 2075 uc_vm_stack_push(vm, ucv_get(func)); 2076 uc_vm_stack_push(vm, ucv_int64_new(idx)); 2077 uc_vm_stack_push(vm, ucv_int64_new(ret)); 2078 2079 if (uc_ubus_vm_call(vm, true, 2)) 2080 ucv_put(uc_vm_stack_pop(vm)); 2081 } 2082 } 2083 2084 static void 2085 uc_ubus_object_notify_complete_cb(struct ubus_notify_request *req, int idx, int ret) 2086 { 2087 uc_ubus_notify_t *notifyctx = (uc_ubus_notify_t *)req; 2088 uc_vm_t *vm = notifyctx->vm; 2089 uc_value_t *this, *func; 2090 2091 this = ucv_get(notifyctx->res); 2092 func = ucv_resource_value_get(this, NOTIFY_RES_CB); 2093 2094 if (ucv_is_callable(func)) { 2095 uc_vm_stack_push(vm, ucv_get(this)); 2096 uc_vm_stack_push(vm, ucv_get(func)); 2097 uc_vm_stack_push(vm, ucv_int64_new(idx)); 2098 uc_vm_stack_push(vm, ucv_int64_new(ret)); 2099 2100 if (uc_ubus_vm_call(vm, true, 2)) 2101 ucv_put(uc_vm_stack_pop(vm)); 2102 } 2103 2104 notifyctx->complete = true; 2105 uc_ubus_put_res(¬ifyctx->res); 2106 ucv_put(this); 2107 } 2108 2109 /** 2110 * Send a notification from a ubus object. 2111 * 2112 * Sends an asynchronous notification of the specified type to all 2113 * subscribers of the object. Optional callbacks can be provided to handle 2114 * data, status, and completion events. 2115 * 2116 * Returns a notification request resource for asynchronous operations. 2117 * 2118 * Returns a status code number when a synchronous timeout is specified. 2119 * 2120 * Returns `null` if the notification could not be sent. 2121 * 2122 * @function module:ubus.object#notify 2123 * 2124 * @param {string} type 2125 * The notification type string. 2126 * 2127 * @param {Object} [data] 2128 * Optional notification data as an object with field names and values. 2129 * 2130 * @param {function} [data_cb] 2131 * Optional callback invoked for each data notification received. 2132 * 2133 * @param {function} [status_cb] 2134 * Optional callback invoked for status updates. 2135 * 2136 * @param {function} [cb] 2137 * Optional callback invoked when the notification operation completes. 2138 * 2139 * @param {number} [timeout] 2140 * Optional timeout in milliseconds. If specified, the operation waits 2141 * synchronously for completion. 2142 * 2143 * @returns {?module:ubus.notify|?number} 2144 * 2145 * @example 2146 * const obj = publish("my.service", { 2147 * "trigger": (req, msg) => { 2148 * obj.notify("update", { key: "value" }, (idx, ret) => { 2149 * printf("Notification %d: status %d\n", idx, ret); 2150 * }); 2151 * req.reply({ sent: true }); 2152 * } 2153 * }); 2154 */ 2155 static uc_value_t * 2156 uc_ubus_object_notify(uc_vm_t *vm, size_t nargs) 2157 { 2158 uc_value_t *typename, *message, *data_cb, *status_cb, *complete_cb, *timeout; 2159 uc_ubus_object_t *uuobj = uc_fn_thisval("ubus.object"); 2160 uc_ubus_notify_t *notifyctx = NULL; 2161 uc_value_t *res; 2162 int64_t t; 2163 int rv = UBUS_STATUS_UNKNOWN_ERROR; 2164 2165 if (!uuobj) 2166 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid object context"); 2167 2168 args_get_named(vm, nargs, 2169 "type", UC_STRING, REQUIRED, &typename, 2170 "data", UC_OBJECT, OPTIONAL, &message, 2171 "data_cb", UC_CLOSURE, OPTIONAL, &data_cb, 2172 "status_cb", UC_CLOSURE, OPTIONAL, &status_cb, 2173 "cb", UC_CLOSURE, OPTIONAL, &complete_cb, 2174 "timeout", UC_INTEGER, OPTIONAL, &timeout); 2175 2176 t = timeout ? ucv_int64_get(timeout) : -1; 2177 2178 if (errno) 2179 err_return(UBUS_STATUS_INVALID_ARGUMENT, 2180 "Invalid timeout value: %s", strerror(errno)); 2181 2182 res = ucv_resource_create_ex(vm, "ubus.notify", (void **)¬ifyctx, __NOTIFY_RES_MAX, sizeof(*notifyctx)); 2183 2184 if (!notifyctx) 2185 err_return(rv, "Out of memory"); 2186 2187 notifyctx->vm = vm; 2188 notifyctx->ctx = uuobj->ctx; 2189 2190 blob_buf_init(&buf, 0); 2191 2192 if (message) 2193 ucv_object_to_blob(message, &buf); 2194 2195 rv = ubus_notify_async(uuobj->ctx, &uuobj->obj, 2196 ucv_string_get(typename), buf.head, 2197 ¬ifyctx->req); 2198 2199 if (rv != UBUS_STATUS_OK) { 2200 ucv_put(res); 2201 err_return(rv, "Failed to send notification"); 2202 } 2203 2204 notifyctx->res = ucv_get(res); 2205 notifyctx->req.data_cb = uc_ubus_object_notify_data_cb; 2206 notifyctx->req.status_cb = uc_ubus_object_notify_status_cb; 2207 notifyctx->req.complete_cb = uc_ubus_object_notify_complete_cb; 2208 2209 ucv_resource_value_set(res, NOTIFY_RES_CONN, ucv_get(uuobj->res)); 2210 ucv_resource_value_set(res, NOTIFY_RES_CB, ucv_get(complete_cb)); 2211 ucv_resource_value_set(res, NOTIFY_RES_DATA_CB, ucv_get(data_cb)); 2212 ucv_resource_value_set(res, NOTIFY_RES_STATUS_CB, ucv_get(status_cb)); 2213 2214 if (t >= 0) { 2215 rv = ubus_complete_request(uuobj->ctx, ¬ifyctx->req.req, t); 2216 2217 ucv_put(res); 2218 2219 ok_return(ucv_int64_new(rv)); 2220 } 2221 2222 ucv_resource_persistent_set(res, true); 2223 ubus_complete_request_async(uuobj->ctx, ¬ifyctx->req.req); 2224 2225 ok_return(res); 2226 } 2227 2228 2229 /* 2230 * ubus object remove 2231 * -------------------------------------------------------------------------- 2232 */ 2233 2234 static int 2235 uc_ubus_object_remove_common(uc_ubus_object_t *uuobj) 2236 { 2237 int rv = ubus_remove_object(uuobj->ctx, &uuobj->obj); 2238 2239 if (rv != UBUS_STATUS_OK) 2240 return rv; 2241 2242 uc_ubus_put_res(&uuobj->res); 2243 2244 return rv; 2245 } 2246 2247 /** 2248 * Remove a ubus object from the bus. 2249 * 2250 * Unregisters the object from the ubus bus, making it no longer accessible 2251 * to other clients. 2252 * 2253 * Returns `true` on success. 2254 * 2255 * Returns `null` if the object could not be removed. 2256 * 2257 * @function module:ubus.object#remove 2258 * 2259 * @returns {?boolean} 2260 * 2261 * @example 2262 * const obj = publish("my.service", { … }); 2263 * // … do work … 2264 * obj.remove(); 2265 */ 2266 static uc_value_t * 2267 uc_ubus_object_remove(uc_vm_t *vm, size_t nargs) 2268 { 2269 uc_ubus_object_t *uuobj = uc_fn_thisval("ubus.object"); 2270 int rv; 2271 2272 if (!uuobj) 2273 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid object context"); 2274 2275 rv = uc_ubus_object_remove_common(uuobj); 2276 2277 if (rv != UBUS_STATUS_OK) 2278 err_return(rv, "Failed to remove object"); 2279 2280 ok_return(ucv_boolean_new(true)); 2281 } 2282 2283 2284 /* 2285 * ubus object subscription status 2286 */ 2287 2288 /** 2289 * Check if a ubus object has subscribers. 2290 * 2291 * Returns `true` if there are active subscribers to the object. 2292 * 2293 * Returns `false` if no subscribers are currently connected. 2294 * 2295 * @function module:ubus.object#subscribed 2296 * 2297 * @returns {boolean} 2298 * 2299 * @example 2300 * const obj = publish("my.service", { 2301 * "trigger": (req, msg) => { 2302 * if (obj.subscribed()) { 2303 * obj.notify("update", { data: "value" }); 2304 * } 2305 * req.reply({ notified: obj.subscribed() }); 2306 * } 2307 * }); 2308 */ 2309 static uc_value_t * 2310 uc_ubus_object_subscribed(uc_vm_t *vm, size_t nargs) 2311 { 2312 uc_ubus_object_t *uuobj = uc_fn_thisval("ubus.object"); 2313 2314 if (!uuobj) 2315 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid object context"); 2316 2317 ok_return(ucv_boolean_new(uuobj->obj.has_subscribers)); 2318 } 2319 2320 2321 /* 2322 * ubus object method call handling 2323 * -------------------------------------------------------------------------- 2324 */ 2325 2326 static int 2327 uc_ubus_object_call_args(struct ubus_object *obj, const char *ubus_method_name, 2328 struct blob_attr *msg, uc_value_t **res) 2329 { 2330 uc_ubus_object_t *uuobj = (uc_ubus_object_t *)obj; 2331 const struct ubus_method *method = NULL; 2332 const struct blobmsg_hdr *hdr; 2333 struct blob_attr *attr; 2334 size_t len; 2335 bool found; 2336 int i; 2337 2338 for (i = 0; i < obj->n_methods; i++) { 2339 if (!strcmp(obj->methods[i].name, ubus_method_name)) { 2340 method = &obj->methods[i]; 2341 break; 2342 } 2343 } 2344 2345 if (!method) 2346 return UBUS_STATUS_METHOD_NOT_FOUND; 2347 2348 len = blob_len(msg); 2349 2350 __blob_for_each_attr(attr, blob_data(msg), len) { 2351 if (!blobmsg_check_attr_len(attr, false, len)) 2352 return UBUS_STATUS_INVALID_ARGUMENT; 2353 2354 if (!blob_is_extended(attr)) 2355 return UBUS_STATUS_INVALID_ARGUMENT; 2356 2357 hdr = blob_data(attr); 2358 found = false; 2359 2360 for (i = 0; i < method->n_policy; i++) { 2361 if (blobmsg_namelen(hdr) != strlen(method->policy[i].name)) 2362 continue; 2363 2364 if (strcmp(method->policy[i].name, (char *)hdr->name)) 2365 continue; 2366 2367 /* named argument found but wrong type */ 2368 if (blob_id(attr) != method->policy[i].type) 2369 goto inval; 2370 2371 found = true; 2372 break; 2373 } 2374 2375 /* named argument not found in policy */ 2376 if (!found) 2377 goto inval; 2378 } 2379 2380 *res = blob_array_to_ucv(uuobj->vm, blob_data(msg), blob_len(msg), true); 2381 2382 return UBUS_STATUS_OK; 2383 2384 inval: 2385 *res = NULL; 2386 2387 return UBUS_STATUS_INVALID_ARGUMENT; 2388 } 2389 2390 static uc_value_t * 2391 uc_ubus_object_call_info(uc_vm_t *vm, 2392 struct ubus_context *ctx, struct ubus_request_data *req, 2393 struct ubus_object *obj, const char *ubus_method_name) 2394 { 2395 uc_value_t *info, *o; 2396 2397 info = ucv_object_new(vm); 2398 2399 o = ucv_object_new(vm); 2400 2401 ucv_object_add(o, "user", ucv_string_new(req->acl.user)); 2402 ucv_object_add(o, "group", ucv_string_new(req->acl.group)); 2403 2404 if (req->acl.object) 2405 ucv_object_add(o, "object", ucv_string_new(req->acl.object)); 2406 2407 ucv_object_add(info, "acl", o); 2408 2409 o = ucv_object_new(vm); 2410 2411 ucv_object_add(o, "id", ucv_int64_new(obj->id)); 2412 2413 if (obj->name) 2414 ucv_object_add(o, "name", ucv_string_new(obj->name)); 2415 2416 if (obj->path) 2417 ucv_object_add(o, "path", ucv_string_new(obj->path)); 2418 2419 ucv_object_add(info, "object", o); 2420 2421 if (ubus_method_name) 2422 ucv_object_add(info, "method", ucv_string_new(ubus_method_name)); 2423 2424 return info; 2425 } 2426 2427 static int 2428 uc_ubus_handle_reply_common(struct ubus_context *ctx, 2429 struct ubus_request_data *req, 2430 uc_vm_t *vm, uc_value_t *this, uc_value_t *func, 2431 uc_value_t *reqproto) 2432 { 2433 uc_ubus_connection_t *conn = container_of(ctx, uc_ubus_connection_t, ctx); 2434 uc_ubus_request_t *callctx = NULL; 2435 uc_value_t *reqobj, *res; 2436 int rv; 2437 2438 /* allocate deferred method call context */ 2439 reqobj = ucv_resource_create_ex(vm, "ubus.request", (void **)&callctx, 1, sizeof(*callctx)); 2440 2441 if (!callctx) 2442 return UBUS_STATUS_UNKNOWN_ERROR; 2443 2444 callctx->ctx = ctx; 2445 callctx->vm = vm; 2446 ucv_resource_value_set(reqobj, 0, ucv_get(conn->res)); 2447 2448 ubus_defer_request(ctx, req, &callctx->req); 2449 2450 /* fd is copied to deferred request. ensure it does not get closed early */ 2451 ubus_request_get_caller_fd(req); 2452 2453 if (reqproto) 2454 ucv_prototype_set(ucv_prototype_get(reqobj), reqproto); 2455 2456 /* push object context, handler and request object onto stack */ 2457 uc_vm_stack_push(vm, ucv_get(this)); 2458 uc_vm_stack_push(vm, ucv_get(func)); 2459 uc_vm_stack_push(vm, ucv_get(reqobj)); 2460 2461 /* execute request handler function */ 2462 switch (uc_vm_call(vm, true, 1)) { 2463 case EXCEPTION_NONE: 2464 res = uc_vm_stack_pop(vm); 2465 2466 /* The handler function invoked a nested aync ubus request and returned it */ 2467 if (ucv_resource_data(res, "ubus.deferred")) { 2468 /* Install guard timer in case the reply callback is never called */ 2469 callctx->timeout.cb = uc_ubus_request_timeout; 2470 uloop_timeout_set(&callctx->timeout, 10000 /* FIXME */); 2471 callctx->res = ucv_get(reqobj); 2472 ucv_resource_persistent_set(callctx->res, true); 2473 } 2474 2475 /* Otherwise, when the function returned an object, treat it as 2476 * reply data and conclude deferred request immediately */ 2477 else if (ucv_type(res) == UC_OBJECT) { 2478 blob_buf_init(&buf, 0); 2479 ucv_object_to_blob(res, &buf); 2480 ubus_send_reply(ctx, &callctx->req, buf.head); 2481 2482 uc_ubus_request_finish_common(callctx, UBUS_STATUS_OK); 2483 } 2484 2485 /* If neither a deferred ubus request, nor a plain object were 2486 * returned and if reqobj.reply() hasn't been called, immediately 2487 * finish deferred request with UBUS_STATUS_NO_DATA. */ 2488 else if (!callctx->replied && !callctx->deferred) { 2489 rv = UBUS_STATUS_NO_DATA; 2490 2491 if (ucv_type(res) == UC_INTEGER) { 2492 rv = (int)ucv_int64_get(res); 2493 2494 if (rv < 0 || rv > __UBUS_STATUS_LAST) 2495 rv = UBUS_STATUS_UNKNOWN_ERROR; 2496 } 2497 2498 uc_ubus_request_finish_common(callctx, rv); 2499 } 2500 2501 ucv_put(res); 2502 break; 2503 2504 /* if the handler function invoked exit(), forward exit status as ubus 2505 * return code, map out of range values to UBUS_STATUS_UNKNOWN_ERROR. */ 2506 case EXCEPTION_EXIT: 2507 rv = vm->arg.s32; 2508 2509 if (rv < UBUS_STATUS_OK || rv >= __UBUS_STATUS_LAST) 2510 rv = UBUS_STATUS_UNKNOWN_ERROR; 2511 2512 uc_ubus_request_finish_common(callctx, rv); 2513 break; 2514 2515 /* treat other exceptions as fatal and halt uloop */ 2516 default: 2517 uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR); 2518 uc_ubus_vm_handle_exception(vm); 2519 break; 2520 } 2521 2522 /* release request object */ 2523 ucv_put(reqobj); 2524 2525 return UBUS_STATUS_OK; 2526 } 2527 2528 static int 2529 uc_ubus_object_call_cb(struct ubus_context *ctx, struct ubus_object *obj, 2530 struct ubus_request_data *req, const char *ubus_method_name, 2531 struct blob_attr *msg) 2532 { 2533 uc_value_t *func, *args = NULL, *reqproto, *methods; 2534 uc_ubus_object_t *uuobj = (uc_ubus_object_t *)obj; 2535 int rv; 2536 2537 methods = ucv_resource_value_get(uuobj->res, OBJ_RES_METHODS); 2538 func = ucv_object_get(ucv_object_get(methods, ubus_method_name, NULL), "call", NULL); 2539 2540 if (!ucv_is_callable(func)) 2541 return UBUS_STATUS_METHOD_NOT_FOUND; 2542 2543 rv = uc_ubus_object_call_args(obj, ubus_method_name, msg, &args); 2544 2545 if (rv != UBUS_STATUS_OK) 2546 return rv; 2547 2548 reqproto = ucv_object_new(uuobj->vm); 2549 2550 ucv_object_add(reqproto, "args", args); 2551 ucv_object_add(reqproto, "info", 2552 uc_ubus_object_call_info(uuobj->vm, ctx, req, obj, ubus_method_name)); 2553 2554 return uc_ubus_handle_reply_common(ctx, req, uuobj->vm, uuobj->res, func, reqproto); 2555 } 2556 2557 2558 /* 2559 * ubus object registration 2560 * -------------------------------------------------------------------------- 2561 */ 2562 2563 static void 2564 uc_ubus_object_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) 2565 { 2566 uc_ubus_object_t *uuobj = (uc_ubus_object_t *)obj; 2567 uc_value_t *func; 2568 2569 func = ucv_resource_value_get(uuobj->res, OBJ_RES_SUB_CB); 2570 2571 uc_vm_stack_push(uuobj->vm, ucv_get(uuobj->res)); 2572 uc_vm_stack_push(uuobj->vm, ucv_get(func)); 2573 2574 if (uc_ubus_vm_call(uuobj->vm, true, 0)) 2575 ucv_put(uc_vm_stack_pop(uuobj->vm)); 2576 } 2577 2578 static bool 2579 uc_ubus_object_methods_validate(uc_value_t *methods) 2580 { 2581 uc_value_t *func, *args; 2582 2583 ucv_object_foreach(methods, ubus_method_name, ubus_method_definition) { 2584 (void)ubus_method_name; 2585 2586 func = ucv_object_get(ubus_method_definition, "call", NULL); 2587 args = ucv_object_get(ubus_method_definition, "args", NULL); 2588 2589 if (!ucv_is_callable(func)) 2590 err_return(UBUS_STATUS_INVALID_ARGUMENT, 2591 "Method '%s' field 'call' is not a function value", 2592 ubus_method_name); 2593 2594 if (args) { 2595 if (ucv_type(args) != UC_OBJECT) 2596 err_return(UBUS_STATUS_INVALID_ARGUMENT, 2597 "Method '%s' field 'args' is not an object value", 2598 ubus_method_name); 2599 2600 ucv_object_foreach(args, ubus_argument_name, ubus_argument_typehint) { 2601 (void)ubus_argument_name; 2602 2603 switch (ucv_type(ubus_argument_typehint)) { 2604 case UC_BOOLEAN: 2605 case UC_INTEGER: 2606 case UC_DOUBLE: 2607 case UC_STRING: 2608 case UC_ARRAY: 2609 case UC_OBJECT: 2610 continue; 2611 2612 default: 2613 err_return(UBUS_STATUS_INVALID_ARGUMENT, 2614 "Method '%s' field 'args' argument '%s' hint has unsupported type %s", 2615 ubus_method_name, ubus_argument_name, 2616 ucv_typename(ubus_argument_typehint)); 2617 } 2618 } 2619 } 2620 } 2621 2622 ok_return(true); 2623 } 2624 2625 static bool 2626 uc_ubus_object_method_register(struct ubus_method *method, const char *ubus_method_name, 2627 uc_value_t *ubus_method_arguments) 2628 { 2629 struct blobmsg_policy *policy; 2630 enum blobmsg_type type; 2631 2632 method->name = strdup(ubus_method_name); 2633 method->policy = calloc(ucv_object_length(ubus_method_arguments), sizeof(*method->policy)); 2634 method->handler = uc_ubus_object_call_cb; 2635 2636 if (!method->name || !method->policy) 2637 return false; 2638 2639 ucv_object_foreach(ubus_method_arguments, ubus_argument_name, ubus_argument_typehint) { 2640 switch (ucv_type(ubus_argument_typehint)) { 2641 case UC_BOOLEAN: 2642 type = BLOBMSG_TYPE_INT8; 2643 break; 2644 2645 case UC_INTEGER: 2646 switch (ucv_int64_get(ubus_argument_typehint)) { 2647 case 8: 2648 type = BLOBMSG_TYPE_INT8; 2649 break; 2650 2651 case 16: 2652 type = BLOBMSG_TYPE_INT16; 2653 break; 2654 2655 case 64: 2656 type = BLOBMSG_TYPE_INT64; 2657 break; 2658 2659 default: 2660 type = BLOBMSG_TYPE_INT32; 2661 break; 2662 } 2663 2664 break; 2665 2666 case UC_DOUBLE: 2667 type = BLOBMSG_TYPE_DOUBLE; 2668 break; 2669 2670 case UC_ARRAY: 2671 type = BLOBMSG_TYPE_ARRAY; 2672 break; 2673 2674 case UC_OBJECT: 2675 type = BLOBMSG_TYPE_TABLE; 2676 break; 2677 2678 default: 2679 type = BLOBMSG_TYPE_STRING; 2680 break; 2681 } 2682 2683 policy = (struct blobmsg_policy *)&method->policy[method->n_policy++]; 2684 policy->type = type; 2685 policy->name = strdup(ubus_argument_name); 2686 2687 if (!policy->name) 2688 return false; 2689 } 2690 2691 return true; 2692 } 2693 2694 static uc_ubus_object_t * 2695 uc_ubus_object_register(uc_vm_t *vm, uc_ubus_connection_t *c, const char *ubus_object_name, 2696 uc_value_t *ubus_object_methods) 2697 { 2698 struct ubus_context *ctx = &c->ctx; 2699 const struct blobmsg_policy *policy; 2700 uc_ubus_object_t *uuobj = NULL; 2701 int rv = UBUS_STATUS_UNKNOWN_ERROR; 2702 char *tnptr, *onptr; 2703 struct ubus_method *method; 2704 struct ubus_object *obj; 2705 size_t len, typelen, namelen, methodlen; 2706 uc_value_t *args, *res; 2707 2708 namelen = strlen(ubus_object_name); 2709 typelen = strlen("ucode-ubus-") + namelen; 2710 methodlen = ucv_object_length(ubus_object_methods) * sizeof(struct ubus_method); 2711 len = sizeof(*uuobj) + methodlen + namelen + 1 + typelen + 1; 2712 2713 res = ucv_resource_create_ex(vm, "ubus.object", (void **)&uuobj, __OBJ_RES_MAX, len); 2714 2715 if (!uuobj) 2716 err_return(rv, "Out of memory"); 2717 2718 method = uuobj->methods; 2719 2720 obj = &uuobj->obj; 2721 obj->methods = method; 2722 2723 if (ubus_object_methods) { 2724 ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) { 2725 args = ucv_object_get(ubus_method_definition, "args", NULL); 2726 2727 if (!uc_ubus_object_method_register(&method[obj->n_methods++], ubus_method_name, args)) 2728 goto out; 2729 } 2730 } 2731 2732 onptr = (char *)&uuobj->methods[obj->n_methods]; 2733 tnptr = onptr + namelen + 1; 2734 2735 snprintf(tnptr, typelen, "ucode-ubus-%s", ubus_object_name); 2736 obj->name = memcpy(onptr, ubus_object_name, namelen); 2737 2738 obj->type = (struct ubus_object_type *)&uuobj->type; 2739 obj->type->name = tnptr; 2740 obj->type->methods = obj->methods; 2741 obj->type->n_methods = obj->n_methods; 2742 2743 rv = ubus_add_object(ctx, obj); 2744 2745 if (rv != UBUS_STATUS_OK) 2746 goto out; 2747 2748 uuobj->vm = vm; 2749 uuobj->ctx = ctx; 2750 uuobj->res = ucv_get(res); 2751 ucv_resource_persistent_set(res, true); 2752 ucv_resource_value_set(res, OBJ_RES_CONN, ucv_get(c->res)); 2753 ucv_resource_value_set(res, OBJ_RES_METHODS, ucv_get(ubus_object_methods)); 2754 2755 return uuobj; 2756 2757 out: 2758 for (; obj->n_methods > 0; method++, obj->n_methods--) { 2759 for (policy = method->policy; method->n_policy > 0; policy++, method->n_policy--) 2760 free((char *)policy->name); 2761 2762 free((char *)method->name); 2763 free((char *)method->policy); 2764 } 2765 2766 ucv_put(res); 2767 2768 err_return(rv, "Unable to add ubus object"); 2769 } 2770 2771 /** 2772 * Publish a ubus object on the bus. 2773 * 2774 * Registers a new object with the specified name and methods on the ubus 2775 * bus. The object can define method handlers and an optional subscribe 2776 * callback. 2777 * 2778 * Returns an object resource representing the published object. 2779 * 2780 * Returns `null` if the object could not be registered. 2781 * 2782 * @function module:ubus.connection#publish 2783 * 2784 * @param {string} object_name 2785 * The name to register the object under. 2786 * 2787 * @param {Object} [methods] 2788 * Optional object defining methods with `call` functions and optional 2789 * `args` type specifications. 2790 * 2791 * @param {function} [subscribe_callback] 2792 * Optional callback invoked when a subscriber connects to the object. 2793 * 2794 * @returns {?module:ubus.object} 2795 * 2796 * @example 2797 * const conn = connect(); 2798 * const obj = conn.publish("my.service", { 2799 * "hello": { 2800 * call: (req, msg) => { 2801 * req.reply({ message: "Hello, " + msg.name }); 2802 * }, 2803 * args: { name: "string" } 2804 * } 2805 * }); 2806 */ 2807 static uc_value_t * 2808 uc_ubus_publish(uc_vm_t *vm, size_t nargs) 2809 { 2810 uc_value_t *objname, *methods, *subscribecb; 2811 uc_ubus_connection_t *c; 2812 uc_ubus_object_t *uuobj; 2813 2814 conn_get(vm, &c); 2815 2816 args_get(vm, nargs, 2817 "object name", UC_STRING, false, &objname, 2818 "object methods", UC_OBJECT, true, &methods, 2819 "subscribe callback", UC_CLOSURE, true, &subscribecb); 2820 2821 if (!methods && !subscribecb) 2822 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Either methods or subscribe callback required"); 2823 2824 if (methods && !uc_ubus_object_methods_validate(methods)) 2825 return NULL; 2826 2827 uuobj = uc_ubus_object_register(vm, c, ucv_string_get(objname), methods); 2828 2829 if (!uuobj) 2830 return NULL; 2831 2832 if (subscribecb) { 2833 uuobj->obj.subscribe_cb = uc_ubus_object_subscribe_cb; 2834 ucv_resource_value_set(uuobj->res, OBJ_RES_SUB_CB, ucv_get(subscribecb)); 2835 } 2836 2837 ok_return(uuobj->res); 2838 } 2839 2840 2841 /* 2842 * ubus events 2843 * -------------------------------------------------------------------------- 2844 */ 2845 2846 static int 2847 uc_ubus_listener_remove_common(uc_ubus_listener_t *uul) 2848 { 2849 int rv = ubus_unregister_event_handler(uul->ctx, &uul->ev); 2850 2851 if (rv == UBUS_STATUS_OK) 2852 uc_ubus_put_res(&uul->res); 2853 2854 return rv; 2855 } 2856 2857 /** 2858 * Remove an event listener from the bus. 2859 * 2860 * Unregisters the event handler, stopping it from receiving further events. 2861 * 2862 * Returns `true` on success. 2863 * 2864 * Returns `null` if the listener could not be removed. 2865 * 2866 * @function module:ubus.listener#remove 2867 * 2868 * @returns {?boolean} 2869 * 2870 * @example 2871 * const listener = conn.listener("my.event.*", (type, data) => { 2872 * printf("Event: %s, Data: %.J\n", type, data); 2873 * }); 2874 * // … later … 2875 * listener.remove(); 2876 */ 2877 static uc_value_t * 2878 uc_ubus_listener_remove(uc_vm_t *vm, size_t nargs) 2879 { 2880 uc_ubus_listener_t *uul = uc_fn_thisval("ubus.listener"); 2881 int rv; 2882 2883 rv = uc_ubus_listener_remove_common(uul); 2884 2885 if (rv != UBUS_STATUS_OK) 2886 err_return(rv, "Failed to remove listener object"); 2887 2888 ok_return(ucv_boolean_new(true)); 2889 } 2890 2891 static void 2892 uc_ubus_listener_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, 2893 const char *type, struct blob_attr *msg) 2894 { 2895 uc_ubus_listener_t *uul = (uc_ubus_listener_t *)ev; 2896 uc_value_t *this, *func; 2897 uc_vm_t *vm = uul->vm; 2898 2899 this = uul->res; 2900 func = ucv_resource_value_get(this, 0); 2901 2902 uc_vm_stack_push(vm, ucv_get(this)); 2903 uc_vm_stack_push(vm, ucv_get(func)); 2904 uc_vm_stack_push(vm, ucv_string_new(type)); 2905 uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true)); 2906 2907 if (uc_ubus_vm_call(vm, true, 2)) 2908 ucv_put(uc_vm_stack_pop(vm)); 2909 } 2910 2911 2912 2913 /** 2914 * Register an event listener. 2915 * 2916 * Registers a callback to be invoked when events matching the specified 2917 * pattern are received. The listener receives the event type and data. 2918 * 2919 * Returns a listener resource that can be removed via 2920 * {@link module:ubus.listener#remove|remove()}. 2921 * 2922 * Returns `null` if the listener could not be registered. 2923 * 2924 * @function module:ubus.connection#listener 2925 * 2926 * @param {string} pattern 2927 * The event type pattern to match, supporting wildcards (e.g., 2928 * `` `system.*` ``, `` `my.event.?` ``). 2929 * 2930 * @param {function} cb 2931 * Callback invoked when a matching event is received. Receives the event 2932 * type string and event data object as arguments. 2933 * 2934 * @returns {?module:ubus.listener} 2935 * 2936 * @example 2937 * const conn = connect(); 2938 * const listener = conn.listener("system.*", (type, data) => { 2939 * printf("Event %s: %.J\n", type, data); 2940 * }); 2941 */ 2942 static uc_value_t * 2943 uc_ubus_listener(uc_vm_t *vm, size_t nargs) 2944 { 2945 uc_value_t *cb, *pattern; 2946 uc_ubus_connection_t *c; 2947 uc_ubus_listener_t *uul = NULL; 2948 uc_value_t *res; 2949 int rv; 2950 2951 conn_get(vm, &c); 2952 2953 args_get(vm, nargs, 2954 "event type pattern", UC_STRING, false, &pattern, 2955 "event callback", UC_CLOSURE, false, &cb); 2956 2957 res = ucv_resource_create_ex(vm, "ubus.listener", (void **)&uul, 1, sizeof(*uul)); 2958 2959 if (!uul) 2960 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Out of memory"); 2961 2962 uul->vm = vm; 2963 uul->ctx = &c->ctx; 2964 uul->res = res; 2965 uul->ev.cb = uc_ubus_listener_cb; 2966 2967 rv = ubus_register_event_handler(&c->ctx, &uul->ev, 2968 ucv_string_get(pattern)); 2969 2970 if (rv != UBUS_STATUS_OK) { 2971 ucv_put(res); 2972 err_return(rv, "Failed to register listener object"); 2973 } 2974 2975 ucv_resource_persistent_set(res, true); 2976 ucv_resource_value_set(res, 0, ucv_get(cb)); 2977 2978 ok_return(ucv_get(res)); 2979 } 2980 2981 /** 2982 * Send a ubus event. 2983 * 2984 * Broadcasts an event of the specified type with optional data to all 2985 * registered event listeners. 2986 * 2987 * Returns `true` on success. 2988 * 2989 * Returns `null` if the event could not be sent. 2990 * 2991 * @function module:ubus.connection#event 2992 * 2993 * @param {string} event_type 2994 * The type string identifying the event. 2995 * 2996 * @param {Object} [event_data] 2997 * Optional event data as an object with field names and values. 2998 * 2999 * @returns {?boolean} 3000 * 3001 * @example 3002 * const conn = connect(); 3003 * conn.event("system.boot", { host: "router1", uptime: 3600 }); 3004 */ 3005 static uc_value_t * 3006 uc_ubus_event(uc_vm_t *vm, size_t nargs) 3007 { 3008 uc_value_t *eventtype, *eventdata; 3009 uc_ubus_connection_t *c; 3010 int rv; 3011 3012 conn_get(vm, &c); 3013 3014 args_get(vm, nargs, 3015 "event id", UC_STRING, false, &eventtype, 3016 "event data", UC_OBJECT, true, &eventdata); 3017 3018 blob_buf_init(&buf, 0); 3019 3020 if (eventdata) 3021 ucv_object_to_blob(eventdata, &buf); 3022 3023 rv = ubus_send_event(&c->ctx, ucv_string_get(eventtype), buf.head); 3024 3025 if (rv != UBUS_STATUS_OK) 3026 err_return(rv, "Unable to send event"); 3027 3028 ok_return(ucv_boolean_new(true)); 3029 } 3030 3031 3032 /* 3033 * ubus subscriptions 3034 * -------------------------------------------------------------------------- 3035 */ 3036 3037 static int 3038 uc_ubus_subscriber_notify_cb(struct ubus_context *ctx, struct ubus_object *obj, 3039 struct ubus_request_data *req, const char *method, 3040 struct blob_attr *msg) 3041 { 3042 struct ubus_subscriber *sub = container_of(obj, struct ubus_subscriber, obj); 3043 uc_ubus_subscriber_t *uusub = container_of(sub, uc_ubus_subscriber_t, sub); 3044 uc_value_t *this, *func, *reqproto; 3045 3046 this = uusub->res; 3047 func = ucv_resource_value_get(this, SUB_RES_NOTIFY_CB); 3048 3049 if (!ucv_is_callable(func)) 3050 return UBUS_STATUS_METHOD_NOT_FOUND; 3051 3052 reqproto = ucv_object_new(uusub->vm); 3053 3054 ucv_object_add(reqproto, "type", ucv_string_new(method)); 3055 3056 ucv_object_add(reqproto, "data", 3057 blob_array_to_ucv(uusub->vm, blob_data(msg), blob_len(msg), true)); 3058 3059 ucv_object_add(reqproto, "info", 3060 uc_ubus_object_call_info(uusub->vm, ctx, req, obj, NULL)); 3061 3062 return uc_ubus_handle_reply_common(ctx, req, uusub->vm, this, func, reqproto); 3063 } 3064 3065 static void 3066 uc_ubus_subscriber_remove_cb(struct ubus_context *ctx, 3067 struct ubus_subscriber *sub, uint32_t id) 3068 { 3069 uc_ubus_subscriber_t *uusub = container_of(sub, uc_ubus_subscriber_t, sub); 3070 uc_value_t *this, *func; 3071 uc_vm_t *vm = uusub->vm; 3072 3073 this = uusub->res; 3074 func = ucv_resource_value_get(this, SUB_RES_REMOVE_CB); 3075 3076 if (!ucv_is_callable(func)) 3077 return; 3078 3079 uc_vm_stack_push(vm, ucv_get(this)); 3080 uc_vm_stack_push(vm, ucv_get(func)); 3081 uc_vm_stack_push(vm, ucv_uint64_new(id)); 3082 3083 if (uc_ubus_vm_call(vm, true, 1)) 3084 ucv_put(uc_vm_stack_pop(vm)); 3085 } 3086 3087 static uc_value_t * 3088 uc_ubus_subscriber_subunsub_common(uc_vm_t *vm, size_t nargs, bool subscribe) 3089 { 3090 uc_ubus_subscriber_t *uusub = uc_fn_thisval("ubus.subscriber"); 3091 uc_value_t *objname; 3092 uint32_t id; 3093 int rv; 3094 3095 if (!uusub) 3096 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid subscriber context"); 3097 3098 args_get(vm, nargs, 3099 "object name", UC_STRING, false, &objname); 3100 3101 rv = ubus_lookup_id(uusub->ctx, ucv_string_get(objname), &id); 3102 3103 if (rv != UBUS_STATUS_OK) 3104 err_return(rv, "Failed to resolve object name '%s'", 3105 ucv_string_get(objname)); 3106 3107 if (subscribe) 3108 rv = ubus_subscribe(uusub->ctx, &uusub->sub, id); 3109 else 3110 rv = ubus_unsubscribe(uusub->ctx, &uusub->sub, id); 3111 3112 if (rv != UBUS_STATUS_OK) 3113 err_return(rv, "Failed to %s object '%s'", 3114 subscribe ? "subscribe" : "unsubscribe", 3115 ucv_string_get(objname)); 3116 3117 ok_return(ucv_boolean_new(true)); 3118 } 3119 3120 /** 3121 * Subscribe to a ubus object. 3122 * 3123 * Registers interest in notifications from the specified object. 3124 * 3125 * Returns `true` on success. 3126 * 3127 * Returns `null` if the subscription failed. 3128 * 3129 * @function module:ubus.subscriber#subscribe 3130 * 3131 * @param {string} object_name 3132 * The name of the object to subscribe to. 3133 * 3134 * @returns {?boolean} 3135 * 3136 * @example 3137 * const conn = connect(); 3138 * const sub = conn.subscriber("my.object", (event) => { 3139 * printf("Notification: type=%s, data=%.J\n", event.type, event.data); 3140 * }); 3141 * // … later, to re-subscribe … 3142 * sub.subscribe("my.object"); 3143 */ 3144 static uc_value_t * 3145 uc_ubus_subscriber_subscribe(uc_vm_t *vm, size_t nargs) 3146 { 3147 return uc_ubus_subscriber_subunsub_common(vm, nargs, true); 3148 } 3149 3150 /** 3151 * Unsubscribe from a ubus object. 3152 * 3153 * Stops receiving notifications from the specified object. 3154 * 3155 * Returns `true` on success. 3156 * 3157 * Returns `null` if the unsubscription failed. 3158 * 3159 * @function module:ubus.subscriber#unsubscribe 3160 * 3161 * @param {string} object_name 3162 * The name of the object to unsubscribe from. 3163 * 3164 * @returns {?boolean} 3165 * 3166 * @example 3167 * const sub = conn.subscriber("my.object", (event) => { … }); 3168 * // … later, to unsubscribe temporarily … 3169 * sub.unsubscribe("my.object"); 3170 */ 3171 static uc_value_t * 3172 uc_ubus_subscriber_unsubscribe(uc_vm_t *vm, size_t nargs) 3173 { 3174 return uc_ubus_subscriber_subunsub_common(vm, nargs, false); 3175 } 3176 3177 static int 3178 uc_ubus_subscriber_remove_common(uc_ubus_subscriber_t *uusub) 3179 { 3180 int rv = ubus_unregister_subscriber(uusub->ctx, &uusub->sub); 3181 3182 if (rv == UBUS_STATUS_OK) 3183 uc_ubus_put_res(&uusub->res); 3184 3185 return rv; 3186 } 3187 3188 #ifdef HAVE_UBUS_NEW_OBJ_CB 3189 static bool 3190 uc_ubus_subscriber_new_object_cb(struct ubus_context *ctx, struct ubus_subscriber *sub, const char *path) 3191 { 3192 uc_ubus_subscriber_t *uusub = container_of(sub, uc_ubus_subscriber_t, sub); 3193 uc_value_t *patterns = ucv_resource_value_get(uusub->res, SUB_RES_PATTERNS); 3194 size_t len = ucv_array_length(patterns); 3195 3196 for (size_t i = 0; i < len; i++) { 3197 uc_value_t *val = ucv_array_get(patterns, i); 3198 const char *pattern; 3199 3200 if (ucv_type(val) != UC_STRING) 3201 continue; 3202 3203 pattern = ucv_string_get(val); 3204 3205 if (fnmatch(pattern, path, 0) == 0) 3206 return true; 3207 } 3208 3209 return false; 3210 } 3211 #endif 3212 3213 /** 3214 * Remove a subscriber from the bus. 3215 * 3216 * Unregisters the subscriber, stopping it from receiving further 3217 * notifications. 3218 * 3219 * Returns `true` on success. 3220 * 3221 * Returns `null` if the subscriber could not be removed. 3222 * 3223 * @function module:ubus.subscriber#remove 3224 * 3225 * @returns {?boolean} 3226 * 3227 * @example 3228 * const sub = conn.subscriber("my.object", (event) => { … }); 3229 * // … when done … 3230 * sub.remove(); 3231 */ 3232 static uc_value_t * 3233 uc_ubus_subscriber_remove(uc_vm_t *vm, size_t nargs) 3234 { 3235 uc_ubus_subscriber_t *uusub = uc_fn_thisval("ubus.subscriber"); 3236 int rv; 3237 3238 if (!uusub) 3239 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid subscriber context"); 3240 3241 rv = uc_ubus_subscriber_remove_common(uusub); 3242 3243 if (rv != UBUS_STATUS_OK) 3244 err_return(rv, "Failed to remove subscriber object"); 3245 3246 ok_return(ucv_boolean_new(true)); 3247 } 3248 3249 /** 3250 * Register a ubus subscriber. 3251 * 3252 * Creates a subscriber that can receive notifications from ubus objects. 3253 * The subscriber can define notify and remove callbacks, and optional 3254 * subscription patterns for automatic object discovery. 3255 * 3256 * Returns a subscriber resource representing the registered subscriber. 3257 * 3258 * Returns `null` if the subscriber could not be registered. 3259 * 3260 * @function module:ubus.connection#subscriber 3261 * 3262 * @param {function} notify_callback 3263 * Callback invoked when a notification is received from a subscribed 3264 * object. 3265 * 3266 * @param {function} remove_callback 3267 * Callback invoked when a subscribed object is removed from the bus. 3268 * 3269 * @param {string[]} [subscription_patterns] 3270 * Optional array of glob patterns for automatic object subscription. 3271 * 3272 * @returns {?module:ubus.subscriber} 3273 * 3274 * @example 3275 * const conn = connect(); 3276 * const sub = conn.subscriber( 3277 * (event) => { 3278 * printf("Received: type=%s, data=%.J\n", 3279 * event.type, event.data); 3280 * }, 3281 * (objid) => { 3282 * printf("Object removed: %d\n", objid); 3283 * }, 3284 * ["network.interface.*"] // Auto-subscribe to matching objects 3285 * ); 3286 * // Subscribe to a specific object 3287 * sub.subscribe("some.object"); 3288 */ 3289 static uc_value_t * 3290 uc_ubus_subscriber(uc_vm_t *vm, size_t nargs) 3291 { 3292 uc_value_t *notify_cb, *remove_cb, *subscriptions; 3293 uc_ubus_subscriber_t *uusub = NULL; 3294 uc_ubus_connection_t *c; 3295 uc_value_t *res; 3296 int rv; 3297 3298 conn_get(vm, &c); 3299 3300 args_get(vm, nargs, 3301 "notify callback", UC_CLOSURE, true, ¬ify_cb, 3302 "remove callback", UC_CLOSURE, true, &remove_cb, 3303 "subscription patterns", UC_ARRAY, true, &subscriptions); 3304 3305 if (!notify_cb && !remove_cb) 3306 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Either notify or remove callback required"); 3307 3308 res = ucv_resource_create_ex(vm, "ubus.subscriber", (void **)&uusub, __SUB_RES_MAX, sizeof(*uusub)); 3309 3310 if (!uusub) 3311 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Out of memory"); 3312 3313 uusub->vm = vm; 3314 uusub->ctx = &c->ctx; 3315 uusub->res = ucv_get(res); 3316 3317 ucv_resource_value_set(res, SUB_RES_NOTIFY_CB, ucv_get(notify_cb)); 3318 ucv_resource_value_set(res, SUB_RES_REMOVE_CB, ucv_get(remove_cb)); 3319 ucv_resource_value_set(res, SUB_RES_PATTERNS, ucv_get(subscriptions)); 3320 3321 #ifdef HAVE_UBUS_NEW_OBJ_CB 3322 if (subscriptions) 3323 uusub->sub.new_obj_cb = uc_ubus_subscriber_new_object_cb; 3324 #endif 3325 3326 rv = ubus_register_subscriber(&c->ctx, &uusub->sub); 3327 3328 if (rv != UBUS_STATUS_OK) { 3329 ucv_put(uusub->res); 3330 ucv_put(res); 3331 err_return(rv, "Failed to register subscriber object"); 3332 } 3333 3334 if (notify_cb) 3335 uusub->sub.cb = uc_ubus_subscriber_notify_cb; 3336 3337 if (remove_cb) 3338 uusub->sub.remove_cb = uc_ubus_subscriber_remove_cb; 3339 3340 ucv_resource_persistent_set(res, true); 3341 3342 ok_return(res); 3343 } 3344 3345 3346 /* 3347 * connection methods 3348 * -------------------------------------------------------------------------- 3349 */ 3350 3351 /** 3352 * Send a reply to an asynchronous method call. 3353 * 3354 * Sends the specified data as a reply to the incoming method call. This 3355 * function should be called within a published object's method handler. 3356 * 3357 * Returns `true` on success. 3358 * 3359 * Returns `null` if the reply could not be sent. 3360 * 3361 * @function module:ubus.request#reply 3362 * 3363 * @param {*} data 3364 * The data to send as reply. Can be any JSON-serializable value. 3365 * 3366 * @returns {boolean} 3367 * 3368 * @example 3369 * // Synchronous reply 3370 * const obj = publish("my.service", { 3371 * hello: (req, msg) => { 3372 * req.reply({ message: "Hello " + msg.name }); 3373 * } 3374 * }); 3375 * 3376 * @example 3377 * // Asynchronous reply after defer completes 3378 * const obj = publish("my.service", { 3379 * some_method: (req) => { 3380 * ubus_conn.defer("other.object", "other_method", {}, 3381 * (rc, data) => { 3382 * req.reply({ result: data }); 3383 * }); 3384 * } 3385 * }); 3386 */ 3387 static uc_value_t * 3388 uc_ubus_remove(uc_vm_t *vm, size_t nargs) 3389 { 3390 uc_ubus_subscriber_t **uusub; 3391 uc_ubus_connection_t *c; 3392 uc_ubus_object_t *uuobj; 3393 uc_ubus_listener_t **uul; 3394 int rv; 3395 3396 conn_get(vm, &c); 3397 3398 uusub = (uc_ubus_subscriber_t **)ucv_resource_dataptr(uc_fn_arg(0), "ubus.subscriber"); 3399 uuobj = (uc_ubus_object_t *)ucv_resource_data(uc_fn_arg(0), "ubus.object"); 3400 uul = (uc_ubus_listener_t **)ucv_resource_dataptr(uc_fn_arg(0), "ubus.listener"); 3401 3402 if (uusub && *uusub) { 3403 if ((*uusub)->ctx != &c->ctx) 3404 err_return(UBUS_STATUS_INVALID_ARGUMENT, 3405 "Subscriber belongs to different connection"); 3406 3407 rv = uc_ubus_subscriber_remove_common(*uusub); 3408 3409 if (rv != UBUS_STATUS_OK) 3410 err_return(rv, "Unable to remove subscriber"); 3411 } 3412 else if (uuobj) { 3413 if (uuobj->ctx != &c->ctx) 3414 err_return(UBUS_STATUS_INVALID_ARGUMENT, 3415 "Object belongs to different connection"); 3416 3417 rv = uc_ubus_object_remove_common(uuobj); 3418 3419 if (rv != UBUS_STATUS_OK) 3420 err_return(rv, "Unable to remove object"); 3421 } 3422 else if (uul && *uul) { 3423 if ((*uul)->ctx != &c->ctx) 3424 err_return(UBUS_STATUS_INVALID_ARGUMENT, 3425 "Listener belongs to different connection"); 3426 3427 rv = uc_ubus_listener_remove_common(*uul); 3428 3429 if (rv != UBUS_STATUS_OK) 3430 err_return(rv, "Unable to remove listener"); 3431 } 3432 else { 3433 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Unhandled resource type"); 3434 } 3435 3436 ok_return(ucv_boolean_new(true)); 3437 } 3438 3439 3440 /** 3441 * Disconnect from the ubus bus. 3442 * 3443 * Closes the connection to the ubus bus and releases associated resources. 3444 * All pending requests are aborted. 3445 * 3446 * Returns `true` on success. 3447 * 3448 * @function module:ubus.connection#disconnect 3449 * 3450 * @returns {boolean} 3451 * 3452 * @example 3453 * const conn = connect(); 3454 * // … do work … 3455 * conn.disconnect(); 3456 */ 3457 static uc_value_t * 3458 uc_ubus_disconnect(uc_vm_t *vm, size_t nargs) 3459 { 3460 uc_ubus_connection_t *c; 3461 3462 conn_get(vm, &c); 3463 3464 #ifdef HAVE_UBUS_FLUSH_REQUESTS 3465 ubus_flush_requests(&c->ctx); 3466 #endif 3467 if (c->fd_handle) { 3468 uloop_fd_delete(&c->ctx.sock); 3469 c->ctx.sock.fd = -1; 3470 } 3471 ubus_shutdown(&c->ctx); 3472 c->ctx.sock.fd = -1; 3473 uc_ubus_put_res(&c->res); 3474 3475 ok_return(ucv_boolean_new(true)); 3476 } 3477 3478 /** 3479 * Check if a deferred request has completed. 3480 * 3481 * Returns `true` if the deferred request has finished. 3482 * 3483 * Returns `false` if the request is still pending. 3484 * 3485 * @function module:ubus.deferred#completed 3486 * 3487 * @returns {boolean} 3488 */ 3489 static uc_value_t * 3490 uc_ubus_defer_completed(uc_vm_t *vm, size_t nargs) 3491 { 3492 uc_ubus_deferred_t *d = uc_fn_thisval("ubus.deferred"); 3493 3494 if (!d) 3495 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid deferred context"); 3496 3497 ok_return(ucv_boolean_new(d->complete)); 3498 } 3499 3500 /** 3501 * Wait synchronously for a deferred request to complete. 3502 * 3503 * Blocks until the deferred request completes or times out. 3504 * 3505 * Returns `true` if the request completed. 3506 * 3507 * Returns `false` if the request was already completed. 3508 * 3509 * @function module:ubus.deferred#await 3510 * 3511 * @returns {boolean} 3512 */ 3513 static uc_value_t * 3514 uc_ubus_defer_await(uc_vm_t *vm, size_t nargs) 3515 { 3516 uc_ubus_deferred_t *d = uc_fn_thisval("ubus.deferred"); 3517 int64_t remaining; 3518 3519 if (!d) 3520 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid deferred context"); 3521 3522 if (d->complete) 3523 ok_return(ucv_boolean_new(false)); 3524 3525 #ifdef HAVE_ULOOP_TIMEOUT_REMAINING64 3526 remaining = uloop_timeout_remaining64(&d->timeout); 3527 #else 3528 remaining = uloop_timeout_remaining(&d->timeout); 3529 #endif 3530 3531 ubus_complete_request(d->ctx, &d->request, remaining); 3532 3533 ok_return(ucv_boolean_new(true)); 3534 } 3535 3536 /** 3537 * Abort a pending deferred request. 3538 * 3539 * Cancels an asynchronous request that has not yet completed. 3540 * 3541 * Returns `true` if the request was aborted. 3542 * 3543 * Returns `false` if the request was already completed. 3544 * 3545 * @function module:ubus.deferred#abort 3546 * 3547 * @returns {boolean} 3548 */ 3549 static uc_value_t * 3550 uc_ubus_defer_abort(uc_vm_t *vm, size_t nargs) 3551 { 3552 uc_ubus_deferred_t *d = uc_fn_thisval("ubus.deferred"); 3553 3554 if (!d) 3555 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid deferred context"); 3556 3557 if (d->complete) 3558 ok_return(ucv_boolean_new(false)); 3559 3560 ubus_abort_request(d->ctx, &d->request); 3561 uloop_timeout_cancel(&d->timeout); 3562 3563 uc_ubus_put_res(&d->res); 3564 d->complete = true; 3565 3566 ok_return(ucv_boolean_new(true)); 3567 } 3568 3569 /* 3570 * channel related methods 3571 * -------------------------------------------------------------------------- 3572 */ 3573 3574 #ifdef HAVE_UBUS_CHANNEL_SUPPORT 3575 static int 3576 uc_ubus_channel_req_cb(struct ubus_context *ctx, struct ubus_object *obj, 3577 struct ubus_request_data *req, const char *method, 3578 struct blob_attr *msg) 3579 { 3580 uc_ubus_connection_t *c = container_of(ctx, uc_ubus_connection_t, ctx); 3581 uc_value_t *func, *args, *reqproto; 3582 3583 func = ucv_resource_value_get(c->res, CONN_RES_CB); 3584 3585 if (!ucv_is_callable(func)) 3586 return UBUS_STATUS_METHOD_NOT_FOUND; 3587 3588 args = blob_array_to_ucv(c->vm, blob_data(msg), blob_len(msg), true); 3589 reqproto = ucv_object_new(c->vm); 3590 ucv_object_add(reqproto, "args", args); 3591 3592 if (method) 3593 ucv_object_add(reqproto, "type", ucv_string_new(method)); 3594 3595 return uc_ubus_handle_reply_common(ctx, req, c->vm, c->res, func, reqproto); 3596 } 3597 3598 static void 3599 uc_ubus_channel_disconnect_cb(struct ubus_context *ctx) 3600 { 3601 uc_ubus_connection_t *c = container_of(ctx, uc_ubus_connection_t, ctx); 3602 uc_value_t *func; 3603 3604 func = ucv_resource_value_get(c->res, CONN_RES_DISCONNECT_CB); 3605 3606 if (ucv_is_callable(func)) { 3607 uc_vm_stack_push(c->vm, ucv_get(c->res)); 3608 uc_vm_stack_push(c->vm, ucv_get(func)); 3609 3610 if (uc_ubus_vm_call(c->vm, true, 0)) 3611 ucv_put(uc_vm_stack_pop(c->vm)); 3612 } 3613 3614 blob_buf_free(&c->buf); 3615 3616 if (c->ctx.sock.fd >= 0) { 3617 if (c->fd_handle) { 3618 uloop_fd_delete(&c->ctx.sock); 3619 c->ctx.sock.fd = -1; 3620 } 3621 ubus_shutdown(&c->ctx); 3622 c->ctx.sock.fd = -1; 3623 } 3624 3625 uc_ubus_put_res(&c->res); 3626 } 3627 3628 static uc_value_t * 3629 uc_ubus_channel_add(uc_ubus_connection_t *c, uc_value_t *cb, 3630 uc_value_t *disconnect_cb, uc_value_t *fd) 3631 { 3632 ucv_resource_persistent_set(c->res, true); 3633 ucv_resource_value_set(c->res, CONN_RES_FD, ucv_get(fd)); 3634 ucv_resource_value_set(c->res, CONN_RES_CB, ucv_get(cb)); 3635 ucv_resource_value_set(c->res, CONN_RES_DISCONNECT_CB, ucv_get(disconnect_cb)); 3636 c->ctx.connection_lost = uc_ubus_channel_disconnect_cb; 3637 ubus_add_uloop(&c->ctx); 3638 3639 ok_return(ucv_get(c->res)); 3640 } 3641 3642 #endif 3643 3644 /** 3645 * Create a new ubus channel from a method call context. 3646 * 3647 * Creates a bidirectional channel communication path in response to an 3648 * incoming method call. The callback will be invoked for incoming messages 3649 * on the channel. 3650 * 3651 * Returns a channel connection resource. 3652 * 3653 * Returns `null` if the channel could not be created. 3654 * 3655 * @function module:ubus.request#new_channel 3656 * 3657 * @param {function} cb 3658 * Callback invoked for incoming messages on the channel. 3659 * 3660 * @param {function} [disconnect_cb] 3661 * Optional callback invoked when the channel is disconnected. 3662 * 3663 * @param {number} [timeout=30] 3664 * The timeout in seconds for subsequent operations. 3665 * 3666 * @returns {?module:ubus.channel} 3667 */ 3668 static uc_value_t * 3669 uc_ubus_request_new_channel(uc_vm_t *vm, size_t nargs) 3670 { 3671 #ifdef HAVE_UBUS_CHANNEL_SUPPORT 3672 uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request"); 3673 uc_value_t *cb, *disconnect_cb, *timeout; 3674 uc_ubus_connection_t *c; 3675 int fd; 3676 3677 if (!callctx) 3678 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid call context"); 3679 3680 args_get(vm, nargs, 3681 "cb", UC_CLOSURE, true, &cb, 3682 "disconnect_cb", UC_CLOSURE, true, &disconnect_cb, 3683 "timeout", UC_INTEGER, true, &timeout); 3684 3685 c = uc_ubus_conn_alloc(vm, timeout, "ubus.channel"); 3686 3687 if (!c) 3688 return NULL; 3689 3690 if (ubus_channel_create(&c->ctx, &fd, cb ? uc_ubus_channel_req_cb : NULL)) { 3691 ucv_put(c->res); 3692 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Unable to create ubus channel"); 3693 } 3694 3695 ubus_request_set_fd(callctx->ctx, &callctx->req, fd); 3696 3697 return uc_ubus_channel_add(c, cb, disconnect_cb, NULL); 3698 #else 3699 err_return(UBUS_STATUS_NOT_SUPPORTED, "No ubus channel support"); 3700 #endif 3701 } 3702 3703 3704 /** 3705 * Connect to a ubus channel from a file descriptor. 3706 * 3707 * Creates a channel connection from an existing file descriptor, typically 3708 * received from a method call. The callback will be invoked for incoming 3709 * messages on the channel. 3710 * 3711 * Returns a channel connection resource. 3712 * 3713 * Returns `null` if the channel could not be created. 3714 * 3715 * @function module:ubus#open_channel 3716 * 3717 * @param {number|module:fs.file|module:socket.socket} fd 3718 * The file descriptor or resource of the channel to connect to. When a plain 3719 * integer fd is passed, the ubus channel takes ownership and closes it on 3720 * disconnect. When a resource object with a `fileno()` method is passed, the 3721 * resource retains ownership of the fd; the channel merely detaches from it 3722 * on disconnect without closing. 3723 * 3724 * @param {function} cb 3725 * Callback invoked for incoming messages on the channel. 3726 * 3727 * @param {function} [disconnect_cb] 3728 * Optional callback invoked when the channel is disconnected. 3729 * 3730 * @param {number} [timeout=30] 3731 * The timeout in seconds for subsequent operations. 3732 * 3733 * @returns {?module:ubus.channel} 3734 */ 3735 static uc_value_t * 3736 uc_ubus_channel_connect(uc_vm_t *vm, size_t nargs) 3737 { 3738 #ifdef HAVE_UBUS_CHANNEL_SUPPORT 3739 uc_value_t *fd, *cb, *disconnect_cb, *timeout; 3740 bool handle = false; 3741 uc_ubus_connection_t *c; 3742 int fd_val; 3743 3744 args_get(vm, nargs, 3745 "fd", UC_NULL, false, &fd, 3746 "cb", UC_CLOSURE, true, &cb, 3747 "disconnect_cb", UC_CLOSURE, true, &disconnect_cb, 3748 "timeout", UC_INTEGER, true, &timeout); 3749 3750 fd_val = get_fd(vm, fd, &handle); 3751 3752 if (fd_val < 0) 3753 err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid file descriptor argument"); 3754 3755 c = uc_ubus_conn_alloc(vm, timeout, "ubus.channel"); 3756 3757 if (!c) 3758 return NULL; 3759 3760 c->fd_handle = handle; 3761 3762 if (ubus_channel_connect(&c->ctx, fd_val, cb ? uc_ubus_channel_req_cb : NULL)) { 3763 ucv_put(c->res); 3764 err_return(UBUS_STATUS_UNKNOWN_ERROR, "Unable to create ubus channel"); 3765 } 3766 3767 return uc_ubus_channel_add(c, cb, disconnect_cb, fd); 3768 #else 3769 err_return(UBUS_STATUS_NOT_SUPPORTED, "No ubus channel support"); 3770 #endif 3771 } 3772 3773 3774 /** 3775 * Get or set the ubus exception handler. 3776 * 3777 * When called without arguments, returns the currently registered exception 3778 * handler function. When called with a function argument, registers it as 3779 * the exception handler for ubus operations. 3780 * 3781 * Returns the current exception handler when called without arguments. 3782 * 3783 * Returns `true` when a new handler was set. 3784 * 3785 * @function module:ubus#guard 3786 * 3787 * @param {function} [handler] 3788 * The exception handler function to register. 3789 * 3790 * @returns {function|boolean} 3791 */ 3792 static uc_value_t * 3793 uc_ubus_guard(uc_vm_t *vm, size_t nargs) 3794 { 3795 uc_value_t *arg = uc_fn_arg(0); 3796 3797 if (!nargs) 3798 return ucv_get(uc_vm_registry_get(vm, "ubus.ex_handler")); 3799 3800 if (arg && !ucv_is_callable(arg)) 3801 return NULL; 3802 3803 uc_vm_registry_set(vm, "ubus.ex_handler", ucv_get(arg)); 3804 3805 return ucv_boolean_new(true); 3806 } 3807 3808 3809 static const uc_function_list_t global_fns[] = { 3810 { "error", uc_ubus_error }, 3811 { "connect", uc_ubus_connect }, 3812 { "open_channel", uc_ubus_channel_connect }, 3813 { "guard", uc_ubus_guard }, 3814 }; 3815 3816 static const uc_function_list_t conn_fns[] = { 3817 { "list", uc_ubus_list }, 3818 { "call", uc_ubus_call }, 3819 { "defer", uc_ubus_defer }, 3820 { "publish", uc_ubus_publish }, 3821 { "remove", uc_ubus_remove }, 3822 { "listener", uc_ubus_listener }, 3823 { "subscriber", uc_ubus_subscriber }, 3824 { "event", uc_ubus_event }, 3825 { "error", uc_ubus_error }, 3826 { "disconnect", uc_ubus_disconnect }, 3827 }; 3828 3829 static const uc_function_list_t chan_fns[] = { 3830 { "request", uc_ubus_chan_request }, 3831 { "defer", uc_ubus_chan_defer }, 3832 { "error", uc_ubus_error }, 3833 { "disconnect", uc_ubus_disconnect }, 3834 }; 3835 3836 static const uc_function_list_t defer_fns[] = { 3837 { "await", uc_ubus_defer_await }, 3838 { "completed", uc_ubus_defer_completed }, 3839 { "abort", uc_ubus_defer_abort }, 3840 }; 3841 3842 static const uc_function_list_t object_fns[] = { 3843 { "subscribed", uc_ubus_object_subscribed }, 3844 { "notify", uc_ubus_object_notify }, 3845 { "remove", uc_ubus_object_remove }, 3846 }; 3847 3848 static const uc_function_list_t request_fns[] = { 3849 { "reply", uc_ubus_request_reply }, 3850 { "error", uc_ubus_request_error }, 3851 { "defer", uc_ubus_request_defer }, 3852 { "get_fd", uc_ubus_request_get_fd }, 3853 { "set_fd", uc_ubus_request_set_fd }, 3854 { "new_channel", uc_ubus_request_new_channel }, 3855 }; 3856 3857 static const uc_function_list_t notify_fns[] = { 3858 { "completed", uc_ubus_notify_completed }, 3859 { "abort", uc_ubus_notify_abort }, 3860 }; 3861 3862 static const uc_function_list_t listener_fns[] = { 3863 { "remove", uc_ubus_listener_remove }, 3864 }; 3865 3866 static const uc_function_list_t subscriber_fns[] = { 3867 { "subscribe", uc_ubus_subscriber_subscribe }, 3868 { "unsubscribe", uc_ubus_subscriber_unsubscribe }, 3869 { "remove", uc_ubus_subscriber_remove }, 3870 }; 3871 3872 static void free_connection(void *ud) { 3873 uc_ubus_connection_t *conn = ud; 3874 3875 blob_buf_free(&conn->buf); 3876 3877 if (conn->ctx.sock.fd >= 0) { 3878 if (conn->fd_handle) { 3879 uloop_fd_delete(&conn->ctx.sock); 3880 conn->ctx.sock.fd = -1; 3881 } 3882 ubus_shutdown(&conn->ctx); 3883 } 3884 } 3885 3886 static void free_deferred(void *ud) { 3887 uc_ubus_deferred_t *defer = ud; 3888 3889 uloop_timeout_cancel(&defer->timeout); 3890 } 3891 3892 static void free_object(void *ud) { 3893 uc_ubus_object_t *uuobj = ud; 3894 struct ubus_object *obj = &uuobj->obj; 3895 int i, j; 3896 3897 for (i = 0; i < obj->n_methods; i++) { 3898 for (j = 0; j < obj->methods[i].n_policy; j++) 3899 free((char *)obj->methods[i].policy[j].name); 3900 3901 free((char *)obj->methods[i].name); 3902 free((char *)obj->methods[i].policy); 3903 } 3904 } 3905 3906 static void free_request(void *ud) { 3907 uc_ubus_request_t *callctx = ud; 3908 3909 uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT); 3910 } 3911 3912 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 3913 { 3914 uc_function_list_register(scope, global_fns); 3915 uc_function_list_register(scope, conn_fns); 3916 3917 /** 3918 * @typedef 3919 * @name Ubus status codes 3920 * @property {number} STATUS_OK - Operation successful 3921 * @property {number} STATUS_INVALID_COMMAND - Invalid command 3922 * @property {number} STATUS_INVALID_ARGUMENT - Invalid argument 3923 * @property {number} STATUS_METHOD_NOT_FOUND - Method not found 3924 * @property {number} STATUS_NOT_FOUND - Object not found 3925 * @property {number} STATUS_NO_DATA - No data available 3926 * @property {number} STATUS_PERMISSION_DENIED - Permission denied 3927 * @property {number} STATUS_TIMEOUT - Operation timed out 3928 * @property {number} STATUS_NOT_SUPPORTED - Operation not supported 3929 * @property {number} STATUS_UNKNOWN_ERROR - Unknown error 3930 * @property {number} STATUS_CONNECTION_FAILED - Connection failed 3931 * @property {number} STATUS_NO_MEMORY - Out of memory (new) 3932 * @property {number} STATUS_PARSE_ERROR - Parse error (new) 3933 * @property {number} STATUS_SYSTEM_ERROR - System error (new) 3934 * @property {number} STATUS_CONTINUE - Virtual code for continued replies 3935 */ 3936 3937 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(UBUS_##x)) 3938 ADD_CONST(STATUS_OK); 3939 ADD_CONST(STATUS_INVALID_COMMAND); 3940 ADD_CONST(STATUS_INVALID_ARGUMENT); 3941 ADD_CONST(STATUS_METHOD_NOT_FOUND); 3942 ADD_CONST(STATUS_NOT_FOUND); 3943 ADD_CONST(STATUS_NO_DATA); 3944 ADD_CONST(STATUS_PERMISSION_DENIED); 3945 ADD_CONST(STATUS_TIMEOUT); 3946 ADD_CONST(STATUS_NOT_SUPPORTED); 3947 ADD_CONST(STATUS_UNKNOWN_ERROR); 3948 ADD_CONST(STATUS_CONNECTION_FAILED); 3949 3950 #ifdef HAVE_NEW_UBUS_STATUS_CODES 3951 ADD_CONST(STATUS_NO_MEMORY); 3952 ADD_CONST(STATUS_PARSE_ERROR); 3953 ADD_CONST(STATUS_SYSTEM_ERROR); 3954 #endif 3955 3956 /* virtual status code for reply */ 3957 #define UBUS_STATUS_CONTINUE -1 3958 ADD_CONST(STATUS_CONTINUE); 3959 3960 /** 3961 * @typedef 3962 * @name Ubus system object IDs 3963 * @property {number} SYSTEM_OBJECT_ACL - System object ACL identifier, 3964 * used to query ACL data via 3965 * {@link module:ubus#call|call()} with an integer object ID 3966 */ 3967 ADD_CONST(SYSTEM_OBJECT_ACL); 3968 3969 uc_type_declare(vm, "ubus.connection", conn_fns, free_connection); 3970 uc_type_declare(vm, "ubus.channel", chan_fns, free_connection); 3971 uc_type_declare(vm, "ubus.deferred", defer_fns, free_deferred); 3972 uc_type_declare(vm, "ubus.object", object_fns, free_object); 3973 uc_type_declare(vm, "ubus.notify", notify_fns, NULL); 3974 uc_type_declare(vm, "ubus.request", request_fns, free_request); 3975 uc_type_declare(vm, "ubus.listener", listener_fns, NULL); 3976 uc_type_declare(vm, "ubus.subscriber", subscriber_fns, NULL); 3977 } 3978
This page was automatically generated by LXR 0.3.1. • OpenWrt