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