libamxb  4.8.2
Bus Agnostic C API
amxb_ba_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 #include <errno.h>
61 #include <fcntl.h>
62 #include <string.h>
63 
64 #include <amxc/amxc.h>
65 #include <amxp/amxp.h>
66 
67 #include <amxd/amxd_dm.h>
68 #include <amxd/amxd_object.h>
69 
70 #include <amxb/amxb_be_intf.h>
71 #include <amxb/amxb.h>
72 
73 #include "amxb_priv.h"
74 
75 static int isdot(int c) {
76  if(c == '.') {
77  return 1;
78  }
79 
80  return 0;
81 }
82 
83 static int isbrace(int c) {
84  if((c == '(') || (c == ')')) {
85  return 1;
86  }
87 
88  return 0;
89 }
90 
91 static void amxb_trim(char* str, amxc_string_is_char_fn_t fn) {
92  amxc_string_t txt;
93  amxc_string_init(&txt, 0);
94  amxc_string_push_buffer(&txt, str, strlen(str) + 1);
95  amxc_string_trimr(&txt, fn);
96  amxc_string_take_buffer(&txt);
97  amxc_string_clean(&txt);
98 }
99 
100 static void amxb_complete_async_invoke(UNUSED const amxc_var_t* const data,
101  void* const priv) {
102  amxb_req_t* req = (amxb_req_t*) priv;
103  amxb_request_t* request = &req->data;
104  amxb_bus_ctx_t* ctx = amxb_request_get_ctx(request);
105  req->in_flight = false;
106 
107  if(req->closed) {
108  if(!req->wait) {
109  amxb_close_request(&request);
110  }
111 
112  goto exit;
113  }
114 
115  if((request->cb_fn != NULL) && (request->bus_retval == 0)) {
116  request->cb_fn(ctx, request->result, request->priv);
117  }
118  if(request->done_fn != NULL) {
119  request->done_fn(ctx, request, request->bus_retval, request->priv);
120  }
121 
122 exit:
123  return;
124 }
125 
126 static void amxb_complete_async_deferred_invoke(const amxc_var_t* const data,
127  void* const priv) {
128  amxc_var_t* rv = GET_ARG(data, "retval");
129  amxb_req_t* req = (amxb_req_t*) priv;
130  amxb_request_t* request = &req->data;
131 
132  amxc_var_set_type(request->result, AMXC_VAR_ID_LIST);
133  amxc_var_take_it(rv);
134  amxc_var_set_index(request->result, 0, rv, AMXC_VAR_FLAG_DEFAULT);
135  request->bus_retval = GET_UINT32(data, "status");
136 
137  amxb_complete_async_invoke(NULL, priv);
138 
139  return;
140 }
141 
142 static int amxb_invoke_local_impl(amxb_invoke_t* invoke_ctx,
143  amxb_bus_ctx_t* ctx,
144  amxc_var_t* args,
145  amxb_request_t* request,
146  bool async) {
147  int retval = -1;
148  amxb_req_t* req = amxc_container_of(request, amxb_req_t, data);
149  amxc_var_t empty_args;
150  amxc_var_t* fn_rv = NULL;
151  amxc_var_t* out_args = NULL;
152  amxd_object_t* object = NULL;
153  amxc_var_init(&empty_args);
154  amxc_var_set_type(&empty_args, AMXC_VAR_ID_HTABLE);
155 
156  object = amxb_fetch_local_object(ctx, invoke_ctx->object);
157  when_null_status(object, exit, retval = amxd_status_object_not_found);
158 
159  if(args == NULL) {
160  args = &empty_args;
161  }
162 
163  amxc_var_set_type(request->result, AMXC_VAR_ID_LIST);
164  fn_rv = amxc_var_add_new(request->result);
165 
166  retval = amxd_object_invoke_function(object, invoke_ctx->method, args, fn_rv);
167  if(!amxc_htable_is_empty(amxc_var_constcast(amxc_htable_t, args))) {
168  out_args = amxc_var_add_new(request->result);
169  amxc_var_move(out_args, args);
170  }
171 
172  request->bus_retval = retval;
173 
174  if(async) {
175  if((request->done_fn != NULL) || (request->cb_fn != NULL)) {
176  req->in_flight = true;
177  if(retval == amxd_status_deferred) {
178  uint64_t call_id = amxc_var_constcast(uint64_t, fn_rv);
179  amxd_function_set_deferred_cb(call_id, amxb_complete_async_deferred_invoke, req);
180  retval = 0;
181  } else {
182  amxp_sigmngr_deferred_call(NULL, amxb_complete_async_invoke, NULL, req);
183  retval = 0;
184  }
185  }
186  } else {
187  if(retval == amxd_status_deferred) {
188  uint64_t call_id = 0;
189  req->in_flight = true;
190  call_id = amxc_var_constcast(uint64_t, fn_rv);
191  amxd_function_set_deferred_cb(call_id, amxb_complete_async_deferred_invoke, req);
192  } else {
193  when_failed(retval, exit);
194  if(request->cb_fn != NULL) {
195  request->cb_fn(ctx, request->result, request->priv);
196  }
197  }
198  }
199 
200 exit:
201  amxc_var_clean(&empty_args);
202  return retval;
203 }
204 
205 static int amxb_invoke_be_impl(amxb_invoke_t* invoke_ctx,
206  amxb_bus_ctx_t* ctx,
207  amxc_var_t* args,
208  amxb_request_t* request,
209  int timeout,
210  bool async) {
211  amxc_var_t* func_args = args;
212  const amxb_be_funcs_t* fns = NULL;
213  int retval = -1;
214 
215  if((amxc_var_type_of(args) == AMXC_VAR_ID_NULL) ||
216  ( amxc_var_type_of(args) == AMXC_VAR_ID_INVALID)) {
217  func_args = NULL;
218  }
219 
220  fns = ctx->bus_fn;
221  if(async) {
222  if(amxb_is_valid_be_func(fns, invoke, fns->async_invoke)) {
223  retval = fns->async_invoke(ctx->bus_ctx,
224  invoke_ctx,
225  func_args,
226  request);
227  } else {
229  }
230  } else {
231  if(amxb_is_valid_be_func(fns, invoke, fns->invoke)) {
232  retval = fns->invoke(ctx->bus_ctx,
233  invoke_ctx,
234  func_args,
235  request,
236  timeout);
237  } else {
239  }
240  }
241 
242  return retval;
243 }
244 
245 static int amxb_invoke_wait_local(amxb_req_t* req, int timeout) {
246  int retval = -1;
247  if(req->in_flight) {
248  int fd = amxp_signal_fd();
249  struct timeval t = {
250  .tv_sec = timeout,
251  .tv_usec = 0
252  };
253 
254  fd_set readset;
255  FD_ZERO(&readset);
256  FD_SET(fd, &readset);
257 
258  req->wait = true;
259  while(req->in_flight) {
260  retval = select(fd + 1, &readset, NULL, NULL, &t);
261  if(retval <= 0) {
262  // timeout or error
263  retval = -1;
264  break;
265  }
266  amxp_signal_read();
267  }
268  req->wait = false;
269  }
270  if(!req->in_flight) {
271  retval = req->data.bus_retval;
272  if(req->closed) {
273  amxb_request_t* request = &req->data;
274  amxb_close_request(&request);
275  }
276  }
277 
278  return retval;
279 }
280 
282  amxb_bus_ctx_t* const ctx,
283  const char* object,
284  const char* interface,
285  const char* method) {
286  int retval = amxd_status_unknown_error;
287  const amxb_be_funcs_t* fns = NULL;
288 
289  when_null(invoke_ctx, exit);
290  when_not_null(*invoke_ctx, exit);
291  when_null(ctx, exit);
292  when_null(ctx->bus_ctx, exit);
293  when_str_empty(method, exit);
294 
295  *invoke_ctx = (amxb_invoke_t*) calloc(1, sizeof(amxb_invoke_t));
296  when_null((*invoke_ctx), exit);
297 
298  (*invoke_ctx)->object = object == NULL ? strdup("") : strdup(object);
299  when_null((*invoke_ctx)->object, exit);
300  (*invoke_ctx)->interface = interface == NULL ? NULL : strdup(interface);
301  (*invoke_ctx)->method = strdup(method);
302  when_null((*invoke_ctx)->method, exit);
303 
304  amxb_trim((*invoke_ctx)->object, isdot);
305  amxb_trim((*invoke_ctx)->method, isbrace);
306 
307  fns = ctx->bus_fn;
308  if(amxb_is_valid_be_func(fns, new_invoke, fns->new_invoke)) {
309  retval = fns->new_invoke(*invoke_ctx);
310  } else {
311  retval = 0;
312  }
313 
314  if(retval == 0) {
315  amxc_llist_append(&ctx->invoke_ctxs, &(*invoke_ctx)->it);
316  } else {
317  amxb_free_invoke(invoke_ctx);
318  }
319 
320 exit:
321  return retval;
322 }
323 
324 void amxb_free_invoke(amxb_invoke_t** invoke_ctx) {
325  const amxb_be_funcs_t* fns = NULL;
326  amxb_bus_ctx_t* ctx = NULL;
327 
328  when_null(invoke_ctx, exit);
329  when_null(*invoke_ctx, exit);
330 
331  if(amxc_llist_it_is_in_list(&(*invoke_ctx)->it)) {
332  ctx = amxc_container_of((*invoke_ctx)->it.llist,
333  amxb_bus_ctx_t, invoke_ctxs);
334  fns = ctx->bus_fn;
335  if(amxb_is_valid_be_func(fns, free_invoke, fns->free_invoke)) {
336  fns->free_invoke(*invoke_ctx);
337  }
338  }
339 
340  amxc_llist_it_take(&(*invoke_ctx)->it);
341  free((*invoke_ctx)->object);
342  free((*invoke_ctx)->interface);
343  free((*invoke_ctx)->method);
344  free((*invoke_ctx));
345 
346  *invoke_ctx = NULL;
347 
348 exit:
349  return;
350 }
351 
352 int amxb_invoke(amxb_invoke_t* invoke_ctx,
353  amxc_var_t* args,
354  amxc_var_t* ret,
355  amxb_be_cb_fn_t fn,
356  void* priv,
357  int timeout) {
358  int retval = -1;
359  amxb_bus_ctx_t* ctx = NULL;
360  amxc_var_t temp_var;
361  amxb_req_t* req = NULL;
362  amxb_request_t* request = NULL;
363 
364  amxc_var_init(&temp_var);
365 
366  when_null(invoke_ctx, exit);
367  when_null(invoke_ctx->it.llist, exit);
368 
369  ctx = amxc_container_of(invoke_ctx->it.llist, amxb_bus_ctx_t, invoke_ctxs);
370  req = (amxb_req_t*) calloc(1, sizeof(amxb_req_t));
371  when_null(req, exit);
372  request = &req->data;
373 
374  request->result = (ret == NULL) ? &temp_var : ret;
375  request->cb_fn = fn;
376  request->done_fn = NULL;
377  request->bus_retval = 0;
378  request->bus_data = NULL;
379  request->priv = priv;
380 
381  if(amxb_is_local_object(ctx, invoke_ctx->object)) {
382  retval = amxb_invoke_local_impl(invoke_ctx, ctx, args, request, false);
383  if(retval == amxd_status_deferred) {
384  retval = amxb_invoke_wait_local(req, timeout);
385  }
386  } else {
387  retval = amxb_invoke_be_impl(invoke_ctx, ctx, args, request, timeout, false);
388  }
389  free(req);
390 
391 exit:
392  amxc_var_clean(&temp_var);
393  return retval;
394 }
395 
397  amxc_var_t* args,
398  amxb_be_cb_fn_t fn,
399  amxb_be_done_cb_fn_t done_fn,
400  void* priv,
401  amxb_request_t** request) {
402  int retval = -1;
403  amxb_bus_ctx_t* ctx = NULL;
404  amxb_req_t* req = NULL;
405 
406  when_null(invoke_ctx, exit);
407  when_null(invoke_ctx->it.llist, exit);
408  when_null(request, exit);
409  ctx = amxc_container_of(invoke_ctx->it.llist, amxb_bus_ctx_t, invoke_ctxs);
410 
411  req = (amxb_req_t*) calloc(1, sizeof(amxb_req_t));
412  when_null(req, exit);
413  *request = &req->data;
414 
415  amxc_var_new(&req->data.result);
416  req->data.cb_fn = fn;
417  req->data.done_fn = done_fn;
418  req->data.priv = priv;
419 
420  if(amxb_is_local_object(ctx, invoke_ctx->object)) {
421  req->local = true;
422  retval = amxb_invoke_local_impl(invoke_ctx, ctx, args, *request, true);
423  } else {
424  req->local = false;
425  retval = amxb_invoke_be_impl(invoke_ctx, ctx, args, *request, 0, true);
426  }
427 
428  if(retval == 0) {
429  amxc_llist_append(&ctx->requests, &req->it);
430  }
431 
432 exit:
433  if((retval != 0) && (req != NULL)) {
434  amxc_var_delete(&req->data.result);
435  free(req);
436  *request = NULL;
437  }
438 
439  return retval;
440 }
441 
442 int amxb_wait_for_request(amxb_request_t* request, int timeout) {
443  int retval = -1;
444  amxb_req_t* req = NULL;
445  const amxb_be_funcs_t* fns = NULL;
446  const amxb_bus_ctx_t* ctx = NULL;
447 
448  when_null(request, exit);
449  req = amxc_container_of(request, amxb_req_t, data);
450 
451  if(req->local) {
452  retval = amxb_invoke_wait_local(req, timeout);
453  goto exit;
454  }
455 
456  ctx = amxb_request_get_ctx(request);
457  when_null(ctx, exit);
458  fns = ctx->bus_fn;
459 
460  if(amxb_is_valid_be_func(fns, wait_request, fns->wait_request)) {
461  retval = fns->wait_request(ctx->bus_ctx, request, timeout);
462  } else {
464  }
465 
466 exit:
467  return retval;
468 }
469 
471  amxb_req_t* req = NULL;
472  int retval = AMXB_ERROR_UNKNOWN;
473 
474  req = (amxb_req_t*) calloc(1, sizeof(amxb_req_t));
475  when_null(req, exit);
476 
477  *request = &req->data;
478  retval = AMXB_STATUS_OK;
479 
480 exit:
481  return retval;
482 }
483 
485  amxb_req_t* req = NULL;
486  amxb_bus_ctx_t* ctx = NULL;
487 
488  when_null(request, exit);
489  req = amxc_container_of(request, amxb_req_t, data);
490  if(amxc_llist_it_is_in_list(&req->it)) {
491  ctx = amxc_container_of(req->it.llist, amxb_bus_ctx_t, requests);
492  }
493 
494 exit:
495  return ctx;
496 }
497 
499  int retval = -1;
500  const amxb_bus_ctx_t* ctx = NULL;
501  const amxb_be_funcs_t* fns = NULL;
502  amxb_req_t* req = NULL;
503 
504  when_null(request, exit);
505  when_null((*request), exit);
506 
507  req = amxc_container_of(*request, amxb_req_t, data);
508  amxc_var_delete(&(*request)->result);
509 
510  if(req->in_flight || req->wait) {
511  req->closed = true;
512  *request = NULL;
513  retval = 0;
514  goto exit;
515  }
516 
517  retval = 0;
518  when_false(amxc_llist_it_is_in_list(&req->it), exit_free);
519 
520  ctx = amxc_container_of(req->it.llist, amxb_bus_ctx_t, requests);
521  fns = ctx->bus_fn;
522 
523  if(amxb_is_valid_be_func(fns, close_request, fns->close_request)) {
524  retval = fns->close_request(ctx->bus_ctx, *request);
525  } else {
527  }
528 
529  amxc_llist_it_take(&req->it);
530 
531 exit_free:
532  free(req);
533  *request = NULL;
534 exit:
535  return retval;
536 }
Ambiorix bus agnostic API header file.
static int amxb_invoke_wait_local(amxb_req_t *req, int timeout)
static void amxb_complete_async_invoke(UNUSED const amxc_var_t *const data, void *const priv)
static int amxb_invoke_local_impl(amxb_invoke_t *invoke_ctx, amxb_bus_ctx_t *ctx, amxc_var_t *args, amxb_request_t *request, bool async)
static void amxb_trim(char *str, amxc_string_is_char_fn_t fn)
static void amxb_complete_async_deferred_invoke(const amxc_var_t *const data, void *const priv)
static int isdot(int c)
static int amxb_invoke_be_impl(amxb_invoke_t *invoke_ctx, amxb_bus_ctx_t *ctx, amxc_var_t *args, amxb_request_t *request, int timeout, bool async)
static int isbrace(int c)
Ambiorix Bus Backend Interface.
#define amxb_is_valid_be_func(ft, member, ptr)
Definition: amxb_priv.h:78
amxd_object_t * amxb_fetch_local_object(amxb_bus_ctx_t *ctx, const char *object)
Definition: amxb_ba_priv.c:87
bool PRIVATE amxb_is_local_object(amxb_bus_ctx_t *ctx, const char *obj_path)
Definition: amxb_ba_priv.c:94
void(* amxb_be_cb_fn_t)(const amxb_bus_ctx_t *bus_ctx, const amxc_var_t *const data, void *priv)
Definition: amxb_types.h:85
void(* amxb_be_done_cb_fn_t)(const amxb_bus_ctx_t *bus_ctx, amxb_request_t *req, int status, void *priv)
Definition: amxb_types.h:89
#define AMXB_ERROR_UNKNOWN
Unknown error.
Definition: amxb_error.h:80
#define AMXB_ERROR_NOT_SUPPORTED_OP
Function/operation not supported.
Definition: amxb_error.h:110
#define AMXB_STATUS_OK
All ok.
Definition: amxb_error.h:86
int amxb_wait_for_request(amxb_request_t *request, int timeout)
Waits for an asynchronous remote function invoke to finish.
int amxb_request_new(amxb_request_t **request)
Allocates an empty amxb_request_t.
int amxb_close_request(amxb_request_t **request)
Closes a previously create remote function called context.
int amxb_new_invoke(amxb_invoke_t **invoke_ctx, amxb_bus_ctx_t *const ctx, const char *object, const char *interface, const char *method)
Prepares a remote function invocation.
void amxb_free_invoke(amxb_invoke_t **invoke_ctx)
Deletes a function invoke context, and frees allocated memory.
int amxb_async_invoke(amxb_invoke_t *invoke_ctx, amxc_var_t *args, amxb_be_cb_fn_t fn, amxb_be_done_cb_fn_t done_fn, void *priv, amxb_request_t **request)
Invokes a remote function, as defined by the function invoke context.
amxb_bus_ctx_t * amxb_request_get_ctx(amxb_request_t *request)
Get the bus context of the request if any.
int amxb_invoke(amxb_invoke_t *invoke_ctx, amxc_var_t *args, amxc_var_t *ret, amxb_be_cb_fn_t fn, void *priv, int timeout)
Invokes a remote function, as defined by the function invoke context.
The back-end interface structure.
amxb_be_free_invoke_fn_t free_invoke
amxb_be_new_invoke_fn_t new_invoke
amxb_be_close_request_fn_t close_request
amxb_be_invoke_fn_t invoke
amxb_be_async_invoke_fn_t async_invoke
amxb_be_wait_request_fn_t wait_request
const amxb_be_funcs_t * bus_fn
Definition: amxb_types.h:121
amxc_llist_t requests
Definition: amxb_types.h:118
amxc_llist_t invoke_ctxs
Definition: amxb_types.h:119
void * bus_ctx
Definition: amxb_types.h:122
amxc_llist_it_t it
Definition: amxb_types.h:150
char * method
Definition: amxb_types.h:153
char * object
Definition: amxb_types.h:151
A request structure.
Definition: amxb_types.h:138
amxc_var_t * result
Definition: amxb_types.h:140
amxb_be_cb_fn_t cb_fn
Definition: amxb_types.h:141
void * bus_data
Definition: amxb_types.h:144
amxb_be_done_cb_fn_t done_fn
Definition: amxb_types.h:142
Definition: amxb_priv.h:69
bool in_flight
Definition: amxb_priv.h:73
bool closed
Definition: amxb_priv.h:74
amxc_llist_it_t it
Definition: amxb_priv.h:70
amxb_request_t data
Definition: amxb_priv.h:71
bool wait
Definition: amxb_priv.h:75
bool local
Definition: amxb_priv.h:72