Amxb_Ubus  3.3.1
Ambiorix Ubus API
amxb_ubus_list.c
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** SPDX-License-Identifier: BSD-2-Clause-Patent
4 **
5 ** SPDX-FileCopyrightText: Copyright (c) 2023 SoftAtHome
6 **
7 ** Redistribution and use in source and binary forms, with or without modification,
8 ** are permitted provided that the following conditions are met:
9 **
10 ** 1. Redistributions of source code must retain the above copyright notice,
11 ** this list of conditions and the following disclaimer.
12 **
13 ** 2. Redistributions in binary form must reproduce the above copyright notice,
14 ** this list of conditions and the following disclaimer in the documentation
15 ** and/or other materials provided with the distribution.
16 **
17 ** Subject to the terms and conditions of this license, each copyright holder
18 ** and contributor hereby grants to those receiving rights under this license
19 ** a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
20 ** (except for failure to satisfy the conditions of this license) patent license
21 ** to make, have made, use, offer to sell, sell, import, and otherwise transfer
22 ** this software, where such license applies only to those patent claims, already
23 ** acquired or hereafter acquired, licensable by such copyright holder or contributor
24 ** that are necessarily infringed by:
25 **
26 ** (a) their Contribution(s) (the licensed copyrights of copyright holders and
27 ** non-copyrightable additions of contributors, in source or binary form) alone;
28 ** or
29 **
30 ** (b) combination of their Contribution(s) with the work of authorship to which
31 ** such Contribution(s) was added by such copyright holder or contributor, if,
32 ** at the time the Contribution is added, such addition causes such combination
33 ** to be necessarily infringed. The patent license shall not apply to any other
34 ** combinations which include the Contribution.
35 **
36 ** Except as expressly stated above, no rights or licenses from any copyright
37 ** holder or contributor is granted under this license, whether expressly, by
38 ** implication, estoppel or otherwise.
39 **
40 ** DISCLAIMER
41 **
42 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
46 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
51 ** USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 **
53 ****************************************************************************/
54 
55 #ifndef _GNU_SOURCE
56 #define _GNU_SOURCE
57 #endif
58 
59 #include "amxb_ubus.h"
60 
61 typedef struct _ubus_filter {
62  uint32_t depth;
63  const char* path;
64  uint32_t path_len;
65  amxc_var_t objects;
66  uint32_t flags;
67  uint32_t access;
68  amxc_var_t amx_paths;
69  amxb_request_t* request;
71 
72 static int32_t amxb_ubus_object_depth(const char* path) {
73  uint32_t length = path == NULL ? 0 : strlen(path);
74  int32_t depth = 0;
75  for(uint32_t i = 0; i < length; i++) {
76  if(path[i] == '.') {
77  depth++;
78  }
79  }
80 
81  return depth;
82 }
83 
84 static bool amxb_ubus_has_function(struct ubus_object_data* o, const char* func_name) {
85  bool retval = false;
86  struct blob_attr* cur;
87  size_t rem = 0;
88  (void) rem; // clang complains about not used variable
89 
90  when_null(o->signature, exit);
91  blob_for_each_attr(cur, o->signature, rem) {
92  (void) rem; // clang complains about not used variable
93  if(strcmp(func_name, blobmsg_name(cur)) == 0) {
94  retval = true;
95  goto exit;
96  }
97  }
98 
99 exit:
100  return retval;
101 }
102 
103 static void amxb_ubus_add_functions(struct ubus_object_data* o,
104  ubus_filter_t* filter,
105  const char* path,
106  amxc_var_t* retval) {
107  if(o->signature && ((filter->flags & AMXB_FLAG_FUNCTIONS) != 0)) {
108  struct blob_attr* cur;
109  size_t rem = 0;
110  (void) rem; // clang complains about not used variable
111  // but rem is needed to be able to use the following macro
112  blob_for_each_attr(cur, o->signature, rem) {
113  (void) rem; // clang complains about not used variable
114  amxc_string_t func_name;
115  amxc_string_init(&func_name, 0);
116  amxc_string_setf(&func_name, "%s%s", path, blobmsg_name(cur));
117  amxc_var_add(cstring_t, retval, amxc_string_get(&func_name, 0));
118  amxc_string_clean(&func_name);
119  }
120  }
121 }
122 
123 static void amxb_ubus_fill_list(amxb_bus_ctx_t* bus_ctx,
124  const char* path,
125  amxc_var_t* data,
126  const char* type,
127  bool fetch_names,
128  amxc_var_t* retval) {
129  amxc_string_t name;
130  amxc_string_init(&name, 0);
131 
132  amxc_var_t* type_data = GETP_ARG(data, type);
133  amxc_var_for_each(d, type_data) {
134  if(amxc_var_type_of(type_data) == AMXC_VAR_ID_HTABLE) {
135  amxc_string_setf(&name, "%s%s", path, amxc_var_key(d));
136  } else {
137  amxc_string_setf(&name, "%s%s", path, GET_CHAR(d, NULL));
138  }
139  amxc_var_add(cstring_t, retval, amxc_string_get(&name, 0));
140  if(fetch_names) {
141  amxc_var_t tmp;
142  amxc_var_init(&tmp);
143  amxb_describe(bus_ctx, amxc_string_get(&name, 0), 0, &tmp, 5);
144  amxc_string_setf(&name, "%s%s", path, GETP_CHAR(&tmp, "0.name"));
145  amxc_var_add(cstring_t, retval, amxc_string_get(&name, 0));
146  amxc_var_clean(&tmp);
147  }
148  }
149 
150  amxc_string_clean(&name);
151 }
152 
153 static int amxb_ubus_list_object_describe(amxb_bus_ctx_t* bus_ctx,
154  const char* object,
155  ubus_filter_t* filter) {
156  int rv = 0;
157  amxc_var_t tmp;
158  amxc_string_t path;
159  amxc_var_t* subojects = NULL;
160  amxc_var_t output;
161 
162  amxc_string_init(&path, 0);
163  amxc_var_init(&tmp);
164 
165  rv = amxb_describe(bus_ctx, object, filter->flags, &tmp, 5);
166  when_failed(rv, exit);
167 
168  amxc_var_init(&output);
169  amxc_var_set_type(&output, AMXC_VAR_ID_LIST);
170  amxc_var_add(cstring_t, &output, object);
171 
172  if(GETP_UINT32(&tmp, "0.type_id") != 2) {
173  if((filter->flags & AMXB_FLAG_PARAMETERS) != 0) {
174  amxb_ubus_fill_list(bus_ctx, object, &tmp, "0.parameters", false, &output);
175  }
176  if((filter->flags & AMXB_FLAG_FUNCTIONS) != 0) {
177  amxb_ubus_fill_list(bus_ctx, object, &tmp, "0.functions", false, &output);
178  }
179  if((filter->flags & AMXB_FLAG_OBJECTS) != 0) {
180  if(filter->flags & AMXB_FLAG_FIRST_LVL) {
181  amxb_ubus_fill_list(bus_ctx, object, &tmp, "0.objects", false, &output);
182  } else {
183  subojects = GETP_ARG(&tmp, "0.objects");
184  }
185  }
186  } else {
187  if((filter->flags & AMXB_FLAG_INSTANCES) != 0) {
188  if(filter->flags & AMXB_FLAG_FIRST_LVL) {
189  amxb_ubus_fill_list(bus_ctx, object, &tmp, "0.instances", true, &output);
190  } else {
191  subojects = GETP_ARG(&tmp, "0.instances");
192  }
193  }
194  }
195 
196  if(filter->request->cb_fn) {
197  filter->request->cb_fn(bus_ctx, &output, filter->request->priv);
198  }
199 
200  // remove all items, they are not needed anymore
201  amxc_var_clean(&output);
202 
203  amxc_var_for_each(subobj, subojects) {
204  amxc_string_setf(&path, "%s%s.", object, GET_CHAR(subobj, NULL));
205  amxb_ubus_list_object_describe(bus_ctx, amxc_string_get(&path, 0), filter);
206  }
207 
208 exit:
209  amxc_var_clean(&tmp);
210  amxc_string_clean(&path);
211  return rv;
212 }
213 
214 static int amxb_ubus_add(const char* recv_path,
215  struct ubus_object_data* o,
216  ubus_filter_t* filter,
217  amxc_var_t* output,
218  bool functions) {
219  int retval = -1;
220  if(amxb_ubus_has_function(o, "_describe")) {
221  amxc_var_add(cstring_t, &filter->amx_paths, recv_path);
222  } else {
223  amxc_var_add(cstring_t, output, recv_path);
224  if(functions) {
225  amxb_ubus_add_functions(o, filter, recv_path, output);
226  }
227  retval = 0;
228  }
229 
230  return retval;
231 }
232 
233 static void amxb_ubus_path_to_depth(amxd_path_t* path,
234  ubus_filter_t* filter,
235  uint32_t depth) {
236  while(depth > filter->depth) {
237  char* part = amxd_path_get_last(path, true);
238  free(part);
239  depth--;
240  }
241 }
242 
243 // In this callback function which is passed to ubus_lookup it is not allowed
244 // to send other requests to ubus.
245 // When doing this, the response of the ubus_lookup is often incomplete.
246 // Therefor when ambiorix objects are detected, they are added to a list
247 // and when the ubus_lookup has finished, the ambiorix objects are inspected
248 // using _describe object method.
249 static void amxb_ubus_objects_cb(AMXB_UNUSED struct ubus_context* c,
250  struct ubus_object_data* o,
251  void* p) {
252  ubus_filter_t* filter = (ubus_filter_t*) p;
253  const char* recv_path = NULL;
254  amxd_path_t path;
255  amxc_var_t retval;
256  amxc_var_t tmp;
257  uint32_t depth = 0;
258  amxb_bus_ctx_t* bus_ctx = amxb_request_get_ctx(filter->request);
259  bool functions = true;
260 
261  amxc_var_init(&retval);
262  amxc_var_init(&tmp);
263  amxc_var_set_type(&retval, AMXC_VAR_ID_LIST);
264  amxd_path_init(&path, NULL);
265  amxd_path_setf(&path, true, "%s", o->path);
266  recv_path = amxd_path_get(&path, AMXD_OBJECT_TERMINATE);
267  depth = amxd_path_get_depth(&path);
268 
269  if((filter->path_len != 0) &&
270  (strncmp(recv_path, filter->path, filter->path_len) != 0)) {
271  goto exit;
272  }
273 
274  if(depth < filter->depth) {
275  goto exit;
276  }
277 
278  if(depth > filter->depth) {
279  if((filter->flags & AMXB_FLAG_FIRST_LVL) != 0) {
280  if(amxb_ubus_has_function(o, "_describe")) {
281  amxb_ubus_path_to_depth(&path, filter, depth);
282  } else {
283  functions = false;
284  amxb_ubus_path_to_depth(&path, filter, filter->path == NULL? depth:depth - 1);
285  }
286  recv_path = amxd_path_get(&path, AMXD_OBJECT_TERMINATE);
287  }
288  }
289 
290  if(GET_ARG(&filter->objects, recv_path) != NULL) {
291  goto exit;
292  }
293 
294  if(amxb_ubus_add(recv_path, o, filter, &retval, functions) == 0) {
295  if(filter->request->cb_fn) {
296  filter->request->cb_fn(bus_ctx, &retval, filter->request->priv);
297  }
298  }
299  amxc_var_add_key(bool, &filter->objects, recv_path, true);
300 
301 exit:
302  amxc_var_clean(&tmp);
303  amxc_var_clean(&retval);
304  amxd_path_clean(&path);
305 }
306 
307 static int amxb_ubus_list_native(amxb_ubus_t* amxb_ubus_ctx,
308  const char* object,
309  ubus_filter_t* filter) {
310  int rv = 0;
311 
312  if(object[strlen(object) - 1] == '.') {
313  amxc_string_t path;
314 
315  filter->path = object;
316  filter->path_len = strlen(object);
317  filter->depth = amxb_ubus_object_depth(object);
318 
319  amxc_string_init(&path, 0);
320  amxc_string_setf(&path, "%s", object);
321  amxc_string_set_at(&path, strlen(object) - 1, "*", 1, amxc_string_overwrite);
322  rv = ubus_lookup(amxb_ubus_ctx->ubus_ctx,
323  amxc_string_get(&path, 0),
325  filter);
326  amxc_string_clean(&path);
327  }
328 
329  return rv;
330 }
331 
332 int amxb_ubus_list(void* const ctx,
333  const char* object,
334  uint32_t flags,
335  uint32_t access,
336  amxb_request_t* request) {
337  amxb_bus_ctx_t* bus_ctx = NULL;
338  amxb_ubus_t* amxb_ubus_ctx = (amxb_ubus_t*) ctx;
339  int rv = -1;
340  ubus_filter_t filter;
341 
342  memset(&filter, 0, sizeof(ubus_filter_t));
343  filter.flags = flags;
344  filter.access = access;
345  filter.request = request;
346  amxc_var_set_type(&filter.amx_paths, AMXC_VAR_ID_LIST);
347  amxc_var_set_type(&filter.objects, AMXC_VAR_ID_HTABLE);
348 
349  bus_ctx = amxb_request_get_ctx(request);
350  if((object == NULL) || (*object == 0)) {
351  filter.depth = 1;
352  filter.flags &= ~(AMXB_FLAG_OBJECTS | AMXB_FLAG_INSTANCES);
353  rv = ubus_lookup(amxb_ubus_ctx->ubus_ctx, NULL, amxb_ubus_objects_cb, &filter);
354  filter.flags &= ~(AMXB_FLAG_PARAMETERS | AMXB_FLAG_FUNCTIONS);
355  amxc_var_for_each(path, &filter.amx_paths) {
356  const char* p = GET_CHAR(path, NULL);
358  }
359  request->cb_fn(bus_ctx, NULL, request->priv);
360  } else {
361  filter.flags |= (AMXB_FLAG_OBJECTS | AMXB_FLAG_INSTANCES);
362  rv = amxb_ubus_list_object_describe(bus_ctx, object, &filter);
363 
364  if(rv != 0) {
365  filter.flags &= ~(AMXB_FLAG_OBJECTS | AMXB_FLAG_INSTANCES);
366  rv = amxb_ubus_list_native(amxb_ubus_ctx, object, &filter);
367  amxc_var_for_each(path, &filter.amx_paths) {
368  const char* p = GET_CHAR(path, NULL);
370  }
371  }
372  request->cb_fn(bus_ctx, NULL, request->priv);
373  }
374 
375  amxc_var_clean(&filter.objects);
376  amxc_var_clean(&filter.amx_paths);
377  amxb_close_request(&request);
378  return rv;
379 }
static int amxb_ubus_list_object_describe(amxb_bus_ctx_t *bus_ctx, const char *object, ubus_filter_t *filter)
static int amxb_ubus_add(const char *recv_path, struct ubus_object_data *o, ubus_filter_t *filter, amxc_var_t *output, bool functions)
static void amxb_ubus_fill_list(amxb_bus_ctx_t *bus_ctx, const char *path, amxc_var_t *data, const char *type, bool fetch_names, amxc_var_t *retval)
static int amxb_ubus_list_native(amxb_ubus_t *amxb_ubus_ctx, const char *object, ubus_filter_t *filter)
static void amxb_ubus_path_to_depth(amxd_path_t *path, ubus_filter_t *filter, uint32_t depth)
static void amxb_ubus_add_functions(struct ubus_object_data *o, ubus_filter_t *filter, const char *path, amxc_var_t *retval)
struct _ubus_filter ubus_filter_t
static void amxb_ubus_objects_cb(AMXB_UNUSED struct ubus_context *c, struct ubus_object_data *o, void *p)
static int32_t amxb_ubus_object_depth(const char *path)
static bool amxb_ubus_has_function(struct ubus_object_data *o, const char *func_name)
int amxb_ubus_list(void *const ctx, const char *object, uint32_t flags, uint32_t access, amxb_request_t *request)
struct ubus_context * ubus_ctx
Definition: amxb_ubus.h:87
amxb_request_t * request
amxc_var_t amx_paths
uint32_t access
uint32_t flags
uint32_t path_len
amxc_var_t objects
uint32_t depth
const char * path
static amxb_bus_ctx_t * bus_ctx