• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/ubus.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt