ubusd: add lookup command queuing support
[project/ubus.git] / ubusd_proto.c
index b20f91c793982159515d8afe8bae92e101223dda..48de9b96141c8011061ac9ae7d9885d5a0eb6b12 100644 (file)
@@ -186,16 +186,56 @@ static void ubusd_send_obj(struct ubus_client *cl, struct ubus_msg_buf *ub, stru
                ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA);
 }
 
-static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr)
+static int ubus_client_cmd_queue_add(struct ubus_client *cl,
+                                       struct ubus_msg_buf *msg,
+                                       struct ubus_object *obj)
 {
-       struct ubus_object *obj;
+       struct ubus_client_cmd *cmd = malloc(sizeof(*cmd));
+
+       if (cmd) {
+               cmd->msg = msg;
+               cmd->obj = obj;
+               list_add_tail(&cmd->list, &cl->cmd_queue);
+               return -2;
+       }
+       return UBUS_STATUS_UNKNOWN_ERROR;
+}
+
+static int __ubusd_handle_lookup(struct ubus_client *cl,
+                               struct ubus_msg_buf *ub,
+                               struct blob_attr **attr,
+                               struct ubus_client_cmd *cmd)
+{
+       struct ubus_object *obj = NULL;
        char *objpath;
        bool found = false;
        int len;
 
        if (!attr[UBUS_ATTR_OBJPATH]) {
-               avl_for_each_element(&path, obj, path)
-                       ubusd_send_obj(cl, ub, obj);
+               if (cmd)
+                       obj = cmd->obj;
+
+               /* Start from beginning or continue from the last object */
+               if (obj == NULL)
+                       obj = avl_first_element(&path, obj, path);
+
+               avl_for_element_range(obj, avl_last_element(&path, obj, path), obj, path) {
+                       /* Keep sending objects until buffering starts */
+                       if (list_empty(&cl->tx_queue)) {
+                               ubusd_send_obj(cl, ub, obj);
+                       } else {
+                               /* Queue command and continue on the next call */
+                               int ret;
+
+                               if (cmd == NULL) {
+                                       ret = ubus_client_cmd_queue_add(cl, ub, obj);
+                               } else {
+                                       cmd->obj = obj;
+                                       ret = -2;
+                               }
+                               return ret;
+                       }
+               }
                return 0;
        }
 
@@ -230,6 +270,40 @@ static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub,
        return 0;
 }
 
+static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr)
+{
+       int rc;
+
+       if (list_empty(&cl->tx_queue))
+               rc = __ubusd_handle_lookup(cl, ub, attr, NULL);
+       else
+               rc = ubus_client_cmd_queue_add(cl, ub, NULL);
+
+       return rc;
+}
+
+int ubusd_cmd_lookup(struct ubus_client *cl, struct ubus_client_cmd *cmd)
+{
+       struct ubus_msg_buf *ub = cmd->msg;
+       struct blob_attr **attr;
+       int ret;
+
+       attr = ubus_parse_msg(ub->data, blob_raw_len(ub->data));
+       ret = __ubusd_handle_lookup(cl, ub, attr, cmd);
+
+       if (ret != -2) {
+               struct ubus_msg_buf *retmsg = cl->retmsg;
+               int *retmsg_data = blob_data(blob_data(retmsg->data));
+
+               retmsg->hdr.seq = ub->hdr.seq;
+               retmsg->hdr.peer = ub->hdr.peer;
+
+               *retmsg_data = htonl(ret);
+               ubus_msg_send(cl, retmsg);
+       }
+       return ret;
+}
+
 static void
 ubusd_forward_invoke(struct ubus_client *cl, struct ubus_object *obj,
                     const char *method, struct ubus_msg_buf *ub,
@@ -458,6 +532,10 @@ void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub
        else
                ret = UBUS_STATUS_INVALID_COMMAND;
 
+       /* Command has not been completed yet and got queued */
+       if (ret == -2)
+               return;
+
        ubus_msg_free(ub);
 
        if (ret == -1)
@@ -495,6 +573,7 @@ struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb)
                goto free;
 
        INIT_LIST_HEAD(&cl->objects);
+       INIT_LIST_HEAD(&cl->cmd_queue);
        INIT_LIST_HEAD(&cl->tx_queue);
        cl->sock.fd = fd;
        cl->sock.cb = cb;