cli.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /*
  2. * Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License version 2.1
  6. * as published by the Free Software Foundation
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include <unistd.h>
  14. #include <libubox/blobmsg_json.h>
  15. #include "libubus.h"
  16. static struct blob_buf b;
  17. static int listen_timeout;
  18. static int timeout = 30;
  19. static bool simple_output = false;
  20. static int verbose = 0;
  21. static int monitor_dir = -1;
  22. static uint32_t monitor_mask;
  23. static const char * const monitor_types[] = {
  24. [UBUS_MSG_HELLO] = "hello",
  25. [UBUS_MSG_STATUS] = "status",
  26. [UBUS_MSG_DATA] = "data",
  27. [UBUS_MSG_PING] = "ping",
  28. [UBUS_MSG_LOOKUP] = "lookup",
  29. [UBUS_MSG_INVOKE] = "invoke",
  30. [UBUS_MSG_ADD_OBJECT] = "add_object",
  31. [UBUS_MSG_REMOVE_OBJECT] = "remove_object",
  32. [UBUS_MSG_SUBSCRIBE] = "subscribe",
  33. [UBUS_MSG_UNSUBSCRIBE] = "unsubscribe",
  34. [UBUS_MSG_NOTIFY] = "notify",
  35. };
  36. static const char *format_type(void *priv, struct blob_attr *attr)
  37. {
  38. static const char * const attr_types[] = {
  39. [BLOBMSG_TYPE_INT8] = "\"Boolean\"",
  40. [BLOBMSG_TYPE_INT32] = "\"Integer\"",
  41. [BLOBMSG_TYPE_STRING] = "\"String\"",
  42. [BLOBMSG_TYPE_ARRAY] = "\"Array\"",
  43. [BLOBMSG_TYPE_TABLE] = "\"Table\"",
  44. };
  45. const char *type = NULL;
  46. size_t typeid;
  47. if (blob_id(attr) != BLOBMSG_TYPE_INT32)
  48. return NULL;
  49. typeid = blobmsg_get_u32(attr);
  50. if (typeid < ARRAY_SIZE(attr_types))
  51. type = attr_types[typeid];
  52. if (!type)
  53. type = "\"(unknown)\"";
  54. return type;
  55. }
  56. static void receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
  57. {
  58. struct blob_attr *cur;
  59. char *s;
  60. size_t rem;
  61. if (simple_output || !verbose) {
  62. printf("%s\n", obj->path);
  63. return;
  64. }
  65. printf("'%s' @%08x\n", obj->path, obj->id);
  66. if (!obj->signature)
  67. return;
  68. blob_for_each_attr(cur, obj->signature, rem) {
  69. s = blobmsg_format_json_with_cb(cur, false, format_type, NULL, -1);
  70. printf("\t%s\n", s);
  71. free(s);
  72. }
  73. }
  74. static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg)
  75. {
  76. char *str;
  77. if (!msg)
  78. return;
  79. str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0);
  80. printf("%s\n", str);
  81. free(str);
  82. }
  83. static void print_event(const char *type, struct blob_attr *msg)
  84. {
  85. char *str;
  86. str = blobmsg_format_json(msg, true);
  87. printf("{ \"%s\": %s }\n", type, str);
  88. fflush(stdout);
  89. free(str);
  90. }
  91. static int receive_request(struct ubus_context *ctx, struct ubus_object *obj,
  92. struct ubus_request_data *req,
  93. const char *method, struct blob_attr *msg)
  94. {
  95. print_event(method, msg);
  96. return 0;
  97. }
  98. static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
  99. const char *type, struct blob_attr *msg)
  100. {
  101. print_event(type, msg);
  102. }
  103. static int ubus_cli_error(char *cmd, int argc, char **argv, int err)
  104. {
  105. int i;
  106. if (!simple_output && !isatty(fileno(stderr))) {
  107. fprintf(stderr, "Command failed: ubus %s ", cmd);
  108. for (i = 0; i < argc; i++) {
  109. fprintf(stderr, "%s ", argv[i]);
  110. }
  111. fprintf(stderr, "(%s)\n", ubus_strerror(err));
  112. return -err;
  113. }
  114. return err;
  115. }
  116. static int ubus_cli_list(struct ubus_context *ctx, int argc, char **argv)
  117. {
  118. const char *path = NULL;
  119. if (argc > 1)
  120. return -2;
  121. if (argc == 1)
  122. path = argv[0];
  123. return ubus_lookup(ctx, path, receive_list_result, NULL);
  124. }
  125. static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv)
  126. {
  127. uint32_t id;
  128. int ret;
  129. if (argc < 2 || argc > 3)
  130. return -2;
  131. blob_buf_init(&b, 0);
  132. if (argc == 3 && !blobmsg_add_json_from_string(&b, argv[2])) {
  133. return ubus_cli_error("call", argc, argv, UBUS_STATUS_PARSE_ERROR);
  134. }
  135. ret = ubus_lookup_id(ctx, argv[0], &id);
  136. if (ret)
  137. return ret;
  138. ret = ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000);
  139. if (ret)
  140. return ubus_cli_error("call", argc, argv, ret);
  141. return ret;
  142. }
  143. struct cli_listen_data {
  144. struct uloop_timeout timeout;
  145. bool timed_out;
  146. };
  147. static void ubus_cli_listen_timeout(struct uloop_timeout *timeout)
  148. {
  149. struct cli_listen_data *data = container_of(timeout, struct cli_listen_data, timeout);
  150. data->timed_out = true;
  151. uloop_end();
  152. }
  153. static void do_listen(struct ubus_context *ctx, struct cli_listen_data *data)
  154. {
  155. memset(data, 0, sizeof(*data));
  156. data->timeout.cb = ubus_cli_listen_timeout;
  157. uloop_init();
  158. ubus_add_uloop(ctx);
  159. if (listen_timeout)
  160. uloop_timeout_set(&data->timeout, listen_timeout * 1000);
  161. uloop_run();
  162. uloop_done();
  163. }
  164. static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv)
  165. {
  166. struct ubus_event_handler ev = {
  167. .cb = receive_event,
  168. };
  169. struct cli_listen_data data;
  170. const char *event;
  171. int ret = 0;
  172. if (argc > 0) {
  173. event = argv[0];
  174. } else {
  175. event = "*";
  176. argc = 1;
  177. }
  178. do {
  179. ret = ubus_register_event_handler(ctx, &ev, event);
  180. if (ret)
  181. break;
  182. argv++;
  183. argc--;
  184. if (argc <= 0)
  185. break;
  186. event = argv[0];
  187. } while (1);
  188. if (ret) {
  189. if (!simple_output)
  190. fprintf(stderr, "Error while registering for event '%s': %s\n",
  191. event, ubus_strerror(ret));
  192. return -1;
  193. }
  194. do_listen(ctx, &data);
  195. return 0;
  196. }
  197. static int ubus_cli_subscribe(struct ubus_context *ctx, int argc, char **argv)
  198. {
  199. struct ubus_subscriber sub = {
  200. .cb = receive_request,
  201. };
  202. struct cli_listen_data data;
  203. const char *event;
  204. int ret = 0;
  205. if (argc > 0) {
  206. event = argv[0];
  207. } else {
  208. if (!simple_output)
  209. fprintf(stderr, "You need to specify an object to subscribe to\n");
  210. return -1;
  211. }
  212. ret = ubus_register_subscriber(ctx, &sub);
  213. for (; !ret && argc > 0; argc--, argv++) {
  214. uint32_t id;
  215. ret = ubus_lookup_id(ctx, argv[0], &id);
  216. if (ret)
  217. break;
  218. ret = ubus_subscribe(ctx, &sub, id);
  219. }
  220. if (ret) {
  221. if (!simple_output)
  222. fprintf(stderr, "Error while registering for event '%s': %s\n",
  223. event, ubus_strerror(ret));
  224. return -1;
  225. }
  226. do_listen(ctx, &data);
  227. return 0;
  228. }
  229. static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv)
  230. {
  231. if (argc < 1 || argc > 2)
  232. return -2;
  233. blob_buf_init(&b, 0);
  234. if (argc == 2 && !blobmsg_add_json_from_string(&b, argv[1])) {
  235. return UBUS_STATUS_PARSE_ERROR;
  236. }
  237. return ubus_send_event(ctx, argv[0], b.head);
  238. }
  239. struct cli_wait_data {
  240. struct uloop_timeout timeout;
  241. struct ubus_event_handler ev;
  242. char **pending;
  243. int n_pending;
  244. };
  245. static void wait_check_object(struct cli_wait_data *data, const char *path)
  246. {
  247. int i;
  248. for (i = 0; i < data->n_pending; i++) {
  249. if (strcmp(path, data->pending[i]) != 0)
  250. continue;
  251. data->n_pending--;
  252. if (i == data->n_pending)
  253. break;
  254. memmove(&data->pending[i], &data->pending[i + 1],
  255. (data->n_pending - i) * sizeof(*data->pending));
  256. i--;
  257. }
  258. if (!data->n_pending)
  259. uloop_end();
  260. }
  261. static void wait_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
  262. const char *type, struct blob_attr *msg)
  263. {
  264. static const struct blobmsg_policy policy = {
  265. "path", BLOBMSG_TYPE_STRING
  266. };
  267. struct cli_wait_data *data = container_of(ev, struct cli_wait_data, ev);
  268. struct blob_attr *attr;
  269. const char *path;
  270. if (strcmp(type, "ubus.object.add") != 0)
  271. return;
  272. blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg));
  273. if (!attr)
  274. return;
  275. path = blobmsg_data(attr);
  276. wait_check_object(data, path);
  277. }
  278. static void wait_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
  279. {
  280. struct cli_wait_data *data = priv;
  281. wait_check_object(data, obj->path);
  282. }
  283. static void wait_timeout(struct uloop_timeout *timeout)
  284. {
  285. uloop_end();
  286. }
  287. static int ubus_cli_wait_for(struct ubus_context *ctx, int argc, char **argv)
  288. {
  289. struct cli_wait_data data = {
  290. .timeout.cb = wait_timeout,
  291. .ev.cb = wait_event_cb,
  292. .pending = argv,
  293. .n_pending = argc,
  294. };
  295. int ret;
  296. if (argc < 1)
  297. return -2;
  298. uloop_init();
  299. ubus_add_uloop(ctx);
  300. ret = ubus_register_event_handler(ctx, &data.ev, "ubus.object.add");
  301. if (ret)
  302. return ret;
  303. if (!data.n_pending)
  304. return ret;
  305. ret = ubus_lookup(ctx, NULL, wait_list_cb, &data);
  306. if (ret)
  307. return ret;
  308. if (!data.n_pending)
  309. return ret;
  310. uloop_timeout_set(&data.timeout, timeout * 1000);
  311. uloop_run();
  312. uloop_done();
  313. if (data.n_pending)
  314. return UBUS_STATUS_TIMEOUT;
  315. return ret;
  316. }
  317. static const char *
  318. ubus_cli_msg_type(uint32_t type)
  319. {
  320. const char *ret = NULL;
  321. static char unk_type[16];
  322. if (type < ARRAY_SIZE(monitor_types))
  323. ret = monitor_types[type];
  324. if (!ret) {
  325. snprintf(unk_type, sizeof(unk_type), "%d", type);
  326. ret = unk_type;
  327. }
  328. return ret;
  329. }
  330. static char *
  331. ubus_cli_get_monitor_data(struct blob_attr *data)
  332. {
  333. static const struct blob_attr_info policy[UBUS_ATTR_MAX] = {
  334. [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 },
  335. [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING },
  336. [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 },
  337. [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING },
  338. [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 },
  339. [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED },
  340. [UBUS_ATTR_DATA] = { .type = BLOB_ATTR_NESTED },
  341. [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 },
  342. [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 },
  343. [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING },
  344. [UBUS_ATTR_GROUP] = { .type = BLOB_ATTR_STRING },
  345. };
  346. static const char * const names[UBUS_ATTR_MAX] = {
  347. [UBUS_ATTR_STATUS] = "status",
  348. [UBUS_ATTR_OBJPATH] = "objpath",
  349. [UBUS_ATTR_OBJID] = "objid",
  350. [UBUS_ATTR_METHOD] = "method",
  351. [UBUS_ATTR_OBJTYPE] = "objtype",
  352. [UBUS_ATTR_SIGNATURE] = "signature",
  353. [UBUS_ATTR_DATA] = "data",
  354. [UBUS_ATTR_ACTIVE] = "active",
  355. [UBUS_ATTR_NO_REPLY] = "no_reply",
  356. [UBUS_ATTR_USER] = "user",
  357. [UBUS_ATTR_GROUP] = "group",
  358. };
  359. struct blob_attr *tb[UBUS_ATTR_MAX];
  360. int i;
  361. blob_buf_init(&b, 0);
  362. blob_parse(data, tb, policy, UBUS_ATTR_MAX);
  363. for (i = 0; i < UBUS_ATTR_MAX; i++) {
  364. const char *n = names[i];
  365. struct blob_attr *v = tb[i];
  366. if (!tb[i] || !n)
  367. continue;
  368. switch(policy[i].type) {
  369. case BLOB_ATTR_INT32:
  370. blobmsg_add_u32(&b, n, blob_get_int32(v));
  371. break;
  372. case BLOB_ATTR_STRING:
  373. blobmsg_add_string(&b, n, blob_data(v));
  374. break;
  375. case BLOB_ATTR_INT8:
  376. blobmsg_add_u8(&b, n, !!blob_get_int8(v));
  377. break;
  378. case BLOB_ATTR_NESTED:
  379. blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, n, blobmsg_data(v), blobmsg_data_len(v));
  380. break;
  381. }
  382. }
  383. return blobmsg_format_json(b.head, true);
  384. }
  385. static void
  386. ubus_cli_monitor_cb(struct ubus_context *ctx, uint32_t seq, struct blob_attr *msg)
  387. {
  388. static const struct blob_attr_info policy[UBUS_MONITOR_MAX] = {
  389. [UBUS_MONITOR_CLIENT] = { .type = BLOB_ATTR_INT32 },
  390. [UBUS_MONITOR_PEER] = { .type = BLOB_ATTR_INT32 },
  391. [UBUS_MONITOR_SEND] = { .type = BLOB_ATTR_INT8 },
  392. [UBUS_MONITOR_TYPE] = { .type = BLOB_ATTR_INT32 },
  393. [UBUS_MONITOR_DATA] = { .type = BLOB_ATTR_NESTED },
  394. };
  395. struct blob_attr *tb[UBUS_MONITOR_MAX];
  396. uint32_t client, peer, type;
  397. bool send;
  398. char *data;
  399. blob_parse_untrusted(msg, blob_raw_len(msg), tb, policy, UBUS_MONITOR_MAX);
  400. if (!tb[UBUS_MONITOR_CLIENT] ||
  401. !tb[UBUS_MONITOR_PEER] ||
  402. !tb[UBUS_MONITOR_SEND] ||
  403. !tb[UBUS_MONITOR_TYPE] ||
  404. !tb[UBUS_MONITOR_DATA]) {
  405. printf("Invalid monitor msg\n");
  406. return;
  407. }
  408. send = blob_get_int32(tb[UBUS_MONITOR_SEND]);
  409. client = blob_get_int32(tb[UBUS_MONITOR_CLIENT]);
  410. peer = blob_get_int32(tb[UBUS_MONITOR_PEER]);
  411. type = blob_get_int32(tb[UBUS_MONITOR_TYPE]);
  412. if (monitor_mask && type < 32 && !(monitor_mask & (1 << type)))
  413. return;
  414. if (monitor_dir >= 0 && send != monitor_dir)
  415. return;
  416. data = ubus_cli_get_monitor_data(tb[UBUS_MONITOR_DATA]);
  417. printf("%s %08x #%08x %14s: %s\n", send ? "->" : "<-", client, peer, ubus_cli_msg_type(type), data);
  418. free(data);
  419. fflush(stdout);
  420. }
  421. static int ubus_cli_monitor(struct ubus_context *ctx, int argc, char **argv)
  422. {
  423. int ret;
  424. uloop_init();
  425. ubus_add_uloop(ctx);
  426. ctx->monitor_cb = ubus_cli_monitor_cb;
  427. ret = ubus_monitor_start(ctx);
  428. if (ret)
  429. return ret;
  430. uloop_run();
  431. uloop_done();
  432. ubus_monitor_stop(ctx);
  433. return 0;
  434. }
  435. static int add_monitor_type(const char *type)
  436. {
  437. size_t i;
  438. for (i = 0; i < ARRAY_SIZE(monitor_types); i++) {
  439. if (!monitor_types[i] || strcmp(monitor_types[i], type) != 0)
  440. continue;
  441. monitor_mask |= 1 << i;
  442. return 0;
  443. }
  444. return -1;
  445. }
  446. static int usage(const char *prog)
  447. {
  448. fprintf(stderr,
  449. "Usage: %s [<options>] <command> [arguments...]\n"
  450. "Options:\n"
  451. " -s <socket>: Set the unix domain socket to connect to\n"
  452. " -t <timeout>: Set the timeout (in seconds) for a command to complete\n"
  453. " -S: Use simplified output (for scripts)\n"
  454. " -v: More verbose output\n"
  455. " -m <type>: (for monitor): include a specific message type\n"
  456. " (can be used more than once)\n"
  457. " -M <r|t> (for monitor): only capture received or transmitted traffic\n"
  458. "\n"
  459. "Commands:\n"
  460. " - list [<path>] List objects\n"
  461. " - call <path> <method> [<message>] Call an object method\n"
  462. " - subscribe <path> [<path>...] Subscribe to object(s) notifications\n"
  463. " - listen [<path>...] Listen for events\n"
  464. " - send <type> [<message>] Send an event\n"
  465. " - wait_for <object> [<object>...] Wait for multiple objects to appear on ubus\n"
  466. " - monitor Monitor ubus traffic\n"
  467. "\n", prog);
  468. return 1;
  469. }
  470. static struct {
  471. const char *name;
  472. int (*cb)(struct ubus_context *ctx, int argc, char **argv);
  473. } commands[] = {
  474. { "list", ubus_cli_list },
  475. { "call", ubus_cli_call },
  476. { "listen", ubus_cli_listen },
  477. { "subscribe", ubus_cli_subscribe },
  478. { "send", ubus_cli_send },
  479. { "wait_for", ubus_cli_wait_for },
  480. { "monitor", ubus_cli_monitor },
  481. };
  482. int main(int argc, char **argv)
  483. {
  484. const char *progname, *ubus_socket = NULL;
  485. struct ubus_context *ctx;
  486. int ret = 0;
  487. char *cmd;
  488. size_t i;
  489. int ch;
  490. progname = argv[0];
  491. while ((ch = getopt(argc, argv, "m:M:vs:t:S")) != -1) {
  492. switch (ch) {
  493. case 's':
  494. ubus_socket = optarg;
  495. break;
  496. case 't':
  497. listen_timeout = atoi(optarg);
  498. timeout = atoi(optarg);
  499. break;
  500. case 'S':
  501. simple_output = true;
  502. break;
  503. case 'v':
  504. verbose++;
  505. break;
  506. case 'm':
  507. if (add_monitor_type(optarg))
  508. return usage(progname);
  509. break;
  510. case 'M':
  511. switch (optarg[0]) {
  512. case 'r':
  513. monitor_dir = 0;
  514. break;
  515. case 't':
  516. monitor_dir = 1;
  517. break;
  518. default:
  519. return usage(progname);
  520. }
  521. break;
  522. default:
  523. return usage(progname);
  524. }
  525. }
  526. argc -= optind;
  527. argv += optind;
  528. cmd = argv[0];
  529. if (argc < 1)
  530. return usage(progname);
  531. ctx = ubus_connect(ubus_socket);
  532. if (!ctx) {
  533. if (!simple_output)
  534. fprintf(stderr, "Failed to connect to ubus\n");
  535. return -1;
  536. }
  537. argv++;
  538. argc--;
  539. ret = -2;
  540. for (i = 0; i < ARRAY_SIZE(commands); i++) {
  541. if (strcmp(commands[i].name, cmd) != 0)
  542. continue;
  543. ret = commands[i].cb(ctx, argc, argv);
  544. break;
  545. }
  546. if (ret > 0 && !simple_output)
  547. fprintf(stderr, "Command failed: %s\n", ubus_strerror(ret));
  548. else if (ret == -2)
  549. usage(progname);
  550. ubus_free(ctx);
  551. return ret;
  552. }