libamxo  4.3.4
Object Definition Language (ODL) parsing
amxo_import_resolver.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 "amxo_parser_priv.h"
60 
61 #define GET_OPTION(parser, name) \
62  amxo_parser_get_config(parser, name)
63 
64 typedef struct _amxo_import_lib {
65  amxc_htable_it_t hit;
66  uint32_t references;
67  void* handle;
69 
70 typedef union _fn_caster {
71  void* fn;
74 
75 static amxc_htable_t import_libs;
76 static bool dbg = false;
77 
78 static void amxo_import_lib_free(UNUSED const char* key,
79  amxc_htable_it_t* it) {
80  amxo_import_lib_t* import =
81  amxc_htable_it_get_data(it, amxo_import_lib_t, hit);
82  char* no_dlclose = getenv("AMXO_NO_DLCLOSE");
83 
84  if(no_dlclose == NULL) {
85  dlclose(import->handle);
86  }
87  free(import);
88 }
89 
91  UNUSED void* priv) {
92  amxc_var_t* config = amxo_parser_claim_config(parser, "import-dirs");
93 
94  amxc_var_set_type(config, AMXC_VAR_ID_LIST);
95  amxc_var_add(cstring_t, config, ".");
96 
97  config = amxo_parser_claim_config(parser, "import-dbg");
98  amxc_var_set_type(config, AMXC_VAR_ID_BOOL);
99  amxc_var_set(bool, config, false);
100 
101  return;
102 }
103 
105  const char* fn_name,
106  bool prefix) {
107  amxc_string_t symbol;
108  amxc_string_init(&symbol, 0);
109 
110  if(parser->param != NULL) {
111  const char* param_name = amxd_param_get_name(parser->param);
112  if(prefix) {
113  amxc_string_setf(&symbol, "_%s_%s", param_name, fn_name);
114  } else {
115  amxc_string_setf(&symbol, "_%s", fn_name);
116  }
117  } else {
118  const char* obj_name = amxd_object_get_name(parser->object,
119  AMXD_OBJECT_NAMED);
120  if(obj_name != NULL) {
121  if(prefix) {
122  amxc_string_setf(&symbol, "_%s_%s", obj_name, fn_name);
123  } else {
124  amxc_string_setf(&symbol, "_%s", fn_name);
125  }
126  } else {
127  amxc_string_setf(&symbol, "_%s", fn_name);
128  }
129  }
130 
131  return amxc_string_take_buffer(&symbol);
132 }
133 
135  const char* fn_name,
136  const char* symbol,
137  const char* lib_name,
138  amxo_import_lib_t* lib,
139  amxc_string_t* msg) {
140  char* dl_error = NULL;
141  fn_caster_t helper;
142  bool silent = amxc_var_constcast(bool, GET_OPTION(parser, "silent"));
143  dbg = amxc_var_constcast(bool, GET_OPTION(parser, "import-dbg"));
144 
145  helper.fn = NULL;
146  dlerror();
147  helper.fn = dlsym(lib->handle, symbol);
148  dl_error = dlerror();
149  if(dl_error == NULL) {
150  if(dbg && !silent) {
151  fprintf(stderr,
152  "[IMPORT-DBG] - symbol %s resolved (for %s) from %s\n",
153  symbol,
154  fn_name,
155  lib_name);
156  }
157  lib->references++;
158  } else {
159  if(dbg && !silent) {
160  amxc_string_appendf(msg,
161  "[IMPORT-DBG] - resolving symbol %s (for %s) from %s failed - [%s]\n",
162  symbol,
163  fn_name,
164  lib_name,
165  dl_error);
166  }
167  }
168  return helper.amxo_fn;
169 }
170 
171 static void amxo_resolver_import_parse_data(const char* data,
172  char** lib,
173  char** symbol) {
174  amxc_string_t str_data;
175  amxc_llist_t parts;
176  size_t length = 0;
177  amxc_llist_it_t* it = NULL;
178 
179  amxc_llist_init(&parts);
180  amxc_string_init(&str_data, 0);
181  amxc_string_push_buffer(&str_data, (char*) data, strlen(data) + 1);
182  amxc_string_split_to_llist(&str_data, &parts, ':');
183  length = amxc_llist_size(&parts);
184 
185  when_true(length > 2, exit);
186 
187  it = amxc_llist_get_first(&parts);
188  amxc_string_trim(amxc_string_from_llist_it(it), NULL);
189  *lib = amxc_string_take_buffer(amxc_string_from_llist_it(it));
190 
191  if(length > 1) {
192  it = amxc_llist_get_last(&parts);
193  amxc_string_trim(amxc_string_from_llist_it(it), NULL);
194  *symbol = amxc_string_take_buffer(amxc_string_from_llist_it(it));
195  }
196 
197 exit:
198  amxc_llist_clean(&parts, amxc_string_list_it_free);
199  return;
200 }
201 
203  amxc_htable_t* import_data,
204  const char* fn_name,
205  const char* data) {
206  amxo_fn_ptr_t fn = NULL;
207  char* lib = NULL;
208  char* symbol = NULL;
209  amxc_string_t res_name;
210  amxc_htable_it_t* it = NULL;
211  amxo_import_lib_t* import = NULL;
212  amxc_string_t msg;
213  amxc_string_init(&msg, 0);
214  amxc_string_init(&res_name, 0);
215  amxo_resolver_import_parse_data(data, &lib, &symbol);
216 
217  if(amxc_string_set_resolved(&res_name, lib, &parser->config) > 0) {
218  free(lib);
219  lib = amxc_string_take_buffer(&res_name);
220  }
221 
222  it = amxc_htable_get(import_data, lib);
223  import = amxc_htable_it_get_data(it, amxo_import_lib_t, hit);
224  if(import == NULL) {
225  amxo_parser_msg(parser, "No import library found with name \"%s\"", lib);
226  parser->status = amxd_status_file_not_found;
227  goto exit;
228  }
229 
230  if(symbol != NULL) {
231  fn = amxo_resolver_try(parser, fn_name, symbol, lib, import, &msg);
232  } else {
233  symbol = amxo_resolver_import_get_symbol(parser, fn_name, true);
234  fn = amxo_resolver_try(parser, fn_name, symbol, lib, import, &msg);
235  when_true(fn != NULL, exit);
236  free(symbol);
237  symbol = amxo_resolver_import_get_symbol(parser, fn_name, false);
238  fn = amxo_resolver_try(parser, fn_name, symbol, lib, import, &msg);
239  }
240 
241  if((fn == NULL) && !amxc_string_is_empty(&msg)) {
242  fprintf(stderr, "%s", amxc_string_get(&msg, 0));
243  }
244 
245 exit:
246  amxc_string_clean(&res_name);
247  amxc_string_clean(&msg);
248  free(lib);
249  free(symbol);
250 
251  return fn;
252 }
253 
255  const char* fn_name,
256  UNUSED amxo_fn_type_t type,
257  const char* data,
258  UNUSED void* priv) {
259  amxc_htable_t* import_data = amxo_parser_get_resolver_data(parser, "import");
260  amxo_fn_ptr_t fn = NULL;
261  amxc_string_t msg;
262 
263  amxc_string_init(&msg, 0);
264 
265  if((data != NULL) && (data[0] != 0)) {
266  fn = amxo_resolver_import_data(parser, import_data, fn_name, data);
267  } else {
268  amxc_htable_for_each(it, import_data) {
269  const char* lib_name = amxc_htable_it_get_key(it);
270  amxo_import_lib_t* import =
271  amxc_htable_it_get_data(it, amxo_import_lib_t, hit);
272  char* symbol = amxo_resolver_import_get_symbol(parser, fn_name, true);
273  fn = amxo_resolver_try(parser, fn_name, symbol, lib_name, import, &msg);
274  free(symbol);
275  if(fn == NULL) {
276  symbol = amxo_resolver_import_get_symbol(parser, fn_name, false);
277  fn = amxo_resolver_try(parser, fn_name, symbol, lib_name, import, &msg);
278  free(symbol);
279  }
280  if(fn != NULL) {
281  break;
282  }
283  }
284  }
285 
286  if((fn == NULL) && !amxc_string_is_empty(&msg)) {
287  fprintf(stderr, "%s", amxc_string_get(&msg, 0));
288  }
289 
290  amxc_string_clean(&msg);
291  return fn;
292 }
293 
294 static bool amxo_resolver_import_alias_exists(amxc_htable_t* import_data,
295  const char* alias) {
296  bool retval = true;
297  amxc_htable_it_t* it = NULL;
298 
299  it = amxc_htable_get(&import_libs, alias);
300  if(it != NULL) {
301  amxc_htable_insert(import_data, alias, it);
302  goto exit;
303  }
304  it = amxc_htable_get(import_data, alias);
305  if(it != NULL) {
306  goto exit;
307  }
308 
309  retval = false;
310 
311 exit:
312  return retval;
313 }
314 
315 static bool amxo_parser_no_import(amxo_parser_t* parser) {
316  amxc_var_t* var_import = GET_OPTION(parser, "odl-import");
317  bool import = true;
318 
319  if(var_import != NULL) {
320  import = amxc_var_dyncast(bool, var_import);
321  }
322 
323  return !import;
324 }
325 
327  const char* so_name,
328  const char* full_path,
329  int flags) {
330  void* handle = NULL;
331  bool silent = amxc_var_constcast(bool, GET_OPTION(parser, "silent"));
332  dbg = amxc_var_constcast(bool, GET_OPTION(parser, "import-dbg"));
333 
334  if((flags & (RTLD_LAZY | RTLD_NOW)) == 0) {
335  flags |= RTLD_LAZY;
336  }
337  dlerror();
338  handle = dlopen(full_path, flags);
339  if(handle == NULL) {
340  char* error = dlerror();
341  if(dbg && !silent) {
342  fprintf(stderr, "[IMPORT-DBG] - failed to load %s - %s\n", so_name, error);
343  }
344  amxc_string_setf(&parser->msg, "Failed to load lib %s", error);
345  } else {
346  if(dbg && !silent) {
347  fprintf(stderr, "[IMPORT-DBG] - dlopen - %s (%p)\n", so_name, handle);
348  }
349  }
350 
351  return handle;
352 }
353 
355  const char* so_name,
356  const char* alias,
357  int flags) {
358  int retval = -1;
359  void* handle = NULL;
360  amxo_import_lib_t* import_lib = NULL;
361  char* full_path = NULL;
362  bool silent = amxc_var_constcast(bool, GET_OPTION(parser, "silent"));
363  const amxc_llist_t* impdirs =
364  amxc_var_constcast(amxc_llist_t, GET_OPTION(parser, "import-dirs"));
365  amxc_htable_t* import_data = amxo_parser_claim_resolver_data(parser, "import");
366  amxc_string_t res_so_name;
367  amxc_string_t res_alias;
368  amxc_string_init(&res_so_name, 0);
369  amxc_string_init(&res_alias, 0);
370 
371  dbg = amxc_var_constcast(bool, GET_OPTION(parser, "import-dbg"));
372 
373  when_null(parser, exit);
374  parser->status = amxd_status_invalid_arg;
375  when_str_empty(so_name, exit);
376  when_true(alias != NULL && alias[0] == 0, exit);
377 
378  parser->status = amxd_status_ok;
379  when_true_status(amxo_parser_no_import(parser), exit, retval = 0);
380 
381  if(amxc_string_set_resolved(&res_alias, alias, &parser->config) > 0) {
382  alias = amxc_string_get(&res_alias, 0);
383  }
384  if(amxc_string_set_resolved(&res_so_name, so_name, &parser->config) > 0) {
385  so_name = amxc_string_get(&res_so_name, 0);
386  }
387 
388  when_true_status(amxo_resolver_import_alias_exists(import_data, alias),
389  exit,
390  retval = 0);
391 
392  if(!amxo_parser_find(parser, impdirs, so_name, &full_path)) {
393  if(dbg && !silent) {
394  fprintf(stderr, "[IMPORT-DBG] - file not found %s\n", so_name);
395  }
396  parser->status = amxd_status_file_not_found;
397  amxo_parser_msg(parser, "Import file not found !!! \"%s\"", so_name);
398  goto exit;
399  }
400 
401  handle = amxo_resolver_import_lib(parser, so_name, full_path, (flags & ~RTLD_NODELETE));
402  when_null(handle, exit);
403 
404  import_lib = (amxo_import_lib_t*) calloc(1, sizeof(amxo_import_lib_t));
405  when_true_status(import_lib == NULL, exit, parser->status = amxd_status_out_of_mem);
406 
407  import_lib->handle = handle;
408  if((flags & RTLD_NODELETE) == RTLD_NODELETE) {
409  import_lib->references++;
410  }
411  amxc_htable_insert(import_data, alias, &import_lib->hit);
412  retval = 0;
413 
414 exit:
415  amxc_string_clean(&res_alias);
416  amxc_string_clean(&res_so_name);
417  free(full_path);
418  if((retval != 0) && (handle != NULL)) {
419  dlclose(handle);
420  }
421  return retval;
422 }
423 
425  UNUSED void* priv) {
426  amxc_htable_t* import_data = NULL;
427  amxc_htable_it_t* it = NULL;
428  bool silent = amxc_var_constcast(bool, GET_OPTION(parser, "silent"));
429  dbg = amxc_var_constcast(bool, GET_OPTION(parser, "import-dbg"));
430  import_data = amxo_parser_get_resolver_data(parser, "import");
431 
432  it = amxc_htable_take_first(import_data);
433  while(it) {
434  amxo_import_lib_t* import =
435  amxc_htable_it_get_data(it, amxo_import_lib_t, hit);
436  const char* key = amxc_htable_it_get_key(it);
437  if(dbg && !silent) {
438  fprintf(stderr, "[IMPORT-DBG] - symbols used of %s = %d\n", key, import->references);
439  }
440  if(import->references != 0) {
441  amxc_htable_insert(&import_libs, key, it);
442  } else {
443  amxc_htable_it_clean(it, amxo_import_lib_free);
444  }
445  it = amxc_htable_take_first(import_data);
446  }
447  amxc_htable_clean(import_data, amxo_import_lib_free);
448  amxo_parser_remove_resolver_data(parser, "import");
449 }
450 
452  amxc_htable_clean(&import_libs, amxo_import_lib_free);
453  amxc_htable_init(&import_libs, 10);
454 }
455 
456 static amxo_resolver_t import = {
457  .hit = { .ait = NULL, .key = NULL, .next = NULL },
459  .resolve = amxo_resolver_import,
461  .priv = NULL
462 };
463 
464 CONSTRUCTOR_LVL(110) static void amxo_import_init(void) {
465  amxc_htable_init(&import_libs, 10);
466  amxo_register_resolver("import", &import);
467 }
468 
469 DESTRUCTOR_LVL(110) static void amxo_import_cleanup(void) {
470  amxc_htable_clean(&import_libs, amxo_import_lib_free);
471  amxo_unregister_resolver("import");
472 }
static bool dbg
static bool amxo_parser_no_import(amxo_parser_t *parser)
static amxo_fn_ptr_t amxo_resolver_import(amxo_parser_t *parser, const char *fn_name, UNUSED amxo_fn_type_t type, const char *data, UNUSED void *priv)
static amxc_htable_t import_libs
union _fn_caster fn_caster_t
static amxo_fn_ptr_t amxo_resolver_import_data(amxo_parser_t *parser, amxc_htable_t *import_data, const char *fn_name, const char *data)
static void amxo_resolver_import_parse_data(const char *data, char **lib, char **symbol)
static amxo_resolver_t import
static void * amxo_resolver_import_lib(amxo_parser_t *parser, const char *so_name, const char *full_path, int flags)
static amxo_fn_ptr_t amxo_resolver_try(amxo_parser_t *parser, const char *fn_name, const char *symbol, const char *lib_name, amxo_import_lib_t *lib, amxc_string_t *msg)
static void amxo_resolver_import_defaults(amxo_parser_t *parser, UNUSED void *priv)
struct _amxo_import_lib amxo_import_lib_t
static void amxo_import_lib_free(UNUSED const char *key, amxc_htable_it_t *it)
#define GET_OPTION(parser, name)
static bool amxo_resolver_import_alias_exists(amxc_htable_t *import_data, const char *alias)
CONSTRUCTOR_LVL(110)
DESTRUCTOR_LVL(110)
void amxo_resolver_import_clean(amxo_parser_t *parser, UNUSED void *priv)
static char * amxo_resolver_import_get_symbol(amxo_parser_t *parser, const char *fn_name, bool prefix)
PRIVATE void PRIVATE int PRIVATE bool amxo_parser_find(amxo_parser_t *parser, const amxc_llist_t *dirs, const char *file_path, char **full_path)
PRIVATE void amxo_parser_msg(amxo_parser_t *parser, const char *format,...) __attribute__((format(printf
void(* amxo_fn_ptr_t)(void)
Definition: amxo_types.h:71
enum _amxo_fn_type amxo_fn_type_t
config
amxc_var_t * amxo_parser_claim_config(amxo_parser_t *parser, const char *path)
Gets or creates a configuration option.
int amxo_resolver_import_open(amxo_parser_t *parser, const char *so_name, const char *alias, int flags)
Opens a shared object file (.so file)
void amxo_resolver_import_close_all(void)
Unloads all loaded shared objects.
int amxo_unregister_resolver(const char *name)
Unregisters a function resolver.
amxc_htable_t * amxo_parser_get_resolver_data(amxo_parser_t *parser, const char *resolver_name)
Gets the resolver specific parser data.
amxc_htable_t * amxo_parser_claim_resolver_data(amxo_parser_t *parser, const char *resolver_name)
Fetches resolver specific data for a parser instance.
int amxo_register_resolver(const char *name, amxo_resolver_t *resolver)
Registers a function resolver.
void amxo_parser_remove_resolver_data(amxo_parser_t *parser, const char *resolver_name)
Removes the resolver specific parser data.
amxc_htable_it_t hit
The ODL parser structure.
Definition: amxo_types.h:245
amxc_string_t msg
Definition: amxo_types.h:259
amxd_param_t * param
Definition: amxo_types.h:263
amxc_var_t config
Definition: amxo_types.h:250
amxd_status_t status
Definition: amxo_types.h:258
amxd_object_t * object
Definition: amxo_types.h:262
amxc_htable_it_t hit
Definition: amxo_types.h:101
#define UNUSED
Definition: test_issue_48.c:84
amxo_fn_ptr_t amxo_fn