CMakeLists.txt: bump minimum cmake version
[project/ubus.git] / cli.c
diff --git a/cli.c b/cli.c
index 080f953179a9ebf208ac7d6d5892abb43d1dee6a..5d2cd4c9cfbcec341028f41dfb480ab4487ca29e 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -17,6 +17,7 @@
 #include "libubus.h"
 
 static struct blob_buf b;
+static int listen_timeout;
 static int timeout = 30;
 static bool simple_output = false;
 static int verbose = 0;
@@ -46,7 +47,7 @@ static const char *format_type(void *priv, struct blob_attr *attr)
                [BLOBMSG_TYPE_TABLE] = "\"Table\"",
        };
        const char *type = NULL;
-       int typeid;
+       size_t typeid;
 
        if (blob_id(attr) != BLOBMSG_TYPE_INT32)
                return NULL;
@@ -64,7 +65,7 @@ static void receive_list_result(struct ubus_context *ctx, struct ubus_object_dat
 {
        struct blob_attr *cur;
        char *s;
-       int rem;
+       size_t rem;
 
        if (simple_output || !verbose) {
                printf("%s\n", obj->path);
@@ -94,8 +95,7 @@ static void receive_call_result_data(struct ubus_request *req, int type, struct
        free(str);
 }
 
-static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
-                         const char *type, struct blob_attr *msg)
+static void print_event(const char *type, struct blob_attr *msg)
 {
        char *str;
 
@@ -105,6 +105,37 @@ static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *e
        free(str);
 }
 
+static int receive_request(struct ubus_context *ctx, struct ubus_object *obj,
+                           struct ubus_request_data *req,
+                           const char *method, struct blob_attr *msg)
+{
+       print_event(method, msg);
+       return 0;
+}
+
+static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
+                         const char *type, struct blob_attr *msg)
+{
+       print_event(type, msg);
+}
+
+static int ubus_cli_error(char *cmd, int argc, char **argv, int err)
+{
+       int i;
+
+       if (!simple_output && !isatty(fileno(stderr))) {
+              fprintf(stderr, "Command failed: ubus %s ", cmd);
+              for (i = 0; i < argc; i++) {
+                      fprintf(stderr, "%s ", argv[i]);
+              }
+              fprintf(stderr, "(%s)\n", ubus_strerror(err));
+
+              return -err;
+       }
+
+       return err;
+}
+
 static int ubus_cli_list(struct ubus_context *ctx, int argc, char **argv)
 {
        const char *path = NULL;
@@ -128,38 +159,50 @@ static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv)
 
        blob_buf_init(&b, 0);
        if (argc == 3 && !blobmsg_add_json_from_string(&b, argv[2])) {
-               if (!simple_output)
-                       fprintf(stderr, "Failed to parse message data\n");
-               return -1;
+               return ubus_cli_error("call", argc, argv, UBUS_STATUS_PARSE_ERROR);
        }
 
        ret = ubus_lookup_id(ctx, argv[0], &id);
        if (ret)
                return ret;
 
-       return ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000);
+       ret = ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000);
+       if (ret)
+               return ubus_cli_error("call", argc, argv, ret);
+
+       return ret;
 }
 
 struct cli_listen_data {
        struct uloop_timeout timeout;
-       struct ubus_event_handler ev;
        bool timed_out;
 };
 
-static void listen_timeout(struct uloop_timeout *timeout)
+static void ubus_cli_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 void do_listen(struct ubus_context *ctx, struct cli_listen_data *data)
+{
+       memset(data, 0, sizeof(*data));
+       data->timeout.cb = ubus_cli_listen_timeout;
+       uloop_init();
+       ubus_add_uloop(ctx);
+       if (listen_timeout)
+               uloop_timeout_set(&data->timeout, listen_timeout * 1000);
+       uloop_run();
+       uloop_done();
+}
+
 static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv)
 {
-       struct cli_listen_data data = {
-               .timeout.cb = listen_timeout,
-               .ev.cb = receive_event,
-               .timed_out = false,
+       struct ubus_event_handler ev = {
+               .cb = receive_event,
        };
+       struct cli_listen_data data;
        const char *event;
        int ret = 0;
 
@@ -171,7 +214,7 @@ static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv)
        }
 
        do {
-               ret = ubus_register_event_handler(ctx, &data.ev, event);
+               ret = ubus_register_event_handler(ctx, &ev, event);
                if (ret)
                        break;
 
@@ -190,15 +233,52 @@ static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv)
                return -1;
        }
 
