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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt