150 lines
4.7 KiB
Ucode
150 lines
4.7 KiB
Ucode
|
'use strict';
|
||
|
|
||
|
let ubus = require('ubus').connect();
|
||
|
|
||
|
/*
|
||
|
* An ucode plugin script is supposed to return a signature object on
|
||
|
* invocation which describes the ubus objects the plugin script wants to
|
||
|
* register, as well as the related methods and their argument signatures.
|
||
|
*/
|
||
|
return {
|
||
|
/*
|
||
|
* Each toplevel key of the returned signature object corresponds to the
|
||
|
* name of an ubus object to register.
|
||
|
*
|
||
|
* The value of each key must be a nested dictionary describing the object
|
||
|
* methods.
|
||
|
*/
|
||
|
example_object_1: {
|
||
|
/*
|
||
|
* Each key within the nested dictionary refers to the name of a method
|
||
|
* to define for the parent object.
|
||
|
*
|
||
|
* The value of each method name key must be another nested dictionary
|
||
|
* describing the method.
|
||
|
*/
|
||
|
method_1: {
|
||
|
/*
|
||
|
* At the very least, each method description dictionary *must*
|
||
|
* contain a key "call" with the ucode callback function to call
|
||
|
* when the corresponding ubus object method is invoked.
|
||
|
*
|
||
|
* The callback function is supposed to return a dictionary which
|
||
|
* is automatically translated into a nested blobmsg structure and
|
||
|
* sent back as ubus reply.
|
||
|
*
|
||
|
* Non-dictionary return values are discarded and the ubus method
|
||
|
* call will fail with UBUS_STATUS_NO_DATA.
|
||
|
*/
|
||
|
call: function() {
|
||
|
return { hello: "world" };
|
||
|
}
|
||
|
},
|
||
|
|
||
|
method_2: {
|
||
|
/*
|
||
|
* Additionally, method descriptions *may* supply an "args" key
|
||
|
* referring to a dictionary describing the ubus method call
|
||
|
* arguments accepted.
|
||
|
*
|
||
|
* The resulting method argument policy is also published to ubus
|
||
|
* and visible with "ubus -v list".
|
||
|
*
|
||
|
* The callback function will receive a dictionary containing the
|
||
|
* received named arguments as first argument. Only named arguments
|
||
|
* present in the "args" dictionary are accepted. Attempts to invoke
|
||
|
* ubus methods with arguments not mentioned here or with argument
|
||
|
* values not matching the given type hints are rejected with
|
||
|
* UBUS_STATUS_INVALID_ARGUMENT.
|
||
|
*
|
||
|
* The expected data type of each named argument is inferred from
|
||
|
* the ucode value within the "args" dictionary:
|
||
|
*
|
||
|
* ucode type | ucode value | blob type
|
||
|
* -----------+-------------+--------------------
|
||
|
* integer | 8 | BLOBMSG_TYPE_INT8
|
||
|
* integer | 16 | BLOBMSG_TYPE_INT16
|
||
|
* integer | 64 | BLOBMSG_TYPE_INT64
|
||
|
* integer | any integer | BLOBMSG_TYPE_INT32
|
||
|
* boolean | true, false | BLOBMSG_TYPE_INT8
|
||
|
* string | any string | BLOBMSG_TYPE_STRING
|
||
|
* double | any double | BLOBMSG_TYPE_DOUBLE
|
||
|
* array | any array | BLOBMSG_TYPE_ARRAY
|
||
|
* object | any object | BLOBMSG_TYPE_TABLE
|
||
|
*
|
||
|
* The ucode callback function will also receive auxillary status
|
||
|
* information about the ubus request within a dictionary passed as
|
||
|
* second argument to it. The dictionary will contain details about
|
||
|
* the invoked object, the invoked method name (useful in case
|
||
|
* multiple methods share the same callback) and the effective ubusd
|
||
|
* ACL for this request.
|
||
|
*/
|
||
|
args: {
|
||
|
foo: 32,
|
||
|
bar: 64,
|
||
|
baz: true,
|
||
|
qrx: "example"
|
||
|
},
|
||
|
|
||
|
call: function(request) {
|
||
|
return {
|
||
|
got_args: request.args,
|
||
|
got_info: request.info
|
||
|
};
|
||
|
}
|
||
|
},
|
||
|
|
||
|
method_3: {
|
||
|
call: function(request) {
|
||
|
/*
|
||
|
* Process exit codes are automatically translated to ubus
|
||
|
* error status codes. Exit code values outside of the
|
||
|
* representable status range are converted to
|
||
|
* UBUS_STATUS_UNKNOWN_ERROR.
|
||
|
*/
|
||
|
if (request.info.acl.user != "root")
|
||
|
exit(UBUS_STATUS_PERMISSION_DENIED);
|
||
|
|
||
|
/*
|
||
|
* To invoke nested ubus requests without potentially blocking
|
||
|
* rpcd's main loop, use the ubus.defer() method to start an
|
||
|
* asynchronous request and issue request.reply() from within
|
||
|
* the completion callback. It is important to return the deferred
|
||
|
* request value produced by ubus.call_async() to instruct rpcd to
|
||
|
* await the completion of the nested request.
|
||
|
*/
|
||
|
return ubus.defer('example_object_2', 'method_a', { number: 5 },
|
||
|
function(code, reply) {
|
||
|
request.reply({
|
||
|
res: reply,
|
||
|
req: request.info
|
||
|
}, UBUS_STATUS_OK);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
method_4: {
|
||
|
call: function() {
|
||
|
/*
|
||
|
* Runtime exceptions are catched by rpcd, the corresponding
|
||
|
* request is replied to with UBUS_STATUS_UNKNOWN_ERROR.
|
||
|
*/
|
||
|
die("An error occurred");
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
example_object_2: {
|
||
|
method_a: {
|
||
|
args: { number: 123 },
|
||
|
call: function(request) {
|
||
|
/*
|
||
|
* Instead of returning the reply, we can also use the reply
|
||
|
* method of the request object.
|
||
|
*/
|
||
|
request.reply({ got_number: request.args.number });
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|