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