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