libamxrt  0.4.2
Ambiorix Run Time Library
amxrt_connections.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 <string.h>
61 #include <sys/stat.h>
62 
63 #include <amxrt/amxrt.h>
64 #include <amxp/amxp_dir.h>
65 #include <amxp/amxp_connection.h>
66 #include <amxb/amxb_register.h>
67 
68 #include "amxrt_priv.h"
69 
70 static void amxrt_connection_read(UNUSED int fd, UNUSED void* priv) {
71  amxb_bus_ctx_t* bus_ctx = (amxb_bus_ctx_t*) priv;
72  int rv = 0;
73 
74  if(amxb_is_data_socket(bus_ctx)) {
75  rv = amxb_read(bus_ctx);
76  } else {
77  amxb_bus_ctx_t* new_ctx = NULL;
78  rv = amxb_accept(bus_ctx, &new_ctx);
79  if(rv == 0) {
80  fd = amxb_get_fd(new_ctx);
81  amxp_connection_add(fd, amxrt_connection_read, NULL, AMXO_BUS, new_ctx);
82  }
83  }
84  if(rv != 0) {
85  amxc_llist_t* connections = amxp_connection_get_connections();
86  amxp_connection_t* con = amxrt_el_get_connection(connections, fd);
87  when_null(con, exit);
88  amxrt_print_message("Failed to read from %s (%d), did the remote end hang up?",
89  con->uri == NULL ? "accepted" : con->uri, fd);
90  amxrt_print_message("=> Removing %s (%d)",
91  con->uri == NULL ? "accepted" : con->uri, fd);
92  amxp_connection_remove(fd);
93  amxb_free(&bus_ctx);
94 
95  if(amxc_llist_is_empty(amxp_connection_get_connections()) &&
96  amxc_llist_is_empty(amxp_connection_get_listeners())) {
97  amxrt_print_error("No more connections available");
98  amxrt_print_message("=> Stop application");
99  amxrt_el_stop();
100  }
101  }
102 
103 exit:
104  return;
105 }
106 
107 static int amxrt_connection_listen_bus_uri(const char* uri, amxb_bus_ctx_t** bus_ctx) {
108  int retval = amxb_listen(bus_ctx, uri);
109  if(retval != 0) {
110  amxrt_print_error("(%d) Failed to listen on [%s]\n", retval, uri);
111  retval = 3;
112  }
113 
114  return retval;
115 }
116 
117 static int amxrt_connection_connect_all_type(const amxc_llist_t* uris,
118  uint32_t type) {
119  int retval = 0;
120 
121  amxc_llist_for_each(it, uris) {
122  amxb_bus_ctx_t* bus_ctx = NULL;
123  int fd = -1;
124  const char* uri = amxc_var_constcast(cstring_t,
125  amxc_var_from_llist_it(it));
126 
127  retval = amxb_connect(&bus_ctx, uri);
128  if(retval != 0) {
129  amxrt_print_error("(%d) Failed to connect to [%s]\n", retval, uri);
130  } else {
131  fd = amxb_get_fd(bus_ctx);
132  amxp_connection_add(fd, amxrt_connection_read, uri, type, bus_ctx);
133  }
134  }
135 
136  return retval;
137 }
138 
139 static bool amxrt_list_contains(amxc_var_t* list, const char* value) {
140  bool retval = false;
141  amxc_var_for_each(v, list) {
142  const char* stored = GET_CHAR(v, NULL);
143  if(strcmp(stored, value) == 0) {
144  retval = true;
145  break;
146  }
147  }
148 
149  return retval;
150 }
151 
152 static int amxrt_connection_add_usp_socket(const char* name, void* priv) {
153  amxc_var_t* sockets = (amxc_var_t*) priv;
154  amxc_string_t filename;
155  amxc_string_init(&filename, 0);
156  amxc_string_setf(&filename, "usp:%s", name);
157  if(!amxrt_list_contains(sockets, amxc_string_get(&filename, 0))) {
158  amxc_var_add(cstring_t, sockets, amxc_string_get(&filename, 0));
159  }
160  amxc_string_clean(&filename);
161 
162  return 0;
163 }
164 
165 static void amxrt_connection_filter_uris(const amxc_llist_t* uris,
166  const amxc_llist_t* exclude_uris) {
167  int cresult = 0;
168  amxc_var_t* euri_var = NULL;
169  amxc_var_t* uri_var = NULL;
170 
171  amxc_llist_for_each(eit, exclude_uris) {
172  euri_var = amxc_var_from_llist_it(eit);
173  amxc_llist_for_each(it, uris) {
174  uri_var = amxc_var_from_llist_it(it);
175  if((amxc_var_compare(uri_var, euri_var, &cresult) == 0) && (cresult == 0)) {
176  amxc_llist_it_take(it);
177  variant_list_it_free(it);
178  }
179  }
180  }
181 }
182 
183 static void amxrt_add_known_uris(amxc_var_t* uris) {
184  struct stat sb;
185 
186  // always check if not already added
187  if(amxb_be_get_info("pcb") != NULL) {
188  if(stat("/var/run/pcb_sys", &sb) == 0) {
189  if(!amxrt_list_contains(uris, "pcb:/var/run/pcb_sys")) {
190  amxc_var_add(cstring_t, uris, "pcb:/var/run/pcb_sys");
191  }
192  }
193  }
194  if(amxb_be_get_info("ubus") != NULL) {
195  if((stat("/var/run/ubus.sock", &sb) == 0) || (stat("/var/run/ubus/ubus.sock", &sb) == 0)) {
196  if(stat("/var/run/ubus.sock", &sb) == 0) {
197  if(!amxrt_list_contains(uris, "ubus:/var/run/ubus.sock")) {
198  amxc_var_add(cstring_t, uris, "ubus:/var/run/ubus.sock");
199  }
200  } else {
201  if(!amxrt_list_contains(uris, "ubus:/var/run/ubus/ubus.sock")) {
202  amxc_var_add(cstring_t, uris, "ubus:/var/run/ubus/ubus.sock");
203  }
204  }
205  }
206  }
207 
208  if(amxb_be_get_info("usp") != NULL) {
209  amxp_dir_scan("/var/run/usp", "d_type == DT_SOCK", false, amxrt_connection_add_usp_socket, uris);
210  }
211 }
212 
213 static void amxrt_set_backend_setting(amxc_var_t* config,
214  const char* backend,
215  const char* setting,
216  const char* value) {
217  amxc_var_t* backend_settings = GET_ARG(config, backend);
218  amxc_var_t* backend_config_opt = NULL;
219  if(backend_settings == NULL) {
220  backend_settings = amxc_var_add_key(amxc_htable_t, config, backend, NULL);
221  }
222  backend_config_opt = GET_ARG(backend_settings, setting);
223  if(backend_config_opt == NULL) {
224  amxc_var_add_key(cstring_t, backend_settings, setting, value);
225  } else {
226  amxc_var_set(cstring_t, backend_config_opt, value);
227  }
228 }
229 
230 static void amxrt_move_backend_uris(amxc_var_t* config, amxc_var_t* backend_settings) {
231  amxc_var_t* uris = GET_ARG(config, AMXRT_COPT_URIS);
232  amxc_var_t* data_uris = GET_ARG(config, AMXRT_COPT_DATA_URIS);
233  amxc_var_t* be_uris = GET_ARG(backend_settings, AMXRT_COPT_URIS);
234  amxc_var_t* be_data_uris = GET_ARG(backend_settings, AMXRT_COPT_DATA_URIS);
235 
236  amxc_var_for_each(uri, be_uris) {
237  const char* u = GET_CHAR(uri, NULL);
238  if(!amxrt_list_contains(data_uris, u) &&
239  !amxrt_list_contains(uris, u)) {
240  amxc_var_set_index(uris, 0, uri, AMXC_VAR_FLAG_DEFAULT);
241  }
242  }
243 
244  amxc_var_for_each(uri, be_data_uris) {
245  const char* u = GET_CHAR(uri, NULL);
246  if(!amxrt_list_contains(data_uris, u) &&
247  !amxrt_list_contains(uris, u)) {
248  amxc_var_set_index(data_uris, 0, uri, AMXC_VAR_FLAG_DEFAULT);
249  }
250  }
251 
252  amxc_var_delete(&be_uris);
253  amxc_var_delete(&be_data_uris);
254 }
255 
257  amxc_var_t* uris = GET_ARG(config, AMXRT_COPT_URIS);
258  bool auto_detect = GET_BOOL(config, AMXRT_COPT_AUTO_DETECT);
259  amxc_array_t* backends = amxb_be_list();
260  size_t size = amxc_array_size(backends);
261 
262  // when auto-detect is on use the socket uris that:
263  // 1. are provided by the back-ends (these are the back-end default uris)
264  // 2. are known to libamxrt
265  // never add uris that are already defined in the config, make sure
266  // no double entries are added.
267 
268  if(auto_detect) {
269  // loop over all loaded backends and fetch uris and data-uris
270  // check if they are not already added to top-level uris or data-uris
271  // if not found in top level move them to top level otherwise delete them
272  for(size_t i = 0; i < size; i++) {
273  const char* be_name = (const char*) amxc_array_get_data_at(backends, i);
274  amxc_var_t* backend_settings = GET_ARG(config, be_name);
275  amxrt_move_backend_uris(config, backend_settings);
276  }
277  // last step - check known unix domain sockets - this for backwards compatibility
278  amxrt_add_known_uris(uris);
279  } else {
280  for(size_t i = 0; i < size; i++) {
281  const char* be_name = (const char*) amxc_array_get_data_at(backends, i);
282  amxc_var_t* backend_settings = GET_ARG(config, be_name);
283  amxc_var_t* be_uris = GET_ARG(backend_settings, AMXRT_COPT_URIS);
284  amxc_var_t* be_data_uris = GET_ARG(backend_settings, AMXRT_COPT_DATA_URIS);
285  amxc_var_delete(&be_uris);
286  amxc_var_delete(&be_data_uris);
287  }
288  }
289 
290  amxc_array_delete(&backends, NULL);
291 }
292 
294  int retval = 0;
295  const char* name = GET_CHAR(config, AMXRT_COPT_NAME);
296  amxc_array_t* backend_names = NULL;
297  size_t size = 0;
298  amxc_var_t* backends = amxc_var_get_key(config, AMXRT_COPT_BACKENDS, AMXC_VAR_FLAG_COPY);
299 
300  if(amxc_var_type_of(backends) == AMXC_VAR_ID_LIST) {
301  const amxc_llist_t* bel = amxc_var_constcast(amxc_llist_t, backends);
302  if(amxc_llist_is_empty(bel)) {
303  amxrt_print_error("No backends specified\n");
304  retval = 2;
305  }
306  } else {
307  char* str = amxc_var_dyncast(cstring_t, backends);
308  if((str == NULL) || (str[0] == 0)) {
309  amxrt_print_error("No backends specified\n");
310  retval = 2;
311  }
312  free(str);
313  }
314 
315  if(retval == 0) {
316  retval = amxb_be_load_multiple(backends);
317  if(retval != 0) {
318  amxrt_print_error("Failed to load backends\n");
319  retval = 2;
320  }
321  }
322 
323  /*
324  Create backend config section if not existing
325  For backwards compatibility set `register-name` if not existing
326  Needed for pcb backend if loaded
327  */
328  backend_names = amxb_be_list();
329  size = amxc_array_size(backend_names);
330  for(size_t i = 0; i < size; i++) {
331  const char* be_name = (const char*) amxc_array_get_data_at(backend_names, i);
332  amxrt_set_backend_setting(config, be_name, "register-name", name);
333  }
334  amxc_array_delete(&backend_names, NULL);
335 
336  amxb_set_config(config);
337 
338  amxc_var_delete(&backends);
339  return retval;
340 }
341 
342 int amxrt_connection_connect_all(amxo_parser_t* parser) {
343  int retval = 0;
344  const amxc_llist_t* uris = amxc_var_constcast(amxc_llist_t, GET_ARG(&parser->config, AMXRT_COPT_URIS));
345  const amxc_llist_t* data_uris = amxc_var_constcast(amxc_llist_t, GET_ARG(&parser->config, AMXRT_COPT_DATA_URIS));
346  bool connect = amxc_var_constcast(bool, GET_ARG(&parser->config, AMXRT_COPT_AUTO_CONNECT));
347  if(!connect) {
348  goto leave;
349  }
350 
351  amxrt_connection_filter_uris(uris, data_uris);
352  retval = amxrt_connection_connect_all_type(uris, AMXP_CONNECTION_BUS);
353  when_failed(retval, leave);
354  amxrt_connection_connect_all_type(data_uris, AMXP_CONNECTION_CUSTOM);
355 
356 leave:
357  return retval;
358 }
359 
360 int amxrt_connection_listen_all(amxo_parser_t* parser) {
361  int retval = 0;
362  const amxc_llist_t* listen = amxc_var_constcast(amxc_llist_t, GET_ARG(&parser->config, AMXRT_COPT_LISTEN));
363 
364  amxc_llist_for_each(it, listen) {
365  amxb_bus_ctx_t* bus_ctx = NULL;
366  int fd = -1;
367  const char* uri = amxc_var_constcast(cstring_t,
368  amxc_var_from_llist_it(it));
369 
370  retval = amxrt_connection_listen_bus_uri(uri, &bus_ctx);
371  if(retval != 0) {
372  amxrt_print_error("Failed to listen on %s", uri);
373  } else {
374  fd = amxb_get_fd(bus_ctx);
375  amxp_connection_add(fd, amxrt_connection_read, uri, AMXP_CONNECTION_LISTEN, bus_ctx);
376  }
377  }
378 
379  return retval;
380 }
381 
382 int amxrt_connection_register_dm(UNUSED amxo_parser_t* parser, amxd_dm_t* dm) {
383  int retval = 0;
384  amxc_llist_t* connections = amxp_connection_get_connections();
385  amxc_llist_for_each(it, connections) {
386  amxp_connection_t* con = amxc_llist_it_get_data(it, amxp_connection_t, it);
387  amxb_bus_ctx_t* bus_ctx = (amxb_bus_ctx_t*) con->priv;
388  if(con->type != AMXP_CONNECTION_BUS) {
389  continue;
390  }
391  retval = amxb_register(bus_ctx, dm);
392  if(retval != 0) {
393  amxrt_print_error("Failed to register data model on %s", con->uri);
394  goto leave;
395  }
396  }
397 
398  amxc_llist_for_each(it, amxp_connection_get_listeners()) {
399  amxp_connection_t* con = amxc_llist_it_get_data(it, amxp_connection_t, it);
400  amxb_bus_ctx_t* bus_ctx = (amxb_bus_ctx_t*) con->priv;
401  retval = amxb_register(bus_ctx, dm);
402  when_failed(retval, leave);
403  }
404 
405 leave:
406  return retval;
407 }
#define AMXRT_COPT_BACKENDS
Definition: amxrt.h:75
#define AMXRT_COPT_LISTEN
Definition: amxrt.h:89
#define AMXRT_COPT_URIS
Definition: amxrt.h:73
#define AMXRT_COPT_DATA_URIS
Definition: amxrt.h:74
#define AMXRT_COPT_NAME
Definition: amxrt.h:85
#define AMXRT_COPT_AUTO_DETECT
Definition: amxrt.h:76
#define AMXRT_COPT_AUTO_CONNECT
Definition: amxrt.h:77
static int amxrt_connection_listen_bus_uri(const char *uri, amxb_bus_ctx_t **bus_ctx)
int amxrt_connection_register_dm(UNUSED amxo_parser_t *parser, amxd_dm_t *dm)
static int amxrt_connection_add_usp_socket(const char *name, void *priv)
static void amxrt_connection_filter_uris(const amxc_llist_t *uris, const amxc_llist_t *exclude_uris)
void amxrt_connection_detect_sockets(amxc_var_t *config)
static void amxrt_move_backend_uris(amxc_var_t *config, amxc_var_t *backend_settings)
static void amxrt_connection_read(UNUSED int fd, UNUSED void *priv)
int amxrt_connection_listen_all(amxo_parser_t *parser)
static void amxrt_add_known_uris(amxc_var_t *uris)
static int amxrt_connection_connect_all_type(const amxc_llist_t *uris, uint32_t type)
int amxrt_connection_connect_all(amxo_parser_t *parser)
static void amxrt_set_backend_setting(amxc_var_t *config, const char *backend, const char *setting, const char *value)
int amxrt_connection_load_backends(amxc_var_t *config)
static bool amxrt_list_contains(amxc_var_t *list, const char *value)
PRIVATE void amxrt_print_message(const char *fmt,...)
PRIVATE void amxrt_print_error(const char *fmt,...)
PRIVATE amxp_connection_t * amxrt_el_get_connection(amxc_llist_t *cons, int fd)
int amxrt_el_stop(void)
Stops the event loop.
config
Definition: test.odl:54