-       uloop_init();
-       ubus_add_uloop(ctx);
-       uloop_timeout_set(&data.timeout, timeout * 1000);
-       uloop_run();
-       uloop_done();
+       do_listen(ctx, &data);
 
        return 0;
 }
 
+static int ubus_cli_subscribe(struct ubus_context *ctx, int argc, char **argv)
+{
+       struct ubus_subscriber sub = {
+               .cb = receive_request,
+       };
+       struct cli_listen_data data;
+       const char *event;
+       int ret = 0;
+
+       if (argc > 0) {
+               event = argv[0];
+       } else {
+               if (!simple_output)
+                       fprintf(stderr, "You need to specify an object to subscribe to\n");
+               return -1;
+       }
+
+       ret = ubus_register_subscriber(ctx, &sub);
+       for (; !ret && argc > 0; argc--, argv++) {
+               uint32_t id;
+
+               ret = ubus_lookup_id(ctx, argv[0], &id);
+               if (ret)
+                       break;
+
+               ret = ubus_subscribe(ctx, &sub, id);
+       }
+
+       if (ret) {
+               if (!simple_output)
+                       fprintf(stderr, "Error while registering for event '%s': %s\n",
+                               event, ubus_strerror(ret));
+               return -1;
+       }
+
+       do_listen(ctx, &data);
+
+       return 0;
+}
+
+
 static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv)
 {
        if (argc < 1 || argc > 2)
@@ -207,9 +287,7 @@ static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv)
        blob_buf_init(&b, 0);
 
        if (argc == 2 && !blobmsg_add_json_from_string(&b, argv[1])) {
-               if (!simple_output)
-                       fprintf(stderr, "Failed to parse message data\n");
-               return -1;
+               return UBUS_STATUS_PARSE_ERROR;
        }
 
        return ubus_send_event(ctx, argv[0], b.head);
@@ -411,7 +489,7 @@ ubus_cli_monitor_cb(struct ubus_context *ctx, uint32_t seq, struct blob_attr *ms
        bool send;
        char *data;
 
-       blob_parse(msg, tb, policy, UBUS_MONITOR_MAX);
+       blob_parse_untrusted(msg, blob_raw_len(msg), tb, policy, UBUS_MONITOR_MAX);
 
        if (!tb[UBUS_MONITOR_CLIENT] ||
            !tb[UBUS_MONITOR_PEER] ||
@@ -459,7 +537,7 @@ static int ubus_cli_monitor(struct ubus_context *ctx, int argc, char **argv)
 
 static int add_monitor_type(const char *type)
 {
-       int i;
+       size_t i;
 
        for (i = 0; i < ARRAY_SIZE(monitor_types); i++) {
                if (!monitor_types[i] || strcmp(monitor_types[i], type) != 0)
@@ -488,6 +566,7 @@ static int usage(const char *prog)
                "Commands:\n"
                " - list [<path>]                       List objects\n"
                " - call <path> <method> [<message>]    Call an object method\n"
+               " - subscribe <path> [<path>...]        Subscribe to object(s) notifications\n"
                " - listen [<path>...]                  Listen for events\n"
                " - send <type> [<message>]             Send an event\n"
                " - wait_for <object> [<object>...]     Wait for multiple objects to appear on ubus\n"
@@ -504,6 +583,7 @@ static struct {
        { "list", ubus_cli_list },
        { "call", ubus_cli_call },
        { "listen", ubus_cli_listen },
+       { "subscribe", ubus_cli_subscribe },
        { "send", ubus_cli_send },
        { "wait_for", ubus_cli_wait_for },
        { "monitor", ubus_cli_monitor },
@@ -513,9 +593,10 @@ int main(int argc, char **argv)
 {
        const char *progname, *ubus_socket = NULL;
        struct ubus_context *ctx;
-       char *cmd;
        int ret = 0;
-       int i, ch;
+       char *cmd;
+       size_t i;
+       int ch;
 
        progname = argv[0];
 
@@ -525,6 +606,7 @@ int main(int argc, char **argv)
                        ubus_socket = optarg;
                        break;
                case 't':
+                       listen_timeout = atoi(optarg);
                        timeout = atoi(optarg);
                        break;
                case 'S':