blob: 1b3fd76bc13d21e73955cc893d0dbbf67bcbbaf0 [file]
/*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stddef.h>
#include <camkes/interface_registration.h>
#include <utils/util.h>
/* Could have used <utils/list.h> lists but that uses malloc by itself */
/*
* Two instances of linked lists structs, one nested inside the other.
* E.g.
*
* -------------------------- -------------------- --------------------
* | outer_interface_list_t | -----> | interface_list_t | -> | interface_list_t |
* -------------------------- -------------------- --------------------
* |
* V
* -------------------------- --------------------
* | outer_interface_list_t | -----> | interface_list_t | -> NULL
* -------------------------- --------------------
* |
* V
* NULL
*/
typedef struct interface_list interface_list_t;
struct interface_list {
void *interface_instance;
char **properties;
interface_list_t *next;
};
typedef struct outer_interface_list outer_interface_list_t;
struct outer_interface_list {
ps_interface_type_t interface_type;
interface_list_t *interface_list;
outer_interface_list_t *next;
};
typedef struct camkes_interface_cookie {
ps_malloc_ops_t *malloc_ops;
outer_interface_list_t *outer_interface_list;
} camkes_interface_cookie_t;
/* Only checks whether or not two interface list nodes are equal. 0 if they are
* equal (interface_instances match), 1 otherwise. */
static inline int interface_list_comparator(interface_list_t *a, interface_list_t *b)
{
assert(a && b);
if (a->interface_instance == b->interface_instance) {
return 0;
}
return 1;
}
SGLIB_DEFINE_LIST_PROTOTYPES(interface_list_t, interface_list_comparator, next);
SGLIB_DEFINE_LIST_FUNCTIONS(interface_list_t, interface_list_comparator, next);
static inline int outer_interface_list_comparator(outer_interface_list_t *a, outer_interface_list_t *b)
{
assert(a && b);
if (a->interface_type == b->interface_type) {
return 0;
}
return 1;
}
SGLIB_DEFINE_LIST_PROTOTYPES(outer_interface_list_t, outer_interface_list_comparator, next);
SGLIB_DEFINE_LIST_FUNCTIONS(outer_interface_list_t, outer_interface_list_comparator, next);
/* This is used to destroy any properties array that *we made a copy of* using copy_properties_array() */
static inline void destroy_properties_array(ps_malloc_ops_t *malloc_ops, char **properties)
{
assert(malloc_ops);
for (char *curr = *properties; curr != NULL; curr++) {
size_t property_len = strlen(curr);
ZF_LOGF_IF(ps_free(malloc_ops, property_len + 1, curr),
"Failed to cleanup properties from the properties array");
}
}
static inline char **copy_properties_array(ps_malloc_ops_t *malloc_ops, char **properties)
{
assert(malloc_ops && properties);
/* Find the amount of strings in the array */
size_t num_strings = 0;
for (char *curr = *properties; curr != NULL; curr++, num_strings++);
char **properties_copy = NULL;
int error = ps_calloc(malloc_ops, 1, sizeof(char *) * num_strings, (void **) &properties_copy);
if (error) {
return NULL;
}
/* Now go through the entire array and copy the strings */
for (int i = 0; i < num_strings; i++) {
size_t property_len = strlen(properties[i]);
error = ps_calloc(malloc_ops, 1, sizeof(char) * property_len + 1, (void **) &properties_copy[i]);
if (error) {
ZF_LOGE("Failed to allocate property %d of %zu properties", i, num_strings);
destroy_properties_array(malloc_ops, properties_copy);
return NULL;
}
strncpy(properties_copy[i], properties[i], property_len + 1);
}
return properties_copy;
}
int camkes_interface_register(void *cookie, ps_interface_type_t interface_type, void *interface_instance,
char **properties)
{
if (!interface_instance && interface_type != PS_NULL_INTERFACE) {
ZF_LOGE("Empty interface instance!");
return -EINVAL;
}
int error = 0;
int ret = 0;
camkes_interface_cookie_t *interface_cookie = cookie;
/* Check if there will be duplicate interface instances (same ptr) if we
* add this new instance in */
outer_interface_list_t dummy_outer_node = { .interface_type = interface_type };
outer_interface_list_t *outer_member = sglib_outer_interface_list_t_find_member(interface_cookie->outer_interface_list,
&dummy_outer_node);
bool allocated_outer_member = false;
if (outer_member) {
interface_list_t dummy_inner_node = { .interface_instance = interface_instance };
interface_list_t *inner_member = sglib_interface_list_t_find_member(outer_member->interface_list,
&dummy_inner_node);
if (!inner_member) {
/* Found a duplicate */
ZF_LOGE("Trying to add the same interface instance into same interface type!");
return -EINVAL;
}
} else {
/* We now need to create a new outer list node for this interface type */
error = ps_calloc(interface_cookie->malloc_ops, 1, sizeof(*outer_member), (void **) &outer_member);
if (error) {
ZF_LOGE("Failed to allocate memory for a new node for the interface type");
return -ENOMEM;
}
/* Don't add it the of the outer interface list yet, as clean-up becomes problematic */
outer_member->interface_type = interface_type;
allocated_outer_member = true;
}
interface_list_t *new_node = NULL;
error = ps_calloc(interface_cookie->malloc_ops, 1, sizeof(*new_node), (void **) &new_node);
if (error) {
ZF_LOGE("Failed to allocate memory for a new node for the interface instance");
ret = -ENOMEM;
goto fail;
}
char **properties_copy = NULL;
if (properties) {
char **properties_copy = copy_properties_array(interface_cookie->malloc_ops, properties);
if (!properties_copy) {
ret = -ENOMEM;
goto fail;
}
}
new_node->interface_instance = interface_instance;
new_node->properties = properties_copy;
sglib_interface_list_t_add(&outer_member->interface_list, new_node);
if (allocated_outer_member) {
/* Now we join this outer node into the outer list */
sglib_outer_interface_list_t_add(&interface_cookie->outer_interface_list, outer_member);
}
return 0;
fail:
if (allocated_outer_member) {
ZF_LOGF_IF(ps_free(interface_cookie->malloc_ops, sizeof(*outer_member), outer_member),
"Failed to cleanup after a failed interface register operation");
}
if (new_node) {
ZF_LOGF_IF(ps_free(interface_cookie->malloc_ops, sizeof(*new_node), new_node),
"Failed to cleanup after a failed interface register operation");
}
return ret;
}
int camkes_interface_unregister(void *cookie, ps_interface_type_t interface_type, void *interface_instance)
{
if (!interface_instance && interface_type != PS_NULL_INTERFACE) {
ZF_LOGE("Empty interface instance!");
return -EINVAL;
}
camkes_interface_cookie_t *interface_cookie = cookie;
/* Grab the outer node for this interface type */
outer_interface_list_t dummy_outer_node = { .interface_type = interface_type };
outer_interface_list_t *outer_member = sglib_outer_interface_list_t_find_member(interface_cookie->outer_interface_list,
&dummy_outer_node);
if (!outer_member) {
/* No outer node for this interface type, so that means that no
* interface was ever registered for this type. Just ignore. */
return 0;
}
interface_list_t dummy_node = { .interface_instance = interface_instance };
interface_list_t *target_node = NULL;
int deleted = sglib_interface_list_t_delete_if_member(&outer_member->interface_list, &dummy_node,
&target_node);
if (deleted) {
/* Only free memory if we've actually deleted something from the list,
* also carry on as if nothing went wrong if we didn't delete aything
* */
if (target_node->properties) {
destroy_properties_array(interface_cookie->malloc_ops, target_node->properties);
}
ZF_LOGF_IF(ps_free(interface_cookie->malloc_ops, sizeof(*target_node), target_node),
"Failed to free memory after unregistering an interface");
}
return 0;
}
int camkes_interface_find(void *cookie, ps_interface_type_t interface_type, ps_interface_search_handler_fn_t handler,
void *handler_data)
{
if (!handler) {
ZF_LOGE("No handler provided!");
return -EINVAL;
}
camkes_interface_cookie_t *interface_cookie = cookie;
outer_interface_list_t dummy_node = { .interface_type = interface_type };
outer_interface_list_t *outer_member = sglib_outer_interface_list_t_find_member(interface_cookie->outer_interface_list,
&dummy_node);
if (!outer_member) {
/* A list for this interface type does not exist yet and hence no entry
* that we can look at */
return -ENOENT;
}
int handler_ret = 0;
for (interface_list_t *curr = outer_member->interface_list; curr != NULL; curr = curr->next) {
handler_ret = handler(handler_data, curr->interface_instance, curr->properties);
if (handler_ret < 0) {
/* Error in the handler */
return handler_ret;
} else if (handler_ret == PS_INTERFACE_FOUND_MATCH) {
/* Handler found a match */
return 0;
}
}
/* We've found no match whatsoever, so we return an error */
return -ENOENT;
}
int camkes_interface_registration_ops(ps_interface_registration_ops_t *interface_ops, ps_malloc_ops_t *malloc_ops)
{
if (!interface_ops || !malloc_ops) {
ZF_LOGE("Arguments are NULL!");
return -EINVAL;
}
camkes_interface_cookie_t *cookie = NULL;
int error = 0;
error = ps_calloc(malloc_ops, 1, sizeof(camkes_interface_cookie_t), (void **) &cookie);
if (error) {
return -ENOMEM;
}
cookie->malloc_ops = malloc_ops;
interface_ops->cookie = cookie;
interface_ops->interface_register_fn = camkes_interface_register;
interface_ops->interface_unregister_fn = camkes_interface_unregister;
interface_ops->interface_find_fn = camkes_interface_find;
return 0;
}