ucode.c 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. /*
  2. * rpcd - UBUS RPC server - ucode plugin
  3. *
  4. * Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
  5. *
  6. * Permission to use, copy, modify, and/or distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <dirent.h>
  21. #include <limits.h>
  22. #include <libubox/blobmsg.h>
  23. #include <libubox/blobmsg_json.h>
  24. #include <libubus.h>
  25. #include <ucode/compiler.h>
  26. #include <ucode/lib.h>
  27. #include <ucode/vm.h>
  28. #include <rpcd/plugin.h>
  29. #define RPC_UCSCRIPT_DIRECTORY INSTALL_PREFIX "/share/rpcd/ucode"
  30. static struct blob_buf buf;
  31. static int request_timeout;
  32. /*
  33. * Track script instances and registered ubus objects in these lists.
  34. *
  35. * This is primarily done to make Valgrind happy and to mark the
  36. * related memory as reachable. Since we don't have a teardown
  37. * mechanism in rpcd plugins we can't orderly free the related
  38. * ubus object and ucode VM memory anyway.
  39. */
  40. static LIST_HEAD(scripts);
  41. static LIST_HEAD(uuobjs);
  42. typedef struct {
  43. struct list_head list;
  44. uc_vm_t vm;
  45. uc_resource_type_t *requesttype;
  46. uc_value_t *pending_replies;
  47. char *path;
  48. } rpc_ucode_script_t;
  49. typedef struct {
  50. struct list_head list;
  51. rpc_ucode_script_t *script;
  52. uc_value_t *signature;
  53. struct ubus_object ubusobj;
  54. } rpc_ucode_ubus_obj_t;
  55. typedef struct {
  56. struct ubus_context *ubus;
  57. struct ubus_request_data req;
  58. struct uloop_timeout timeout;
  59. rpc_ucode_script_t *script;
  60. uc_value_t *func;
  61. uc_value_t *args;
  62. uc_value_t *info;
  63. bool replied;
  64. } rpc_ucode_call_ctx_t;
  65. static uc_parse_config_t config = {
  66. .strict_declarations = false,
  67. .lstrip_blocks = true,
  68. .trim_blocks = true,
  69. .raw_mode = true
  70. };
  71. static rpc_ucode_script_t *
  72. rpc_ucode_obj_to_script(struct ubus_object *obj)
  73. {
  74. rpc_ucode_ubus_obj_t *uo = container_of(obj, rpc_ucode_ubus_obj_t, ubusobj);
  75. return uo->script;
  76. }
  77. static uc_value_t *
  78. rpc_ucode_obj_to_signature(struct ubus_object *obj)
  79. {
  80. rpc_ucode_ubus_obj_t *uo = container_of(obj, rpc_ucode_ubus_obj_t, ubusobj);
  81. return uo->signature;
  82. }
  83. static void
  84. rpc_ucode_ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob);
  85. static void
  86. rpc_ucode_ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob);
  87. static void
  88. rpc_ucode_ucv_to_blob(const char *name, uc_value_t *val, struct blob_buf *blob)
  89. {
  90. int64_t n;
  91. void *c;
  92. switch (ucv_type(val)) {
  93. case UC_NULL:
  94. blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0);
  95. break;
  96. case UC_BOOLEAN:
  97. blobmsg_add_u8(blob, name, ucv_boolean_get(val));
  98. break;
  99. case UC_INTEGER:
  100. n = ucv_int64_get(val);
  101. if (errno == ERANGE)
  102. blobmsg_add_u64(blob, name, ucv_uint64_get(val));
  103. else if (n >= INT32_MIN && n <= INT32_MAX)
  104. blobmsg_add_u32(blob, name, n);
  105. else
  106. blobmsg_add_u64(blob, name, n);
  107. break;
  108. case UC_DOUBLE:
  109. blobmsg_add_double(blob, name, ucv_double_get(val));
  110. break;
  111. case UC_STRING:
  112. blobmsg_add_string(blob, name, ucv_string_get(val));
  113. break;
  114. case UC_ARRAY:
  115. c = blobmsg_open_array(blob, name);
  116. rpc_ucode_ucv_array_to_blob(val, blob);
  117. blobmsg_close_array(blob, c);
  118. break;
  119. case UC_OBJECT:
  120. c = blobmsg_open_table(blob, name);
  121. rpc_ucode_ucv_object_to_blob(val, blob);
  122. blobmsg_close_table(blob, c);
  123. break;
  124. default:
  125. break;
  126. }
  127. }
  128. static void
  129. rpc_ucode_ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob)
  130. {
  131. size_t i;
  132. for (i = 0; i < ucv_array_length(val); i++)
  133. rpc_ucode_ucv_to_blob(NULL, ucv_array_get(val, i), blob);
  134. }
  135. static void
  136. rpc_ucode_ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob)
  137. {
  138. ucv_object_foreach(val, k, v)
  139. rpc_ucode_ucv_to_blob(k, v, blob);
  140. }
  141. static uc_value_t *
  142. rpc_ucode_blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name);
  143. static uc_value_t *
  144. rpc_ucode_blob_array_to_ucv(uc_vm_t *vm, struct blob_attr *attr, size_t len, bool table)
  145. {
  146. uc_value_t *o = table ? ucv_object_new(vm) : ucv_array_new(vm);
  147. uc_value_t *v;
  148. struct blob_attr *pos;
  149. size_t rem = len;
  150. const char *name;
  151. if (!o)
  152. return NULL;
  153. __blob_for_each_attr(pos, attr, rem) {
  154. name = NULL;
  155. v = rpc_ucode_blob_to_ucv(vm, pos, table, &name);
  156. if (table && name)
  157. ucv_object_add(o, name, v);
  158. else if (!table)
  159. ucv_array_push(o, v);
  160. else
  161. ucv_put(v);
  162. }
  163. return o;
  164. }
  165. static uc_value_t *
  166. rpc_ucode_blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name)
  167. {
  168. void *data;
  169. int len;
  170. if (!blobmsg_check_attr(attr, false))
  171. return NULL;
  172. if (table && blobmsg_name(attr)[0])
  173. *name = blobmsg_name(attr);
  174. data = blobmsg_data(attr);
  175. len = blobmsg_data_len(attr);
  176. switch (blob_id(attr)) {
  177. case BLOBMSG_TYPE_BOOL:
  178. return ucv_boolean_new(*(uint8_t *)data);
  179. case BLOBMSG_TYPE_INT16:
  180. return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data));
  181. case BLOBMSG_TYPE_INT32:
  182. return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data));
  183. case BLOBMSG_TYPE_INT64:
  184. return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data));
  185. case BLOBMSG_TYPE_DOUBLE:
  186. ;
  187. union {
  188. double d;
  189. uint64_t u64;
  190. } v;
  191. v.u64 = be64_to_cpu(*(uint64_t *)data);
  192. return ucv_double_new(v.d);
  193. case BLOBMSG_TYPE_STRING:
  194. return ucv_string_new(data);
  195. case BLOBMSG_TYPE_ARRAY:
  196. return rpc_ucode_blob_array_to_ucv(vm, data, len, false);
  197. case BLOBMSG_TYPE_TABLE:
  198. return rpc_ucode_blob_array_to_ucv(vm, data, len, true);
  199. default:
  200. return NULL;
  201. }
  202. }
  203. static int
  204. rpc_ucode_validate_call_args(struct ubus_object *obj, const char *ubus_method_name, struct blob_attr *msg, uc_value_t **res)
  205. {
  206. rpc_ucode_script_t *script = rpc_ucode_obj_to_script(obj);
  207. const struct ubus_method *method = NULL;
  208. const struct blobmsg_hdr *hdr;
  209. struct blob_attr *attr;
  210. bool found;
  211. size_t i;
  212. int len;
  213. for (i = 0; i < obj->n_methods; i++) {
  214. if (!strcmp(obj->methods[i].name, ubus_method_name)) {
  215. method = &obj->methods[i];
  216. break;
  217. }
  218. }
  219. if (!method)
  220. return UBUS_STATUS_METHOD_NOT_FOUND;
  221. len = blob_len(msg);
  222. __blob_for_each_attr(attr, blob_data(msg), len) {
  223. if (!blobmsg_check_attr_len(attr, false, len))
  224. return UBUS_STATUS_INVALID_ARGUMENT;
  225. if (!blob_is_extended(attr))
  226. return UBUS_STATUS_INVALID_ARGUMENT;
  227. hdr = blob_data(attr);
  228. found = false;
  229. for (i = 0; i < method->n_policy; i++) {
  230. if (blobmsg_namelen(hdr) != strlen(method->policy[i].name))
  231. continue;
  232. if (strcmp(method->policy[i].name, (char *)hdr->name))
  233. continue;
  234. /* named argument found but wrong type */
  235. if (blob_id(attr) != method->policy[i].type)
  236. goto inval;
  237. found = true;
  238. break;
  239. }
  240. /* named argument not found in policy */
  241. if (!found) {
  242. /* allow special ubus_rpc_session argument */
  243. if (!strcmp("ubus_rpc_session", (char *)hdr->name) && blob_id(attr) == BLOBMSG_TYPE_STRING)
  244. continue;
  245. goto inval;
  246. }
  247. }
  248. *res = rpc_ucode_blob_array_to_ucv(&script->vm, blob_data(msg), blob_len(msg), true);
  249. return UBUS_STATUS_OK;
  250. inval:
  251. *res = NULL;
  252. return UBUS_STATUS_INVALID_ARGUMENT;
  253. }
  254. static uc_value_t *
  255. rpc_ucode_gather_call_info(uc_vm_t *vm,
  256. struct ubus_context *ctx, struct ubus_request_data *req,
  257. struct ubus_object *obj, const char *ubus_method_name)
  258. {
  259. uc_value_t *info, *o;
  260. info = ucv_object_new(vm);
  261. o = ucv_object_new(vm);
  262. ucv_object_add(o, "user", ucv_string_new(req->acl.user));
  263. ucv_object_add(o, "group", ucv_string_new(req->acl.group));
  264. ucv_object_add(o, "object", ucv_string_new(req->acl.object));
  265. ucv_object_add(info, "acl", o);
  266. o = ucv_object_new(vm);
  267. ucv_object_add(o, "id", ucv_uint64_new(obj->id));
  268. ucv_object_add(o, "name", ucv_string_new(obj->name));
  269. if (obj->path)
  270. ucv_object_add(o, "path", ucv_string_new(obj->path));
  271. ucv_object_add(info, "object", o);
  272. ucv_object_add(info, "method", ucv_string_new(ubus_method_name));
  273. return info;
  274. }
  275. static void
  276. rpc_ucode_request_finish(rpc_ucode_call_ctx_t *callctx, int code, uc_value_t *reply)
  277. {
  278. rpc_ucode_script_t *script = callctx->script;
  279. uc_resource_t *r;
  280. size_t i;
  281. if (callctx->replied)
  282. return;
  283. if (reply) {
  284. blob_buf_init(&buf, 0);
  285. rpc_ucode_ucv_object_to_blob(reply, &buf);
  286. ubus_send_reply(callctx->ubus, &callctx->req, buf.head);
  287. }
  288. ubus_complete_deferred_request(callctx->ubus, &callctx->req, code);
  289. callctx->replied = true;
  290. for (i = 0; i < ucv_array_length(script->pending_replies); i++) {
  291. r = (uc_resource_t *)ucv_array_get(script->pending_replies, i);
  292. if (r && r->data == callctx) {
  293. ucv_array_set(script->pending_replies, i, NULL);
  294. break;
  295. }
  296. }
  297. }
  298. static void
  299. rpc_ucode_request_timeout(struct uloop_timeout *timeout)
  300. {
  301. rpc_ucode_call_ctx_t *callctx = container_of(timeout, rpc_ucode_call_ctx_t, timeout);
  302. rpc_ucode_request_finish(callctx, UBUS_STATUS_TIMEOUT, NULL);
  303. }
  304. static int
  305. rpc_ucode_script_call(struct ubus_context *ctx, struct ubus_object *obj,
  306. struct ubus_request_data *req, const char *ubus_method_name,
  307. struct blob_attr *msg)
  308. {
  309. rpc_ucode_script_t *script = rpc_ucode_obj_to_script(obj);
  310. uc_value_t *func, *args = NULL, *reqobj, *reqproto, *res;
  311. rpc_ucode_call_ctx_t *callctx;
  312. const char *extype;
  313. size_t i;
  314. int rv;
  315. rv = rpc_ucode_validate_call_args(obj, ubus_method_name, msg, &args);
  316. if (rv != UBUS_STATUS_OK)
  317. return rv;
  318. func = ucv_object_get(
  319. ucv_object_get(rpc_ucode_obj_to_signature(obj), ubus_method_name, NULL),
  320. "call", NULL
  321. );
  322. if (!ucv_is_callable(func))
  323. return UBUS_STATUS_METHOD_NOT_FOUND;
  324. /* allocate deferred method call context */
  325. callctx = calloc(1, sizeof(*callctx));
  326. if (!callctx)
  327. return UBUS_STATUS_UNKNOWN_ERROR;
  328. callctx->ubus = ctx;
  329. callctx->script = script;
  330. ubus_defer_request(ctx, req, &callctx->req);
  331. /* create ucode request type object and set properties */
  332. reqobj = uc_resource_new(script->requesttype, callctx);
  333. reqproto = ucv_object_new(&script->vm);
  334. ucv_object_add(reqproto, "args", args);
  335. ucv_object_add(reqproto, "info",
  336. rpc_ucode_gather_call_info(&script->vm, ctx, req, obj, ubus_method_name));
  337. ucv_prototype_set(ucv_prototype_get(reqobj), reqproto);
  338. /* push handler and request object onto stack */
  339. uc_vm_stack_push(&script->vm, ucv_get(func));
  340. uc_vm_stack_push(&script->vm, ucv_get(reqobj));
  341. /* execute request handler function */
  342. switch (uc_vm_call(&script->vm, false, 1)) {
  343. case EXCEPTION_NONE:
  344. res = uc_vm_stack_pop(&script->vm);
  345. /* The handler function invoked a nested aync ubus request and returned it */
  346. if (ucv_resource_dataptr(res, "ubus.deferred")) {
  347. /* Install guard timer in case the reply callback is never called */
  348. callctx->timeout.cb = rpc_ucode_request_timeout;
  349. uloop_timeout_set(&callctx->timeout, request_timeout);
  350. /* Add wrapped request context into registry to prevent GC'ing
  351. * until reply or timeout occurred */
  352. for (i = 0;; i++) {
  353. if (ucv_array_get(script->pending_replies, i) == NULL) {
  354. ucv_array_set(script->pending_replies, i, ucv_get(reqobj));
  355. break;
  356. }
  357. }
  358. }
  359. /* Otherwise, when the function returned an object, treat it as
  360. * reply data and conclude deferred request immediately */
  361. else if (ucv_type(res) == UC_OBJECT) {
  362. blob_buf_init(&buf, 0);
  363. rpc_ucode_ucv_object_to_blob(res, &buf);
  364. ubus_send_reply(ctx, &callctx->req, buf.head);
  365. ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_OK);
  366. callctx->replied = true;
  367. }
  368. /* If neither a deferred ubus request, nor a plain object were
  369. * returned and if reqobj.reply() hasn't been called, immediately
  370. * finish deferred request with UBUS_STATUS_NO_DATA. The */
  371. else if (!callctx->replied) {
  372. ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_NO_DATA);
  373. callctx->replied = true;
  374. }
  375. ucv_put(res);
  376. break;
  377. /* if the handler function invoked exit(), forward exit status as ubus
  378. * return code, map out of range values to UBUS_STATUS_UNKNOWN_ERROR. */
  379. case EXCEPTION_EXIT:
  380. rv = script->vm.arg.s32;
  381. if (rv < UBUS_STATUS_OK || rv >= __UBUS_STATUS_LAST)
  382. rv = UBUS_STATUS_UNKNOWN_ERROR;
  383. ubus_complete_deferred_request(ctx, &callctx->req, rv);
  384. callctx->replied = true;
  385. break;
  386. /* treat other exceptions as unknown error */
  387. default:
  388. switch (script->vm.exception.type) {
  389. case EXCEPTION_SYNTAX: extype = "Syntax error"; break;
  390. case EXCEPTION_RUNTIME: extype = "Runtime error"; break;
  391. case EXCEPTION_TYPE: extype = "Type error"; break;
  392. case EXCEPTION_REFERENCE: extype = "Reference error"; break;
  393. default: extype = "Exception";
  394. }
  395. res = ucv_object_get(
  396. ucv_array_get(script->vm.exception.stacktrace, 0),
  397. "context", NULL);
  398. fprintf(stderr,
  399. "Unhandled ucode exception in '%s' method!\n%s: %s\n\n%s\n",
  400. ubus_method_name, extype, script->vm.exception.message,
  401. ucv_string_get(res));
  402. ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_UNKNOWN_ERROR);
  403. callctx->replied = true;
  404. break;
  405. }
  406. /* release request object */
  407. ucv_put(reqobj);
  408. /* garbage collect */
  409. ucv_gc(&script->vm);
  410. return UBUS_STATUS_OK;
  411. }
  412. static uc_program_t *
  413. rpc_ucode_script_compile(const char *path, uc_source_t *src)
  414. {
  415. char *syntax_error = NULL;
  416. uc_program_t *prog;
  417. prog = uc_compile(&config, src, &syntax_error);
  418. if (!prog)
  419. fprintf(stderr, "Unable to compile ucode script %s: %s\n",
  420. path, syntax_error);
  421. uc_source_put(src);
  422. free(syntax_error);
  423. return prog;
  424. }
  425. static bool
  426. rpc_ucode_script_validate(rpc_ucode_script_t *script)
  427. {
  428. uc_value_t *signature = uc_vm_registry_get(&script->vm, "rpcd.ucode.signature");
  429. uc_value_t *args, *func;
  430. if (ucv_type(signature) != UC_OBJECT) {
  431. fprintf(stderr, "Invalid object signature for ucode script %s"
  432. " - expected dictionary, got %s\n",
  433. script->path, ucv_typename(signature));
  434. return false;
  435. }
  436. ucv_object_foreach(signature, ubus_object_name, ubus_object_methods) {
  437. if (ucv_type(ubus_object_methods) != UC_OBJECT) {
  438. fprintf(stderr, "Invalid method signature for ucode script %s, object %s"
  439. " - expected dictionary, got %s\n",
  440. script->path, ubus_object_name, ucv_typename(ubus_object_methods));
  441. return false;
  442. }
  443. ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) {
  444. func = ucv_object_get(ubus_method_definition, "call", NULL);
  445. args = ucv_object_get(ubus_method_definition, "args", NULL);
  446. if (ucv_type(ubus_method_definition) != UC_OBJECT) {
  447. fprintf(stderr, "Invalid method definition for ucode script %s, object %s, method %s"
  448. " - expected dictionary, got %s\n",
  449. script->path, ubus_object_name, ubus_method_name, ucv_typename(ubus_method_definition));
  450. return false;
  451. }
  452. if (!ucv_is_callable(func)) {
  453. fprintf(stderr, "Invalid method callback for ucode script %s, object %s, method %s"
  454. " - expected callable, got %s\n",
  455. script->path, ubus_object_name, ubus_method_name, ucv_typename(func));
  456. return false;
  457. }
  458. if (args) {
  459. if (ucv_type(args) != UC_OBJECT) {
  460. fprintf(stderr, "Invalid method argument definition for ucode script %s, "
  461. "object %s, method %s - expected dictionary, got %s\n",
  462. script->path, ubus_object_name, ubus_method_name, ucv_typename(args));
  463. return false;
  464. }
  465. ucv_object_foreach(args, ubus_argument_name, ubus_argument_typehint) {
  466. switch (ucv_type(ubus_argument_typehint)) {
  467. case UC_BOOLEAN:
  468. case UC_INTEGER:
  469. case UC_DOUBLE:
  470. case UC_STRING:
  471. case UC_ARRAY:
  472. case UC_OBJECT:
  473. continue;
  474. default:
  475. fprintf(stderr, "Unsupported argument type for ucode script %s, object %s, "
  476. "method %s, argument %s - expected boolean, integer, string, "
  477. "array or object, got %s\n",
  478. script->path, ubus_object_name, ubus_method_name, ubus_argument_name,
  479. ucv_typename(ubus_argument_typehint));
  480. return false;
  481. }
  482. }
  483. }
  484. }
  485. }
  486. return true;
  487. }
  488. static bool
  489. rpc_ucode_method_register(struct ubus_method *method, const char *ubus_method_name, uc_value_t *ubus_method_arguments)
  490. {
  491. struct blobmsg_policy *policy;
  492. enum blobmsg_type type;
  493. method->name = strdup(ubus_method_name);
  494. if (!method->name) {
  495. fprintf(stderr, "Unable to allocate ubus method name: %s\n",
  496. strerror(errno));
  497. return false;
  498. }
  499. method->policy = calloc(ucv_object_length(ubus_method_arguments), sizeof(*method->policy));
  500. if (!method->policy) {
  501. fprintf(stderr, "Unable to allocate ubus method argument policy: %s\n",
  502. strerror(errno));
  503. return false;
  504. }
  505. method->handler = rpc_ucode_script_call;
  506. ucv_object_foreach(ubus_method_arguments, ubus_argument_name, ubus_argument_typehint) {
  507. switch (ucv_type(ubus_argument_typehint)) {
  508. case UC_BOOLEAN:
  509. type = BLOBMSG_TYPE_INT8;
  510. break;
  511. case UC_INTEGER:
  512. switch (ucv_int64_get(ubus_argument_typehint)) {
  513. case 8:
  514. type = BLOBMSG_TYPE_INT8;
  515. break;
  516. case 16:
  517. type = BLOBMSG_TYPE_INT16;
  518. break;
  519. case 64:
  520. type = BLOBMSG_TYPE_INT64;
  521. break;
  522. default:
  523. type = BLOBMSG_TYPE_INT32;
  524. break;
  525. }
  526. break;
  527. case UC_DOUBLE:
  528. type = BLOBMSG_TYPE_DOUBLE;
  529. break;
  530. case UC_ARRAY:
  531. type = BLOBMSG_TYPE_ARRAY;
  532. break;
  533. case UC_OBJECT:
  534. type = BLOBMSG_TYPE_TABLE;
  535. break;
  536. default:
  537. type = BLOBMSG_TYPE_STRING;
  538. break;
  539. }
  540. policy = (struct blobmsg_policy *)&method->policy[method->n_policy++];
  541. policy->type = type;
  542. policy->name = strdup(ubus_argument_name);
  543. if (!policy->name) {
  544. fprintf(stderr, "Unable to allocate ubus method argument name: %s\n",
  545. strerror(errno));
  546. return false;
  547. }
  548. }
  549. return true;
  550. }
  551. static bool
  552. rpc_ucode_script_register(struct ubus_context *ctx, rpc_ucode_script_t *script)
  553. {
  554. uc_value_t *signature = uc_vm_registry_get(&script->vm, "rpcd.ucode.signature");
  555. const struct blobmsg_policy *policy;
  556. rpc_ucode_ubus_obj_t *uuobj = NULL;
  557. char *tptr, *tnptr, *onptr, *mptr;
  558. struct ubus_method *method;
  559. struct ubus_object *obj;
  560. size_t typelen, namelen;
  561. uc_value_t *args;
  562. int rv;
  563. if (!rpc_ucode_script_validate(script))
  564. return false;
  565. ucv_object_foreach(signature, ubus_object_name, ubus_object_methods) {
  566. namelen = strlen(ubus_object_name);
  567. typelen = strlen("rpcd-plugin-ucode-") + namelen;
  568. uuobj = calloc_a(sizeof(*uuobj),
  569. &onptr, namelen + 1,
  570. &mptr, ucv_object_length(ubus_object_methods) * sizeof(struct ubus_method),
  571. &tptr, sizeof(struct ubus_object_type),
  572. &tnptr, typelen + 1);
  573. if (!uuobj) {
  574. fprintf(stderr, "Unable to allocate ubus object signature: %s\n",
  575. strerror(errno));
  576. continue;
  577. }
  578. list_add(&uuobj->list, &uuobjs);
  579. uuobj->script = script;
  580. uuobj->signature = ubus_object_methods;
  581. snprintf(tnptr, typelen, "rpcd-plugin-ucode-%s", ubus_object_name);
  582. method = (struct ubus_method *)mptr;
  583. obj = &uuobj->ubusobj;
  584. obj->name = strncpy(onptr, ubus_object_name, namelen);
  585. obj->methods = method;
  586. obj->type = (struct ubus_object_type *)tptr;
  587. obj->type->name = tnptr;
  588. obj->type->methods = obj->methods;
  589. ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) {
  590. args = ucv_object_get(ubus_method_definition, "args", NULL);
  591. if (!rpc_ucode_method_register(&method[obj->n_methods++], ubus_method_name, args))
  592. goto free;
  593. }
  594. obj->type = (struct ubus_object_type *)tptr;
  595. obj->type->name = tnptr;
  596. obj->type->methods = obj->methods;
  597. obj->type->n_methods = obj->n_methods;
  598. rv = ubus_add_object(ctx, obj);
  599. if (rv != UBUS_STATUS_OK) {
  600. fprintf(stderr, "Unable to register ubus object %s: %s\n",
  601. obj->name, ubus_strerror(rv));
  602. goto free;
  603. }
  604. continue;
  605. free:
  606. for (; obj->n_methods > 0; method++, obj->n_methods--) {
  607. for (policy = method->policy; method->n_policy > 0; policy++, method->n_policy--)
  608. free((char *)policy->name);
  609. free((char *)method->name);
  610. free((char *)method->policy);
  611. }
  612. free(uuobj);
  613. }
  614. return true;
  615. }
  616. static uc_value_t *
  617. rpc_ucode_request_reply(uc_vm_t *vm, size_t nargs)
  618. {
  619. rpc_ucode_call_ctx_t **callctx = uc_fn_this("rpcd.ucode.request");
  620. uc_value_t *reply = uc_fn_arg(0);
  621. uc_value_t *rcode = uc_fn_arg(1);
  622. int64_t code = UBUS_STATUS_OK;
  623. if (!callctx || !*callctx) {
  624. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  625. "Attempt to invoke reply() on invalid self");
  626. return NULL;
  627. }
  628. else if (reply && ucv_type(reply) != UC_OBJECT) {
  629. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  630. "First argument to reply() must be null or an object");
  631. return NULL;
  632. }
  633. else if (rcode && ucv_type(rcode) != UC_INTEGER) {
  634. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  635. "Second argument to reply() must be null or an integer");
  636. return NULL;
  637. }
  638. if ((*callctx)->replied) {
  639. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  640. "Reply has already been sent");
  641. return NULL;
  642. }
  643. if (rcode) {
  644. code = ucv_int64_get(rcode);
  645. if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST)
  646. code = UBUS_STATUS_UNKNOWN_ERROR;
  647. }
  648. rpc_ucode_request_finish(*callctx, code, reply);
  649. return NULL;
  650. }
  651. static uc_value_t *
  652. rpc_ucode_request_error(uc_vm_t *vm, size_t nargs)
  653. {
  654. rpc_ucode_call_ctx_t **callctx = uc_fn_this("rpcd.ucode.request");
  655. uc_value_t *rcode = uc_fn_arg(0);
  656. int64_t code;
  657. if (!callctx || !*callctx) {
  658. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  659. "Attempt to invoke error() on invalid self");
  660. return NULL;
  661. }
  662. else if (ucv_type(rcode) != UC_INTEGER) {
  663. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  664. "First argument to error() must be an integer");
  665. return NULL;
  666. }
  667. if ((*callctx)->replied) {
  668. uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
  669. "Reply has already been sent");
  670. return NULL;
  671. }
  672. code = ucv_int64_get(rcode);
  673. if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST)
  674. code = UBUS_STATUS_UNKNOWN_ERROR;
  675. rpc_ucode_request_finish(*callctx, code, NULL);
  676. return NULL;
  677. }
  678. static const uc_function_list_t rpc_ucode_request_fns[] = {
  679. { "reply", rpc_ucode_request_reply },
  680. { "error", rpc_ucode_request_error },
  681. };
  682. static void
  683. rpc_ucode_request_gc(void *ud)
  684. {
  685. rpc_ucode_call_ctx_t *callctx = ud;
  686. uloop_timeout_cancel(&callctx->timeout);
  687. free(callctx);
  688. }
  689. static void
  690. rpc_ucode_init_globals(rpc_ucode_script_t *script)
  691. {
  692. uc_vm_t *vm = &script->vm;
  693. uc_value_t *scope = uc_vm_scope_get(vm);
  694. #define status_const(name) \
  695. ucv_object_add(scope, #name, ucv_uint64_new(name))
  696. status_const(UBUS_STATUS_OK);
  697. status_const(UBUS_STATUS_INVALID_COMMAND);
  698. status_const(UBUS_STATUS_INVALID_ARGUMENT);
  699. status_const(UBUS_STATUS_METHOD_NOT_FOUND);
  700. status_const(UBUS_STATUS_NOT_FOUND);
  701. status_const(UBUS_STATUS_NO_DATA);
  702. status_const(UBUS_STATUS_PERMISSION_DENIED);
  703. status_const(UBUS_STATUS_TIMEOUT);
  704. status_const(UBUS_STATUS_NOT_SUPPORTED);
  705. status_const(UBUS_STATUS_UNKNOWN_ERROR);
  706. status_const(UBUS_STATUS_CONNECTION_FAILED);
  707. #undef status_const
  708. uc_stdlib_load(scope);
  709. script->requesttype = uc_type_declare(vm, "rpcd.ucode.request",
  710. rpc_ucode_request_fns, rpc_ucode_request_gc);
  711. }
  712. static rpc_ucode_script_t *
  713. rpc_ucode_script_execute(struct ubus_context *ctx, const char *path, uc_program_t *prog)
  714. {
  715. rpc_ucode_script_t *script;
  716. uc_value_t *signature;
  717. uc_vm_status_t status;
  718. size_t pathlen;
  719. char *pptr;
  720. pathlen = strlen(path);
  721. script = calloc_a(sizeof(*script), &pptr, pathlen + 1);
  722. if (!script) {
  723. fprintf(stderr, "Unable to allocate context for ucode script %s: %s\n",
  724. path, strerror(errno));
  725. uc_program_put(prog);
  726. return NULL;
  727. }
  728. script->path = strncpy(pptr, path, pathlen);
  729. uc_vm_init(&script->vm, &config);
  730. rpc_ucode_init_globals(script);
  731. status = uc_vm_execute(&script->vm, prog, &signature);
  732. script->pending_replies = ucv_array_new(&script->vm);
  733. uc_vm_registry_set(&script->vm, "rpcd.ucode.signature", signature);
  734. uc_vm_registry_set(&script->vm, "rpcd.ucode.deferreds", script->pending_replies);
  735. uc_program_put(prog);
  736. ucv_gc(&script->vm);
  737. switch (status) {
  738. case STATUS_OK:
  739. if (rpc_ucode_script_register(ctx, script))
  740. return script;
  741. fprintf(stderr, "Skipping registration of ucode script %s\n", path);
  742. break;
  743. case STATUS_EXIT:
  744. fprintf(stderr, "The ucode script %s invoked exit(%" PRId64 ")\n",
  745. path, ucv_int64_get(signature));
  746. break;
  747. case ERROR_COMPILE:
  748. fprintf(stderr, "Compilation error while executing ucode script %s\n", path);
  749. break;
  750. case ERROR_RUNTIME:
  751. fprintf(stderr, "Runtime error while executing ucode script %s\n", path);
  752. break;
  753. }
  754. uc_vm_free(&script->vm);
  755. free(script);
  756. return NULL;
  757. }
  758. static int
  759. rpc_ucode_init_script(struct ubus_context *ctx, const char *path)
  760. {
  761. rpc_ucode_script_t *script;
  762. uc_program_t *prog;
  763. uc_source_t *src;
  764. src = uc_source_new_file(path);
  765. if (!src) {
  766. fprintf(stderr, "Unable to open ucode script %s: %s\n",
  767. path, strerror(errno));
  768. return UBUS_STATUS_UNKNOWN_ERROR;
  769. }
  770. prog = rpc_ucode_script_compile(path, src);
  771. if (!prog)
  772. return UBUS_STATUS_UNKNOWN_ERROR;
  773. script = rpc_ucode_script_execute(ctx, path, prog);
  774. if (!script)
  775. return UBUS_STATUS_UNKNOWN_ERROR;
  776. list_add(&script->list, &scripts);
  777. return UBUS_STATUS_OK;
  778. }
  779. static int
  780. rpc_ucode_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx)
  781. {
  782. char path[PATH_MAX];
  783. struct dirent *e;
  784. struct stat s;
  785. int rv = 0;
  786. DIR *d;
  787. request_timeout = *ops->exec_timeout;
  788. /* reopen ucode.so with RTLD_GLOBAL in order to export libucode runtime
  789. * symbols for ucode extensions loaded later at runtime */
  790. if (!dlopen(RPC_LIBRARY_DIRECTORY "/ucode.so", RTLD_LAZY|RTLD_GLOBAL)) {
  791. fprintf(stderr, "Failed to dlopen() ucode.so: %s, dynamic ucode plugins may fail\n",
  792. dlerror());
  793. }
  794. /* initialize default module search path */
  795. uc_search_path_init(&config.module_search_path);
  796. if ((d = opendir(RPC_UCSCRIPT_DIRECTORY)) != NULL) {
  797. while ((e = readdir(d)) != NULL) {
  798. snprintf(path, sizeof(path), RPC_UCSCRIPT_DIRECTORY "/%s", e->d_name);
  799. if (stat(path, &s) || !S_ISREG(s.st_mode))
  800. continue;
  801. if (s.st_mode & S_IWOTH) {
  802. fprintf(stderr, "Ignoring ucode script %s because it is world writable\n",
  803. path);
  804. continue;
  805. }
  806. rv |= rpc_ucode_init_script(ctx, path);
  807. }
  808. closedir(d);
  809. }
  810. return rv;
  811. }
  812. struct rpc_plugin rpc_plugin = {
  813. .init = rpc_ucode_api_init
  814. };