summaryrefslogtreecommitdiff
path: root/examples/ucode/example-plugin.uc
blob: a96b703601a7a472bba0cd39c3d3103645a46793 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
'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 });
			}
		}
	}
};