plugin.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. * rpcd - UBUS RPC server
  3. *
  4. * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
  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 <rpcd/plugin.h>
  19. static struct blob_buf buf;
  20. struct rpc_plugin_lookup_context {
  21. uint32_t id;
  22. char *name;
  23. bool found;
  24. };
  25. static void
  26. rpc_plugin_lookup_plugin_cb(struct ubus_context *ctx,
  27. struct ubus_object_data *obj, void *priv)
  28. {
  29. struct rpc_plugin_lookup_context *c = priv;
  30. if (c->id == obj->id)
  31. {
  32. c->found = true;
  33. sprintf(c->name, "%s", obj->path);
  34. }
  35. }
  36. static bool
  37. rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj,
  38. char *strptr)
  39. {
  40. struct rpc_plugin_lookup_context c = { .id = obj->id, .name = strptr };
  41. if (ubus_lookup(ctx, NULL, rpc_plugin_lookup_plugin_cb, &c))
  42. return false;
  43. return c.found;
  44. }
  45. static void
  46. rpc_plugin_json_array_to_blob(struct array_list *a, struct blob_buf *blob);
  47. static void
  48. rpc_plugin_json_object_to_blob(json_object *o, struct blob_buf *blob);
  49. static void
  50. rpc_plugin_json_element_to_blob(const char *name, json_object *val,
  51. struct blob_buf *blob)
  52. {
  53. void *c;
  54. int64_t n;
  55. switch (json_object_get_type(val)) {
  56. case json_type_object:
  57. c = blobmsg_open_table(blob, name);
  58. rpc_plugin_json_object_to_blob(val, blob);
  59. blobmsg_close_table(blob, c);
  60. break;
  61. case json_type_array:
  62. c = blobmsg_open_array(blob, name);
  63. rpc_plugin_json_array_to_blob(json_object_get_array(val), blob);
  64. blobmsg_close_array(blob, c);
  65. break;
  66. case json_type_string:
  67. blobmsg_add_string(blob, name, json_object_get_string(val));
  68. break;
  69. case json_type_boolean:
  70. blobmsg_add_u8(blob, name, json_object_get_boolean(val));
  71. break;
  72. case json_type_int:
  73. n = json_object_get_int64(val);
  74. if (n >= INT32_MIN && n <= INT32_MAX)
  75. blobmsg_add_u32(blob, name, n);
  76. else
  77. blobmsg_add_u64(blob, name, n);
  78. break;
  79. case json_type_double:
  80. blobmsg_add_double(blob, name, json_object_get_double(val));
  81. break;
  82. case json_type_null:
  83. blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0);
  84. break;
  85. }
  86. }
  87. static void
  88. rpc_plugin_json_array_to_blob(struct array_list *a, struct blob_buf *blob)
  89. {
  90. int i, len;
  91. for (i = 0, len = array_list_length(a); i < len; i++)
  92. rpc_plugin_json_element_to_blob(NULL, array_list_get_idx(a, i), blob);
  93. }
  94. static void
  95. rpc_plugin_json_object_to_blob(json_object *o, struct blob_buf *blob)
  96. {
  97. json_object_object_foreach(o, key, val)
  98. rpc_plugin_json_element_to_blob(key, val, blob);
  99. }
  100. struct call_context {
  101. char path[PATH_MAX];
  102. const char *argv[4];
  103. char *method;
  104. char *input;
  105. json_tokener *tok;
  106. json_object *obj;
  107. bool input_done;
  108. bool output_done;
  109. };
  110. static int
  111. rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
  112. {
  113. struct call_context *c = priv;
  114. if (!c->input_done)
  115. {
  116. ustream_write(s, c->input, strlen(c->input), false);
  117. c->input_done = true;
  118. }
  119. return 0;
  120. }
  121. static int
  122. rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
  123. {
  124. struct call_context *c = priv;
  125. if (!c->output_done)
  126. {
  127. c->obj = json_tokener_parse_ex(c->tok, buf, len);
  128. if (json_tokener_get_error(c->tok) != json_tokener_continue)
  129. c->output_done = true;
  130. }
  131. return len;
  132. }
  133. static int
  134. rpc_plugin_call_stderr_cb(struct blob_buf *blob, char *buf, int len, void *priv)
  135. {
  136. return len;
  137. }
  138. static int
  139. rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
  140. {
  141. struct call_context *c = priv;
  142. int rv = UBUS_STATUS_INVALID_ARGUMENT;
  143. if (json_tokener_get_error(c->tok) == json_tokener_success)
  144. {
  145. if (c->obj)
  146. {
  147. if (json_object_get_type(c->obj) == json_type_object)
  148. {
  149. rpc_plugin_json_object_to_blob(c->obj, blob);
  150. rv = UBUS_STATUS_OK;
  151. }
  152. json_object_put(c->obj);
  153. }
  154. else
  155. {
  156. rv = UBUS_STATUS_NO_DATA;
  157. }
  158. }
  159. json_tokener_free(c->tok);
  160. free(c->input);
  161. return rv;
  162. }
  163. static int
  164. rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
  165. struct ubus_request_data *req, const char *method,
  166. struct blob_attr *msg)
  167. {
  168. int rv = UBUS_STATUS_UNKNOWN_ERROR;
  169. struct call_context *c;
  170. char *plugin, *mptr;
  171. c = calloc_a(sizeof(*c), &mptr, strlen(method) + 1);
  172. if (!c)
  173. goto fail;
  174. c->method = strcpy(mptr, method);
  175. c->input = blobmsg_format_json(msg, true);
  176. c->tok = json_tokener_new();
  177. if (!c->input || !c->tok)
  178. goto fail;
  179. plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
  180. if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
  181. {
  182. rv = UBUS_STATUS_NOT_FOUND;
  183. goto fail;
  184. }
  185. c->argv[0] = c->path;
  186. c->argv[1] = "call";
  187. c->argv[2] = c->method;
  188. rv = rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
  189. rpc_plugin_call_stdout_cb, rpc_plugin_call_stderr_cb,
  190. rpc_plugin_call_finish_cb, c, ctx, req);
  191. if (rv == UBUS_STATUS_OK)
  192. return rv;
  193. fail:
  194. if (c)
  195. {
  196. if (c->input)
  197. free(c->input);
  198. if (c->tok)
  199. json_tokener_free(c->tok);
  200. free(c);
  201. }
  202. return rv;
  203. }
  204. static bool
  205. rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
  206. {
  207. int rem, n_attr;
  208. enum blobmsg_type type;
  209. struct blob_attr *attr;
  210. struct blobmsg_policy *policy = NULL;
  211. if (!sig || blobmsg_type(sig) != BLOBMSG_TYPE_TABLE)
  212. return false;
  213. n_attr = 0;
  214. blobmsg_for_each_attr(attr, sig, rem)
  215. n_attr++;
  216. if (n_attr)
  217. {
  218. policy = calloc(n_attr, sizeof(*policy));
  219. if (!policy)
  220. return false;
  221. n_attr = 0;
  222. blobmsg_for_each_attr(attr, sig, rem)
  223. {
  224. type = blobmsg_type(attr);
  225. if (type == BLOBMSG_TYPE_INT32)
  226. {
  227. switch (blobmsg_get_u32(attr))
  228. {
  229. case 8:
  230. type = BLOBMSG_TYPE_INT8;
  231. break;
  232. case 16:
  233. type = BLOBMSG_TYPE_INT16;
  234. break;
  235. case 64:
  236. type = BLOBMSG_TYPE_INT64;
  237. break;
  238. default:
  239. type = BLOBMSG_TYPE_INT32;
  240. break;
  241. }
  242. }
  243. policy[n_attr].name = strdup(blobmsg_name(attr));
  244. policy[n_attr].type = type;
  245. n_attr++;
  246. }
  247. }
  248. method->name = strdup(blobmsg_name(sig));
  249. method->handler = rpc_plugin_call;
  250. method->policy = policy;
  251. method->n_policy = n_attr;
  252. return true;
  253. }
  254. static struct ubus_object *
  255. rpc_plugin_parse_exec(const char *name, int fd)
  256. {
  257. int len, rem, n_method;
  258. struct blob_attr *cur;
  259. struct ubus_method *methods;
  260. struct ubus_object_type *obj_type;
  261. struct ubus_object *obj;
  262. char outbuf[1024];
  263. json_tokener *tok;
  264. json_object *jsobj;
  265. blob_buf_init(&buf, 0);
  266. tok = json_tokener_new();
  267. if (!tok)
  268. return NULL;
  269. while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
  270. {
  271. jsobj = json_tokener_parse_ex(tok, outbuf, len);
  272. if (json_tokener_get_error(tok) == json_tokener_continue)
  273. continue;
  274. if (json_tokener_get_error(tok) != json_tokener_success)
  275. break;
  276. if (jsobj)
  277. {
  278. if (json_object_get_type(jsobj) == json_type_object)
  279. blobmsg_add_object(&buf, jsobj);
  280. json_object_put(jsobj);
  281. break;
  282. }
  283. }
  284. json_tokener_free(tok);
  285. n_method = 0;
  286. blob_for_each_attr(cur, buf.head, rem)
  287. n_method++;
  288. if (!n_method)
  289. return NULL;
  290. methods = calloc(n_method, sizeof(*methods));
  291. if (!methods)
  292. return NULL;
  293. n_method = 0;
  294. blob_for_each_attr(cur, buf.head, rem)
  295. {
  296. if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
  297. continue;
  298. n_method++;
  299. }
  300. obj = calloc(1, sizeof(*obj));
  301. if (!obj)
  302. return NULL;
  303. obj_type = calloc(1, sizeof(*obj_type));
  304. if (!obj_type) {
  305. free(obj);
  306. return NULL;
  307. }
  308. if (asprintf((char **)&obj_type->name, "rpcd-plugin-exec-%s", name) < 0) {
  309. free(obj);
  310. free(obj_type);
  311. return NULL;
  312. }
  313. obj_type->methods = methods;
  314. obj_type->n_methods = n_method;
  315. obj->name = strdup(name);
  316. obj->type = obj_type;
  317. obj->methods = methods;
  318. obj->n_methods = n_method;
  319. return obj;
  320. }
  321. static int
  322. rpc_plugin_register_exec(struct ubus_context *ctx, const char *path)
  323. {
  324. pid_t pid;
  325. int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
  326. const char *name;
  327. struct ubus_object *plugin;
  328. name = strrchr(path, '/');
  329. if (!name)
  330. return UBUS_STATUS_INVALID_ARGUMENT;
  331. if (pipe(fds))
  332. return UBUS_STATUS_UNKNOWN_ERROR;
  333. switch ((pid = fork()))
  334. {
  335. case -1:
  336. return UBUS_STATUS_UNKNOWN_ERROR;
  337. case 0:
  338. fd = open("/dev/null", O_RDWR);
  339. if (fd > -1)
  340. {
  341. dup2(fd, 0);
  342. dup2(fd, 2);
  343. if (fd > 2)
  344. close(fd);
  345. }
  346. dup2(fds[1], 1);
  347. close(fds[0]);
  348. close(fds[1]);
  349. if (execl(path, path, "list", NULL))
  350. return UBUS_STATUS_UNKNOWN_ERROR;
  351. default:
  352. plugin = rpc_plugin_parse_exec(name + 1, fds[0]);
  353. if (!plugin)
  354. goto out;
  355. rv = ubus_add_object(ctx, plugin);
  356. out:
  357. close(fds[0]);
  358. close(fds[1]);
  359. waitpid(pid, NULL, 0);
  360. return rv;
  361. }
  362. }
  363. static LIST_HEAD(plugins);
  364. static const struct rpc_daemon_ops ops = {
  365. .session_access = rpc_session_access,
  366. .session_create_cb = rpc_session_create_cb,
  367. .session_destroy_cb = rpc_session_destroy_cb,
  368. .exec = rpc_exec,
  369. .exec_timeout = &rpc_exec_timeout,
  370. };
  371. static int
  372. rpc_plugin_register_library(struct ubus_context *ctx, const char *path)
  373. {
  374. struct rpc_plugin *p;
  375. void *dlh;
  376. dlh = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
  377. if (!dlh) {
  378. fprintf(stderr, "Failed to load plugin %s: %s\n",
  379. path, dlerror());
  380. return UBUS_STATUS_UNKNOWN_ERROR;
  381. }
  382. p = dlsym(dlh, "rpc_plugin");
  383. if (!p)
  384. return UBUS_STATUS_NOT_FOUND;
  385. list_add(&p->list, &plugins);
  386. return p->init(&ops, ctx);
  387. }
  388. int rpc_plugin_api_init(struct ubus_context *ctx)
  389. {
  390. DIR *d;
  391. int rv = 0;
  392. struct stat s;
  393. struct dirent *e;
  394. char path[PATH_MAX];
  395. if ((d = opendir(RPC_PLUGIN_DIRECTORY)) != NULL)
  396. {
  397. while ((e = readdir(d)) != NULL)
  398. {
  399. snprintf(path, sizeof(path) - 1,
  400. RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
  401. if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
  402. continue;
  403. rv |= rpc_plugin_register_exec(ctx, path);
  404. }
  405. closedir(d);
  406. }
  407. if ((d = opendir(RPC_LIBRARY_DIRECTORY)) != NULL)
  408. {
  409. while ((e = readdir(d)) != NULL)
  410. {
  411. snprintf(path, sizeof(path) - 1,
  412. RPC_LIBRARY_DIRECTORY "/%s", e->d_name);
  413. if (stat(path, &s) || !S_ISREG(s.st_mode))
  414. continue;
  415. rv |= rpc_plugin_register_library(ctx, path);
  416. }
  417. closedir(d);
  418. }
  419. return rv;
  420. }