cli.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  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. int 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. int 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_list(struct ubus_context *ctx, int argc, char **argv)
  104. {
  105. const char *path = NULL;
  106. if (argc > 1)
  107. return -2;
  108. if (argc == 1)
  109. path = argv[0];
  110. return ubus_lookup(ctx, path, receive_list_result, NULL);
  111. }
  112. static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv)
  113. {
  114. uint32_t id;
  115. int ret;
  116. if (argc < 2 || argc > 3)
  117. return -2;
  118. blob_buf_init(&b, 0);
  119. if (argc == 3 && !blobmsg_add_json_from_string(&b, argv[2])) {
  120. if (!simple_output)
  121. fprintf(stderr, "Failed to parse message data\n");
  122. return -1;
  123. }
  124. ret = ubus_lookup_id(ctx, argv[0], &id);
  125. if (ret)
  126. return ret;
  127. return ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000);
  128. }
  129. struct cli_listen_data {
  130. struct uloop_timeout timeout;
  131. bool timed_out;
  132. };
  133. static void ubus_cli_listen_timeout(struct uloop_timeout *timeout)
  134. {
  135. struct cli_listen_data *data = container_of(timeout, struct cli_listen_data, timeout);
  136. data->timed_out = true;
  137. uloop_end();
  138. }
  139. static void do_listen(struct ubus_context *ctx, struct cli_listen_data *data)
  140. {
  141. memset(data, 0, sizeof(*data));
  142. data->timeout.cb = ubus_cli_listen_timeout;
  143. uloop_init();
  144. ubus_add_uloop(ctx);
  145. if (listen_timeout)
  146. uloop_timeout_set(&data->timeout, listen_timeout * 1000);
  147. uloop_run();
  148. uloop_done();
  149. }
  150. static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv)
  151. {
  152. struct ubus_event_handler ev = {
  153. .cb = receive_event,
  154. };
  155. struct cli_listen_data data;
  156. const char *event;
  157. int ret = 0;
  158. if (argc > 0) {
  159. event = argv[0];
  160. } else {
  161. event = "*";
  162. argc = 1;
  163. }
  164. do {
  165. ret = ubus_register_event_handler(ctx, &ev, event);
  166. if (ret)
  167. break;
  168. argv++;
  169. argc--;
  170. if (argc <= 0)
  171. break;
  172. event = argv[0];
  173. } while (1);
  174. if (ret) {
  175. if (!simple_output)
  176. fprintf(stderr, "Error while registering for event '%s': %s\n",
  177. event, ubus_strerror(ret));
  178. return -1;
  179. }
  180. do_listen(ctx, &data);
  181. return 0;
  182. }
  183. static int ubus_cli_subscribe(struct ubus_context *ctx, int argc, char **argv)
  184. {
  185. struct ubus_subscriber sub = {
  186. .cb = receive_request,
  187. };
  188. struct cli_listen_data data;
  189. const char *event;
  190. int ret = 0;
  191. if (argc > 0) {
  192. event = argv[0];
  193. } else {
  194. if (!simple_output)
  195. fprintf(stderr, "You need to specify an object to subscribe to\n");
  196. return -1;
  197. }
  198. ret = ubus_register_subscriber(ctx, &sub);
  199. for (; !ret && argc > 0; argc--, argv++) {
  200. uint32_t id;
  201. ret = ubus_lookup_id(ctx, argv[0], &id);
  202. if (ret)
  203. break;
  204. ret = ubus_subscribe(ctx, &sub, id);
  205. }
  206. if (ret) {
  207. if (!simple_output)
  208. fprintf(stderr, "Error while registering for event '%s': %s\n",
  209. event, ubus_strerror(ret));
  210. return -1;
  211. }
  212. do_listen(ctx, &data);
  213. return 0;
  214. }
  215. static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv)
  216. {
  217. if (argc < 1 || argc > 2)
  218. return -2;
  219. blob_buf_init(&b, 0);
  220. if (argc == 2 && !blobmsg_add_json_from_string(&b, argv[1])) {
  221. if (!simple_output)
  222. fprintf(stderr, "Failed to parse message data\n");
  223. return -1;
  224. }
  225. return ubus_send_event(ctx, argv[0], b.head);
  226. }
  227. struct cli_wait_data {
  228. struct uloop_timeout timeout;
  229. struct ubus_event_handler ev;
  230. char **pending;
  231. int n_pending;
  232. };
  233. static void wait_check_object(struct cli_wait_data *data, const char *path)
  234. {
  235. int i;
  236. for (i = 0; i < data->n_pending; i++) {
  237. if (strcmp(path, data->pending[i]) != 0)
  238. continue;
  239. data->n_pending--;
  240. if (i == data->n_pending)
  241. break;
  242. memmove(&data->pending[i], &data->pending[i + 1],
  243. (data->n_pending - i) * sizeof(*data->pending));
  244. i--;
  245. }
  246. if (!data->n_pending)
  247. uloop_end();
  248. }
  249. static void wait_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
  250. const char *type, struct blob_attr *msg)
  251. {
  252. static const struct blobmsg_policy policy = {
  253. "path", BLOBMSG_TYPE_STRING
  254. };
  255. struct cli_wait_data *data = container_of(ev, struct cli_wait_data, ev);
  256. struct blob_attr *attr;
  257. const char *path;
  258. if (strcmp(type, "ubus.object.add") != 0)
  259. return;
  260. blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg));
  261. if (!attr)
  262. return;
  263. path = blobmsg_data(attr);
  264. wait_check_object(data, path);
  265. }
  266. static void wait_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
  267. {
  268. struct cli_wait_data *data = priv;
  269. wait_check_object(data, obj->path);
  270. }
  271. static void wait_timeout(struct uloop_timeout *timeout)
  272. {
  273. uloop_end();
  274. }
  275. static int ubus_cli_wait_for(struct ubus_context *ctx, int argc, char **argv)
  276. {
  277. struct cli_wait_data data = {
  278. .timeout.cb = wait_timeout,
  279. .ev.cb = wait_event_cb,
  280. .pending = argv,
  281. .n_pending = argc,
  282. };
  283. int ret;
  284. if (argc < 1)
  285. return -2;
  286. uloop_init();
  287. ubus_add_uloop(ctx);
  288. ret = ubus_register_event_handler(ctx, &data.ev, "ubus.object.add");
  289. if (ret)
  290. return ret;
  291. if (!data.n_pending)
  292. return ret;
  293. ret = ubus_lookup(ctx, NULL, wait_list_cb, &data);
  294. if (ret)
  295. return ret;
  296. if (!data.n_pending)
  297. return ret;
  298. uloop_timeout_set(&data.timeout, timeout * 1000);
  299. uloop_run();
  300. uloop_done();
  301. if (data.n_pending)
  302. return UBUS_STATUS_TIMEOUT;
  303. return ret;
  304. }
  305. static const char *
  306. ubus_cli_msg_type(uint32_t type)
  307. {
  308. const char *ret = NULL;
  309. static char unk_type[16];
  310. if (type < ARRAY_SIZE(monitor_types))
  311. ret = monitor_types[type];
  312. if (!ret) {
  313. snprintf(unk_type, sizeof(unk_type), "%d", type);
  314. ret = unk_type;
  315. }
  316. return ret;
  317. }
  318. static char *
  319. ubus_cli_get_monitor_data(struct blob_attr *data)
  320. {
  321. static const struct blob_attr_info policy[UBUS_ATTR_MAX] = {
  322. [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 },
  323. [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING },
  324. [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 },
  325. [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING },
  326. [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 },
  327. [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED },
  328. [UBUS_ATTR_DATA] = { .type = BLOB_ATTR_NESTED },
  329. [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 },
  330. [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 },
  331. [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING },
  332. [UBUS_ATTR_GROUP] = { .type = BLOB_ATTR_STRING },
  333. };
  334. static const char * const names[UBUS_ATTR_MAX] = {
  335. [UBUS_ATTR_STATUS] = "status",
  336. [UBUS_ATTR_OBJPATH] = "objpath",
  337. [UBUS_ATTR_OBJID] = "objid",
  338. [UBUS_ATTR_METHOD] = "method",
  339. [UBUS_ATTR_OBJTYPE] = "objtype",
  340. [UBUS_ATTR_SIGNATURE] = "signature",
  341. [UBUS_ATTR_DATA] = "data",
  342. [UBUS_ATTR_ACTIVE] = "active",
  343. [UBUS_ATTR_NO_REPLY] = "no_reply",
  344. [UBUS_ATTR_USER] = "user",
  345. [UBUS_ATTR_GROUP] = "group",
  346. };
  347. struct blob_attr *tb[UBUS_ATTR_MAX];
  348. int i;
  349. blob_buf_init(&b, 0);
  350. blob_parse(data, tb, policy, UBUS_ATTR_MAX);
  351. for (i = 0; i < UBUS_ATTR_MAX; i++) {
  352. const char *n = names[i];
  353. struct blob_attr *v = tb[i];
  354. if (!tb[i] || !n)
  355. continue;
  356. switch(policy[i].type) {
  357. case BLOB_ATTR_INT32:
  358. blobmsg_add_u32(&b, n, blob_get_int32(v));
  359. break;
  360. case BLOB_ATTR_STRING:
  361. blobmsg_add_string(&b, n, blob_data(v));
  362. break;
  363. case BLOB_ATTR_INT8:
  364. blobmsg_add_u8(&b, n, !!blob_get_int8(v));
  365. break;
  366. case BLOB_ATTR_NESTED:
  367. blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, n, blobmsg_data(v), blobmsg_data_len(v));
  368. break;
  369. }
  370. }
  371. return blobmsg_format_json(b.head, true);
  372. }
  373. static void
  374. ubus_cli_monitor_cb(struct ubus_context *ctx, uint32_t seq, struct blob_attr *msg)
  375. {
  376. static const struct blob_attr_info policy[UBUS_MONITOR_MAX] = {
  377. [UBUS_MONITOR_CLIENT] = { .type = BLOB_ATTR_INT32 },
  378. [UBUS_MONITOR_PEER] = { .type = BLOB_ATTR_INT32 },
  379. [UBUS_MONITOR_SEND] = { .type = BLOB_ATTR_INT8 },
  380. [UBUS_MONITOR_TYPE] = { .type = BLOB_ATTR_INT32 },
  381. [UBUS_MONITOR_DATA] = { .type = BLOB_ATTR_NESTED },
  382. };
  383. struct blob_attr *tb[UBUS_MONITOR_MAX];
  384. uint32_t client, peer, type;
  385. bool send;
  386. char *data;
  387. blob_parse(msg, tb, policy, UBUS_MONITOR_MAX);
  388. if (!tb[UBUS_MONITOR_CLIENT] ||
  389. !tb[UBUS_MONITOR_PEER] ||
  390. !tb[UBUS_MONITOR_SEND] ||
  391. !tb[UBUS_MONITOR_TYPE] ||
  392. !tb[UBUS_MONITOR_DATA]) {
  393. printf("Invalid monitor msg\n");
  394. return;
  395. }
  396. send = blob_get_int32(tb[UBUS_MONITOR_SEND]);
  397. client = blob_get_int32(tb[UBUS_MONITOR_CLIENT]);
  398. peer = blob_get_int32(tb[UBUS_MONITOR_PEER]);
  399. type = blob_get_int32(tb[UBUS_MONITOR_TYPE]);
  400. if (monitor_mask && type < 32 && !(monitor_mask & (1 << type)))
  401. return;
  402. if (monitor_dir >= 0 && send != monitor_dir)
  403. return;
  404. data = ubus_cli_get_monitor_data(tb[UBUS_MONITOR_DATA]);
  405. printf("%s %08x #%08x %14s: %s\n", send ? "->" : "<-", client, peer, ubus_cli_msg_type(type), data);
  406. free(data);
  407. fflush(stdout);
  408. }
  409. static int ubus_cli_monitor(struct ubus_context *ctx, int argc, char **argv)
  410. {
  411. int ret;
  412. uloop_init();
  413. ubus_add_uloop(ctx);
  414. ctx->monitor_cb = ubus_cli_monitor_cb;
  415. ret = ubus_monitor_start(ctx);
  416. if (ret)
  417. return ret;
  418. uloop_run();
  419. uloop_done();
  420. ubus_monitor_stop(ctx);
  421. return 0;
  422. }
  423. static int add_monitor_type(const char *type)
  424. {
  425. int i;
  426. for (i = 0; i < ARRAY_SIZE(monitor_types); i++) {
  427. if (!monitor_types[i] || strcmp(monitor_types[i], type) != 0)
  428. continue;
  429. monitor_mask |= 1 << i;
  430. return 0;
  431. }
  432. return -1;
  433. }
  434. static int usage(const char *prog)
  435. {
  436. fprintf(stderr,
  437. "Usage: %s [<options>] <command> [arguments...]\n"
  438. "Options:\n"
  439. " -s <socket>: Set the unix domain socket to connect to\n"
  440. " -t <timeout>: Set the timeout (in seconds) for a command to complete\n"
  441. " -S: Use simplified output (for scripts)\n"
  442. " -v: More verbose output\n"
  443. " -m <type>: (for monitor): include a specific message type\n"
  444. " (can be used more than once)\n"
  445. " -M <r|t> (for monitor): only capture received or transmitted traffic\n"
  446. "\n"
  447. "Commands:\n"
  448. " - list [<path>] List objects\n"
  449. " - call <path> <method> [<message>] Call an object method\n"
  450. " - listen [<path>...] Listen for events\n"
  451. " - send <type> [<message>] Send an event\n"
  452. " - wait_for <object> [<object>...] Wait for multiple objects to appear on ubus\n"
  453. " - monitor Monitor ubus traffic\n"
  454. "\n", prog);
  455. return 1;
  456. }
  457. static struct {
  458. const char *name;
  459. int (*cb)(struct ubus_context *ctx, int argc, char **argv);
  460. } commands[] = {
  461. { "list", ubus_cli_list },
  462. { "call", ubus_cli_call },
  463. { "listen", ubus_cli_listen },
  464. { "subscribe", ubus_cli_subscribe },
  465. { "send", ubus_cli_send },
  466. { "wait_for", ubus_cli_wait_for },
  467. { "monitor", ubus_cli_monitor },
  468. };
  469. int main(int argc, char **argv)
  470. {
  471. const char *progname, *ubus_socket = NULL;
  472. struct ubus_context *ctx;
  473. char *cmd;
  474. int ret = 0;
  475. int i, ch;
  476. progname = argv[0];
  477. while ((ch = getopt(argc, argv, "m:M:vs:t:S")) != -1) {
  478. switch (ch) {
  479. case 's':
  480. ubus_socket = optarg;
  481. break;
  482. case 't':
  483. listen_timeout = atoi(optarg);
  484. timeout = atoi(optarg);
  485. break;
  486. case 'S':
  487. simple_output = true;
  488. break;
  489. case 'v':
  490. verbose++;
  491. break;
  492. case 'm':
  493. if (add_monitor_type(optarg))
  494. return usage(progname);
  495. break;
  496. case 'M':
  497. switch (optarg[0]) {
  498. case 'r':
  499. monitor_dir = 0;
  500. break;
  501. case 't':
  502. monitor_dir = 1;
  503. break;
  504. default:
  505. return usage(progname);
  506. }
  507. break;
  508. default:
  509. return usage(progname);
  510. }
  511. }
  512. argc -= optind;
  513. argv += optind;
  514. cmd = argv[0];
  515. if (argc < 1)
  516. return usage(progname);
  517. ctx = ubus_connect(ubus_socket);
  518. if (!ctx) {
  519. if (!simple_output)
  520. fprintf(stderr, "Failed to connect to ubus\n");
  521. return -1;
  522. }
  523. argv++;
  524. argc--;
  525. ret = -2;
  526. for (i = 0; i < ARRAY_SIZE(commands); i++) {
  527. if (strcmp(commands[i].name, cmd) != 0)
  528. continue;
  529. ret = commands[i].cb(ctx, argc, argv);
  530. break;
  531. }
  532. if (ret > 0 && !simple_output)
  533. fprintf(stderr, "Command failed: %s\n", ubus_strerror(ret));
  534. else if (ret == -2)
  535. usage(progname);
  536. ubus_free(ctx);
  537. return ret;
  538. }