Amxb_Ubus  3.3.1
Ambiorix Ubus API
amxb_ubus_invoke.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 <stdlib.h>
60 
61 #include <amxc/amxc_string_split.h>
62 
63 #include "amxb_ubus.h"
64 
65 static int amxb_ubus_status_to_amxd_status(int rv, amxc_var_t* result) {
66  int retval = amxd_status_ok;
67  amxc_var_t* error = GETI_ARG(result, 2);
68 
69  int status[] = {
70  amxd_status_ok, // UBUS_STATUS_OK
71  amxd_status_invalid_function, // UBUS_STATUS_INVALID_COMMAND
72  amxd_status_invalid_arg, // UBUS_STATUS_INVALID_ARGUMENT
73  amxd_status_function_not_found, // UBUS_STATUS_METHOD_NOT_FOUND
74  amxd_status_object_not_found, // UBUS_STATUS_NOT_FOUND,
75  amxd_status_unknown_error, // UBUS_STATUS_NO_DATA
76  amxd_status_permission_denied, // UBUS_STATUS_PERMISSION_DENIED
77  amxd_status_timeout, // UBUS_STATUS_TIMEOUT
78  amxd_status_not_supported, // UBUS_STATUS_NOT_SUPPORTED
79  amxd_status_unknown_error, // UBUS_STATUS_UNKNOWN_ERROR
80  amxd_status_unknown_error, // UBUS_STATUS_CONNECTION_FAILED
81  };
82 
83  if((error != NULL) && (GET_ARG(error, "amxd-error-code") != NULL)) {
84  // to be able to pass the amxd error code 3 results are passed over ubus
85  // 1 - the function return value
86  // 2 - the function out arguments
87  // 3 - the amxd error code
88  retval = GET_UINT32(error, "amxd-error-code");
89  amxc_var_delete(&error);
90  // if the error code was found, check if nr 2 is an empty table,
91  // if an empty table remove it from the result.
92  error = GETI_ARG(result, 1);
93  if((amxc_var_type_of(error) == AMXC_VAR_ID_HTABLE) &&
94  amxc_htable_is_empty(amxc_var_constcast(amxc_htable_t, error))) {
95  amxc_var_delete(&error);
96  }
97  } else {
98  if((rv >= 0) && (rv < (int) (sizeof(status) / sizeof(int)))) {
99  retval = status[rv];
100  } else {
101  retval = AMXB_ERROR_BUS_UNKNOWN;
102  }
103  }
104 
105  return retval;
106 }
107 
108 
109 static void amxb_ubus_transform_args(amxc_var_t* dest, amxc_var_t* args, const char* method) {
110  amxc_var_set_type(dest, AMXC_VAR_ID_HTABLE);
111  if(method[0] != '_') {
112  amxc_var_add_key(cstring_t, dest, "method", method);
113  if(args != NULL) {
114  amxc_var_set_key(dest, "args", args, AMXC_VAR_FLAG_COPY);
115  } else {
116  amxc_var_add_key(amxc_htable_t, dest, "args", NULL);
117  }
118  } else {
119  if(args != NULL) {
120  amxc_var_copy(dest, args);
121  }
122  }
123 }
124 
125 static void amxb_ubus_convert_out_args(amxc_var_t* result) {
126  amxc_var_t* out_args = GETI_ARG(result, 1);
127  amxc_var_t* args = GET_ARG(out_args, "args");
128  amxc_var_t copy_var;
129 
130  amxc_var_init(&copy_var);
131 
132  when_null(args, exit);
133 
134  amxc_var_move(&copy_var, args);
135  amxc_var_move(out_args, &copy_var);
136 
137 exit:
138  amxc_var_clean(&copy_var);
139  return;
140 }
141 
142 static void amxb_ubus_request_done(struct ubus_request* req, int ret) {
143  amxb_ubus_request_t* amxb_ubus_req = (amxb_ubus_request_t*) req->priv;
144  amxb_request_t* request = amxb_ubus_req->request;
145  amxc_var_t* var_method_name = GET_ARG(&amxb_ubus_req->args, "method");
146  const char* method = GET_CHAR(var_method_name, NULL);
147  const amxb_bus_ctx_t* ctx = amxb_request_get_ctx(request);
148  amxb_ubus_t* amxb_ubus_ctx = (amxb_ubus_t*) ctx->bus_ctx;
149 
150  if((ret == UBUS_STATUS_METHOD_NOT_FOUND) && (method != NULL) && (method[0] != '_')) {
151  const amxc_htable_t* htable = amxc_var_constcast(amxc_htable_t, &amxb_ubus_req->args);
152  blob_buf_init(&amxb_ubus_ctx->b, 0);
153 
154  amxb_ubus_format_blob_table(htable, &amxb_ubus_ctx->b);
155 
156  ret = ubus_invoke_async(amxb_ubus_ctx->ubus_ctx,
157  amxb_ubus_req->id,
158  "_exec",
159  amxb_ubus_ctx->b.head,
160  amxb_ubus_req->ubus_req);
161 
162  amxb_ubus_req->ubus_req->data_cb = amxb_ubus_result_data;
163  amxb_ubus_req->ubus_req->complete_cb = amxb_ubus_request_done;
164  amxb_ubus_req->ubus_req->priv = amxb_ubus_req;
165  amxb_ubus_req->converted = true;
166 
167  amxc_var_set(cstring_t, var_method_name, "_exec");
168  ubus_complete_request_async(amxb_ubus_ctx->ubus_ctx, amxb_ubus_req->ubus_req);
169 
170  if(ret == 0) {
171  return;
172  }
173  }
174 
175  ret = amxb_ubus_status_to_amxd_status(ret, request->result);
176  request->bus_retval = ret;
177  if(amxb_ubus_req->converted) {
178  amxb_ubus_convert_out_args(request->result);
179  }
180 
181  if(request->done_fn != NULL) {
182  request->done_fn(ctx, request, ret, request->priv);
183  }
184 }
185 
186 /*
187  When using key object paths, which are not known by ubusd, try to find the
188  largest possible path, known by ubusd.
189 
190  When one of the internal data model methods are called (_get, _set, ....) update
191  the rel_path argument or add it when not available.
192 
193  If not one of the internal data model methods, convert to `_exec` method
194  */
195 static int amxb_ubus_convert(amxb_ubus_t* amxb_ubus_ctx,
196  amxb_invoke_t* invoke_ctx,
197  amxc_var_t* args) {
198  amxd_path_t path;
199  char* object = NULL;
200  char* method = invoke_ctx->method;
201  amxc_string_t rel_path;
202  int retval = -1;
203 
204  amxc_string_init(&rel_path, 64);
205  amxd_path_init(&path, NULL);
206  amxd_path_setf(&path, true, "%s", invoke_ctx->object);
207 
208  free(invoke_ctx->object);
209  invoke_ctx->object = NULL;
210  invoke_ctx->method = NULL;
211 
212  when_failed(amxb_ubus_get_longest_path(amxb_ubus_ctx, &path, &rel_path), exit);
213 
214  object = strdup(amxd_path_get(&path, 0));
215 
216  if(method[0] == '_') {
217  amxc_var_t* vrel_path = GET_ARG(args, "rel_path");
218  if(vrel_path == NULL) {
219  amxc_var_add_key(cstring_t, args, "rel_path", amxc_string_get(&rel_path, 0));
220  } else {
221  amxc_string_appendf(&rel_path, "%s", GET_CHAR(vrel_path, NULL));
222  amxc_var_set(cstring_t, vrel_path, amxc_string_get(&rel_path, 0));
223  }
224  invoke_ctx->method = method;
225  method = NULL;
226  } else {
227  amxc_var_add_key(cstring_t, args, "rel_path", amxc_string_get(&rel_path, 0));
228  invoke_ctx->method = strdup("_exec");
229  }
230  invoke_ctx->object = object;
231  object = NULL;
232 
233  retval = 0;
234 
235 exit:
236  free(object);
237  free(method);
238  amxc_string_clean(&rel_path);
239  amxd_path_clean(&path);
240  return retval;
241 }
242 
243 int PRIVATE amxb_ubus_get_longest_path(amxb_ubus_t* amxb_ubus_ctx,
244  amxd_path_t* path,
245  amxc_string_t* rel_path) {
246  int retval = -1;
247  char* part = NULL;
248  const char* object = NULL;
249  uint32_t id = 0;
250 
251  do{
252  object = amxd_path_get(path, 0);
253  when_str_empty(object, exit);
254  retval = ubus_lookup_id(amxb_ubus_ctx->ubus_ctx, object, &id);
255  if((retval == 0) && (id != 0)) {
256  break;
257  }
258  free(part);
259  part = amxd_path_get_last(path, true);
260  amxc_string_prependf(rel_path, "%s", part);
261  } while (part != NULL);
262 
263 exit:
264  free(part);
265  return retval;
266 }
267 
268 void PRIVATE amxb_ubus_result_data(struct ubus_request* req,
269  AMXB_UNUSED int type,
270  struct blob_attr* msg) {
271  amxb_ubus_request_t* amxb_ubus_req = (amxb_ubus_request_t*) req->priv;
272  amxb_request_t* request = amxb_ubus_req->request;
273 
274  amxc_var_t* result = request->result;
275  amxc_var_t* item = NULL;
276  amxc_var_t* rv = NULL;
277  const amxb_bus_ctx_t* ctx = NULL;
278 
279  amxc_var_new(&item);
280  ctx = amxb_request_get_ctx(request);
281 
282  if((msg != NULL) && (result != NULL)) {
283  amxc_var_set_type(item, AMXC_VAR_ID_HTABLE);
284  amxb_ubus_parse_blob_table(item, (struct blob_attr*) blob_data(msg), blob_len(msg));
285  rv = GET_ARG(item, "retval");
286  if(rv == NULL) {
287  amxc_var_set_index(result, -1, item, AMXC_VAR_FLAG_DEFAULT);
288  } else {
289  amxc_var_take_it(rv);
290  amxc_var_set_index(result, -1, rv, AMXC_VAR_FLAG_DEFAULT);
291  amxc_var_delete(&item);
292  item = rv;
293  }
294  } else {
295  amxc_var_delete(&item);
296  }
297 
298  if((request->cb_fn != NULL) && (item != NULL)) {
299  request->cb_fn(ctx, item, request->priv);
300  }
301 }
302 
303 int PRIVATE amxb_ubus_invoke_base(amxb_ubus_t* amxb_ubus_ctx,
304  const char* object,
305  amxc_var_t* args,
306  amxb_request_t* request,
307  uint32_t* id) {
308  int retval = -1;
309 
310  // lookup id can cause the ubus blob msg to be overwritten
311  // first do the lookup and then initialize the blob
312  retval = ubus_lookup_id(amxb_ubus_ctx->ubus_ctx, object, id);
313  if(retval != 0) {
314  goto exit;
315  }
316 
317  blob_buf_init(&amxb_ubus_ctx->b, 0);
318 
319  if(args != NULL) {
320  when_true(amxc_var_type_of(args) != AMXC_VAR_ID_HTABLE, exit);
321  const amxc_htable_t* htable = amxc_var_constcast(amxc_htable_t, args);
322  when_failed(amxb_ubus_format_blob_table(htable, &amxb_ubus_ctx->b), exit);
323  }
324 
325  amxc_var_set_type(request->result, AMXC_VAR_ID_LIST);
326 
327 exit:
328  return retval;
329 }
330 
331 int PRIVATE amxb_ubus_invoke(void* const ctx,
332  amxb_invoke_t* invoke_ctx,
333  amxc_var_t* args,
334  amxb_request_t* request,
335  int timeout) {
336  amxb_ubus_t* amxb_ubus_ctx = (amxb_ubus_t*) ctx;
337  uint32_t id = 0;
338  int ret = amxd_status_unknown_error;
339  amxb_ubus_request_t* amxb_ubus_req = NULL;
340 
341  when_str_empty(invoke_ctx->object, exit);
342  amxb_ubus_req = (amxb_ubus_request_t*) calloc(1, sizeof(amxb_ubus_request_t));
343  when_null(amxb_ubus_req, exit);
344  amxc_var_init(&amxb_ubus_req->args);
345  amxb_ubus_transform_args(&amxb_ubus_req->args, args, invoke_ctx->method);
346 
347  if(amxb_ubus_invoke_base(amxb_ubus_ctx,
348  invoke_ctx->object,
349  args,
350  request,
351  &id) != 0) {
352  when_failed(amxb_ubus_convert(amxb_ubus_ctx, invoke_ctx, &amxb_ubus_req->args), exit);
353  ret = amxb_ubus_invoke_base(amxb_ubus_ctx,
354  invoke_ctx->object,
355  &amxb_ubus_req->args,
356  request,
357  &id);
358  ret = amxb_ubus_status_to_amxd_status(ret, request->result);
359  request->bus_retval = ret;
360  amxb_ubus_req->converted = true;
361  when_failed(ret, exit);
362  }
363 
364  amxb_ubus_req->request = request;
365  request->bus_data = amxb_ubus_req;
366 
367  ret = ubus_invoke(amxb_ubus_ctx->ubus_ctx,
368  id,
369  invoke_ctx->method,
370  amxb_ubus_ctx->b.head,
372  amxb_ubus_req,
373  timeout * 1000);
374 
375  if((ret == UBUS_STATUS_METHOD_NOT_FOUND) && (invoke_ctx->method[0] != '_')) {
376  const amxc_htable_t* htable = NULL;
377  htable = amxc_var_constcast(amxc_htable_t, &amxb_ubus_req->args);
378  blob_buf_init(&amxb_ubus_ctx->b, 0);
379 
380  amxb_ubus_format_blob_table(htable, &amxb_ubus_ctx->b);
381 
382  ret = ubus_invoke(amxb_ubus_ctx->ubus_ctx,
383  id,
384  "_exec",
385  amxb_ubus_ctx->b.head,
387  amxb_ubus_req,
388  timeout * 1000);
389  }
390 
391  ret = amxb_ubus_status_to_amxd_status(ret, request->result);
392  request->bus_retval = ret;
393  if(amxb_ubus_req->converted) {
394  amxb_ubus_convert_out_args(request->result);
395  }
396 
397 exit:
398  if(amxb_ubus_req != NULL) {
399  amxc_var_clean(&amxb_ubus_req->args);
400  }
401  free(amxb_ubus_req);
402  return ret;
403 }
404 
405 int PRIVATE amxb_ubus_async_invoke(void* const ctx,
406  amxb_invoke_t* invoke_ctx,
407  amxc_var_t* args,
408  amxb_request_t* request) {
409  amxb_ubus_t* amxb_ubus_ctx = (amxb_ubus_t*) ctx;
410  uint32_t id = 0;
411  int ret = -1;
412  amxb_ubus_request_t* amxb_ubus_req = NULL;
413 
414  when_str_empty(invoke_ctx->object, exit);
415  amxb_ubus_req = (amxb_ubus_request_t*) calloc(1, sizeof(amxb_ubus_request_t));
416  when_null(amxb_ubus_req, exit);
417  amxc_var_init(&amxb_ubus_req->args);
418  amxb_ubus_transform_args(&amxb_ubus_req->args, args, invoke_ctx->method);
419 
420  if(amxb_ubus_invoke_base(amxb_ubus_ctx,
421  invoke_ctx->object,
422  args,
423  request,
424  &id) != 0) {
425  when_failed(amxb_ubus_convert(amxb_ubus_ctx, invoke_ctx, &amxb_ubus_req->args), exit);
426  when_failed(amxb_ubus_invoke_base(amxb_ubus_ctx,
427  invoke_ctx->object,
428  &amxb_ubus_req->args,
429  request,
430  &id),
431  exit);
432  }
433 
434  amxb_ubus_req->request = request;
435  amxb_ubus_req->id = id;
436  request->bus_data = amxb_ubus_req;
437 
438  amxb_ubus_req->ubus_req = (struct ubus_request*) calloc(1, sizeof(struct ubus_request));
439  when_null(amxb_ubus_req->ubus_req, exit);
440 
441  ret = ubus_invoke_async(amxb_ubus_ctx->ubus_ctx,
442  id,
443  invoke_ctx->method,
444  amxb_ubus_ctx->b.head,
445  amxb_ubus_req->ubus_req);
446 
447  amxb_ubus_req->ubus_req->data_cb = amxb_ubus_result_data;
448  amxb_ubus_req->ubus_req->complete_cb = amxb_ubus_request_done;
449  amxb_ubus_req->ubus_req->priv = amxb_ubus_req;
450 
451  ubus_complete_request_async(amxb_ubus_ctx->ubus_ctx, amxb_ubus_req->ubus_req);
452 
453 exit:
454  if(ret != 0) {
455  if(amxb_ubus_req != NULL) {
456  amxc_var_clean(&amxb_ubus_req->args);
457  free(amxb_ubus_req->ubus_req);
458  }
459  free(amxb_ubus_req);
460  request->bus_data = NULL;
461  }
462  return ret;
463 }
464 
465 int PRIVATE amxb_ubus_wait_request(AMXB_UNUSED void* const ctx,
466  amxb_request_t* request,
467  int timeout) {
468  amxb_ubus_t* amxb_ubus_ctx = (amxb_ubus_t*) ctx;
469  amxb_ubus_request_t* amxb_ubus_req = (amxb_ubus_request_t*) request->bus_data;
470  struct ubus_request* ubus_req = amxb_ubus_req->ubus_req;
471 
472  return ubus_complete_request(amxb_ubus_ctx->ubus_ctx, ubus_req, timeout);
473 }
474 
475 int PRIVATE amxb_ubus_close_request(void* const ctx,
476  amxb_request_t* request) {
477  amxb_ubus_t* amxb_ubus_ctx = (amxb_ubus_t*) ctx;
478  amxb_ubus_request_t* amxb_ubus_req = (amxb_ubus_request_t*) request->bus_data;
479 
480  if(amxb_ubus_req != NULL) {
481  struct ubus_request* ubus_req = amxb_ubus_req->ubus_req;
482  ubus_abort_request(amxb_ubus_ctx->ubus_ctx, ubus_req);
483  amxc_var_clean(&amxb_ubus_req->args);
484  free(ubus_req);
485  free(amxb_ubus_req);
486  request->bus_data = NULL;
487  }
488  return 0;
489 }
int PRIVATE amxb_ubus_format_blob_table(const amxc_htable_t *table, struct blob_buf *b)
int PRIVATE amxb_ubus_parse_blob_table(amxc_var_t *var, struct blob_attr *attr, int len)
static void amxb_ubus_transform_args(amxc_var_t *dest, amxc_var_t *args, const char *method)
int PRIVATE amxb_ubus_close_request(void *const ctx, amxb_request_t *request)
static int amxb_ubus_convert(amxb_ubus_t *amxb_ubus_ctx, amxb_invoke_t *invoke_ctx, amxc_var_t *args)
int PRIVATE amxb_ubus_invoke_base(amxb_ubus_t *amxb_ubus_ctx, const char *object, amxc_var_t *args, amxb_request_t *request, uint32_t *id)
static void amxb_ubus_convert_out_args(amxc_var_t *result)
static int amxb_ubus_status_to_amxd_status(int rv, amxc_var_t *result)
int PRIVATE amxb_ubus_invoke(void *const ctx, amxb_invoke_t *invoke_ctx, amxc_var_t *args, amxb_request_t *request, int timeout)
int PRIVATE amxb_ubus_wait_request(AMXB_UNUSED void *const ctx, amxb_request_t *request, int timeout)
int PRIVATE amxb_ubus_async_invoke(void *const ctx, amxb_invoke_t *invoke_ctx, amxc_var_t *args, amxb_request_t *request)
static void amxb_ubus_request_done(struct ubus_request *req, int ret)
void PRIVATE amxb_ubus_result_data(struct ubus_request *req, AMXB_UNUSED int type, struct blob_attr *msg)
int PRIVATE amxb_ubus_get_longest_path(amxb_ubus_t *amxb_ubus_ctx, amxd_path_t *path, amxc_string_t *rel_path)
struct ubus_request * ubus_req
Definition: amxb_ubus.h:106
amxb_request_t * request
Definition: amxb_ubus.h:109
amxc_var_t args
Definition: amxb_ubus.h:108
struct blob_buf b
Definition: amxb_ubus.h:89
struct ubus_context * ubus_ctx
Definition: amxb_ubus.h:87