X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fubus.git;a=blobdiff_plain;f=cli.c;h=080f953179a9ebf208ac7d6d5892abb43d1dee6a;hp=9e16bc8f47bd338b2109e9e5074e500c469d18fc;hb=d009a084735c7e0bca28ca05919fa9c60007f314;hpb=460672c6dab5f8d8ab67dc4955cea2137ed11990 diff --git a/cli.c b/cli.c index 9e16bc8..080f953 100644 --- a/cli.c +++ b/cli.c @@ -19,6 +19,22 @@ static struct blob_buf b; static int timeout = 30; static bool simple_output = false; +static int verbose = 0; +static int monitor_dir = -1; +static uint32_t monitor_mask; +static const char * const monitor_types[] = { + [UBUS_MSG_HELLO] = "hello", + [UBUS_MSG_STATUS] = "status", + [UBUS_MSG_DATA] = "data", + [UBUS_MSG_PING] = "ping", + [UBUS_MSG_LOOKUP] = "lookup", + [UBUS_MSG_INVOKE] = "invoke", + [UBUS_MSG_ADD_OBJECT] = "add_object", + [UBUS_MSG_REMOVE_OBJECT] = "remove_object", + [UBUS_MSG_SUBSCRIBE] = "subscribe", + [UBUS_MSG_UNSUBSCRIBE] = "unsubscribe", + [UBUS_MSG_NOTIFY] = "notify", +}; static const char *format_type(void *priv, struct blob_attr *attr) { @@ -26,6 +42,8 @@ static const char *format_type(void *priv, struct blob_attr *attr) [BLOBMSG_TYPE_INT8] = "\"Boolean\"", [BLOBMSG_TYPE_INT32] = "\"Integer\"", [BLOBMSG_TYPE_STRING] = "\"String\"", + [BLOBMSG_TYPE_ARRAY] = "\"Array\"", + [BLOBMSG_TYPE_TABLE] = "\"Table\"", }; const char *type = NULL; int typeid; @@ -48,7 +66,7 @@ static void receive_list_result(struct ubus_context *ctx, struct ubus_object_dat char *s; int rem; - if (simple_output) { + if (simple_output || !verbose) { printf("%s\n", obj->path); return; } @@ -71,7 +89,7 @@ static void receive_call_result_data(struct ubus_request *req, int type, struct if (!msg) return; - str = blobmsg_format_json_indent(msg, true, 0); + str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0); printf("%s\n", str); free(str); } @@ -83,6 +101,7 @@ static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *e str = blobmsg_format_json(msg, true); printf("{ \"%s\": %s }\n", type, str); + fflush(stdout); free(str); } @@ -121,15 +140,29 @@ static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv) return ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000); } +struct cli_listen_data { + struct uloop_timeout timeout; + struct ubus_event_handler ev; + bool timed_out; +}; + +static void listen_timeout(struct uloop_timeout *timeout) +{ + struct cli_listen_data *data = container_of(timeout, struct cli_listen_data, timeout); + data->timed_out = true; + uloop_end(); +} + static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) { - static struct ubus_event_handler listener; + struct cli_listen_data data = { + .timeout.cb = listen_timeout, + .ev.cb = receive_event, + .timed_out = false, + }; const char *event; int ret = 0; - memset(&listener, 0, sizeof(listener)); - listener.cb = receive_event; - if (argc > 0) { event = argv[0]; } else { @@ -138,7 +171,7 @@ static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) } do { - ret = ubus_register_event_handler(ctx, &listener, event); + ret = ubus_register_event_handler(ctx, &data.ev, event); if (ret) break; @@ -159,6 +192,7 @@ static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) uloop_init(); ubus_add_uloop(ctx); + uloop_timeout_set(&data.timeout, timeout * 1000); uloop_run(); uloop_done(); @@ -181,6 +215,263 @@ static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv) return ubus_send_event(ctx, argv[0], b.head); } +struct cli_wait_data { + struct uloop_timeout timeout; + struct ubus_event_handler ev; + char **pending; + int n_pending; +}; + +static void wait_check_object(struct cli_wait_data *data, const char *path) +{ + int i; + + for (i = 0; i < data->n_pending; i++) { + if (strcmp(path, data->pending[i]) != 0) + continue; + + data->n_pending--; + if (i == data->n_pending) + break; + + memmove(&data->pending[i], &data->pending[i + 1], + (data->n_pending - i) * sizeof(*data->pending)); + i--; + } + + if (!data->n_pending) + uloop_end(); +} + +static void wait_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + static const struct blobmsg_policy policy = { + "path", BLOBMSG_TYPE_STRING + }; + struct cli_wait_data *data = container_of(ev, struct cli_wait_data, ev); + struct blob_attr *attr; + const char *path; + + if (strcmp(type, "ubus.object.add") != 0) + return; + + blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg)); + if (!attr) + return; + + path = blobmsg_data(attr); + wait_check_object(data, path); +} + +static void wait_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv) +{ + struct cli_wait_data *data = priv; + + wait_check_object(data, obj->path); +} + + +static void wait_timeout(struct uloop_timeout *timeout) +{ + uloop_end(); +} + +static int ubus_cli_wait_for(struct ubus_context *ctx, int argc, char **argv) +{ + struct cli_wait_data data = { + .timeout.cb = wait_timeout, + .ev.cb = wait_event_cb, + .pending = argv, + .n_pending = argc, + }; + int ret; + + if (argc < 1) + return -2; + + uloop_init(); + ubus_add_uloop(ctx); + + ret = ubus_register_event_handler(ctx, &data.ev, "ubus.object.add"); + if (ret) + return ret; + + if (!data.n_pending) + return ret; + + ret = ubus_lookup(ctx, NULL, wait_list_cb, &data); + if (ret) + return ret; + + if (!data.n_pending) + return ret; + + uloop_timeout_set(&data.timeout, timeout * 1000); + uloop_run(); + uloop_done(); + + if (data.n_pending) + return UBUS_STATUS_TIMEOUT; + + return ret; +} + +static const char * +ubus_cli_msg_type(uint32_t type) +{ + const char *ret = NULL; + static char unk_type[16]; + + + if (type < ARRAY_SIZE(monitor_types)) + ret = monitor_types[type]; + + if (!ret) { + snprintf(unk_type, sizeof(unk_type), "%d", type); + ret = unk_type; + } + + return ret; +} + +static char * +ubus_cli_get_monitor_data(struct blob_attr *data) +{ + static const struct blob_attr_info policy[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_DATA] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_GROUP] = { .type = BLOB_ATTR_STRING }, + }; + static const char * const names[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = "status", + [UBUS_ATTR_OBJPATH] = "objpath", + [UBUS_ATTR_OBJID] = "objid", + [UBUS_ATTR_METHOD] = "method", + [UBUS_ATTR_OBJTYPE] = "objtype", + [UBUS_ATTR_SIGNATURE] = "signature", + [UBUS_ATTR_DATA] = "data", + [UBUS_ATTR_ACTIVE] = "active", + [UBUS_ATTR_NO_REPLY] = "no_reply", + [UBUS_ATTR_USER] = "user", + [UBUS_ATTR_GROUP] = "group", + }; + struct blob_attr *tb[UBUS_ATTR_MAX]; + int i; + + blob_buf_init(&b, 0); + blob_parse(data, tb, policy, UBUS_ATTR_MAX); + + for (i = 0; i < UBUS_ATTR_MAX; i++) { + const char *n = names[i]; + struct blob_attr *v = tb[i]; + + if (!tb[i] || !n) + continue; + + switch(policy[i].type) { + case BLOB_ATTR_INT32: + blobmsg_add_u32(&b, n, blob_get_int32(v)); + break; + case BLOB_ATTR_STRING: + blobmsg_add_string(&b, n, blob_data(v)); + break; + case BLOB_ATTR_INT8: + blobmsg_add_u8(&b, n, !!blob_get_int8(v)); + break; + case BLOB_ATTR_NESTED: + blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, n, blobmsg_data(v), blobmsg_data_len(v)); + break; + } + } + + return blobmsg_format_json(b.head, true); +} + +static void +ubus_cli_monitor_cb(struct ubus_context *ctx, uint32_t seq, struct blob_attr *msg) +{ + static const struct blob_attr_info policy[UBUS_MONITOR_MAX] = { + [UBUS_MONITOR_CLIENT] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_PEER] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_SEND] = { .type = BLOB_ATTR_INT8 }, + [UBUS_MONITOR_TYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_DATA] = { .type = BLOB_ATTR_NESTED }, + }; + struct blob_attr *tb[UBUS_MONITOR_MAX]; + uint32_t client, peer, type; + bool send; + char *data; + + blob_parse(msg, tb, policy, UBUS_MONITOR_MAX); + + if (!tb[UBUS_MONITOR_CLIENT] || + !tb[UBUS_MONITOR_PEER] || + !tb[UBUS_MONITOR_SEND] || + !tb[UBUS_MONITOR_TYPE] || + !tb[UBUS_MONITOR_DATA]) { + printf("Invalid monitor msg\n"); + return; + } + + send = blob_get_int32(tb[UBUS_MONITOR_SEND]); + client = blob_get_int32(tb[UBUS_MONITOR_CLIENT]); + peer = blob_get_int32(tb[UBUS_MONITOR_PEER]); + type = blob_get_int32(tb[UBUS_MONITOR_TYPE]); + + if (monitor_mask && type < 32 && !(monitor_mask & (1 << type))) + return; + + if (monitor_dir >= 0 && send != monitor_dir) + return; + + data = ubus_cli_get_monitor_data(tb[UBUS_MONITOR_DATA]); + printf("%s %08x #%08x %14s: %s\n", send ? "->" : "<-", client, peer, ubus_cli_msg_type(type), data); + free(data); + fflush(stdout); +} + +static int ubus_cli_monitor(struct ubus_context *ctx, int argc, char **argv) +{ + int ret; + + uloop_init(); + ubus_add_uloop(ctx); + ctx->monitor_cb = ubus_cli_monitor_cb; + ret = ubus_monitor_start(ctx); + if (ret) + return ret; + + uloop_run(); + uloop_done(); + + ubus_monitor_stop(ctx); + return 0; +} + +static int add_monitor_type(const char *type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(monitor_types); i++) { + if (!monitor_types[i] || strcmp(monitor_types[i], type) != 0) + continue; + + monitor_mask |= 1 << i; + return 0; + } + + return -1; +} + static int usage(const char *prog) { fprintf(stderr, @@ -189,18 +480,24 @@ static int usage(const char *prog) " -s : Set the unix domain socket to connect to\n" " -t : Set the timeout (in seconds) for a command to complete\n" " -S: Use simplified output (for scripts)\n" + " -v: More verbose output\n" + " -m : (for monitor): include a specific message type\n" + " (can be used more than once)\n" + " -M (for monitor): only capture received or transmitted traffic\n" "\n" "Commands:\n" " - list [] List objects\n" " - call [] Call an object method\n" " - listen [...] Listen for events\n" " - send [] Send an event\n" + " - wait_for [...] Wait for multiple objects to appear on ubus\n" + " - monitor Monitor ubus traffic\n" "\n", prog); return 1; } -struct { +static struct { const char *name; int (*cb)(struct ubus_context *ctx, int argc, char **argv); } commands[] = { @@ -208,19 +505,21 @@ struct { { "call", ubus_cli_call }, { "listen", ubus_cli_listen }, { "send", ubus_cli_send }, + { "wait_for", ubus_cli_wait_for }, + { "monitor", ubus_cli_monitor }, }; int main(int argc, char **argv) { const char *progname, *ubus_socket = NULL; - static struct ubus_context *ctx; + struct ubus_context *ctx; char *cmd; int ret = 0; int i, ch; progname = argv[0]; - while ((ch = getopt(argc, argv, "s:t:S")) != -1) { + while ((ch = getopt(argc, argv, "m:M:vs:t:S")) != -1) { switch (ch) { case 's': ubus_socket = optarg; @@ -231,6 +530,25 @@ int main(int argc, char **argv) case 'S': simple_output = true; break; + case 'v': + verbose++; + break; + case 'm': + if (add_monitor_type(optarg)) + return usage(progname); + break; + case 'M': + switch (optarg[0]) { + case 'r': + monitor_dir = 0; + break; + case 't': + monitor_dir = 1; + break; + default: + return usage(progname); + } + break; default: return usage(progname); }