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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt