X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=blobdiff_plain;f=src%2Fafb-apiset.c;h=b4a57ee64c8278025d832b88cc7d070ec9ad5ca0;hp=38edcd588ab07b03919ef4cd282b4710ea46ee67;hb=65353dce81a629e042800bb7b86fcd869a76727e;hpb=8f3368daeca3d5c184321e96cba60886bb7fc82f diff --git a/src/afb-apiset.c b/src/afb-apiset.c index 38edcd58..b4a57ee6 100644 --- a/src/afb-apiset.c +++ b/src/afb-apiset.c @@ -1,6 +1,5 @@ /* - * Copyright (C) 2016, 2017 "IoT.bzh" - * Author "Fulup Ar Foll" + * Copyright (C) 2015-2020 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,8 +21,8 @@ #include #include #include +#include -#include "afb-session.h" #include "verbose.h" #include "afb-api.h" #include "afb-apiset.h" @@ -31,16 +30,74 @@ #include "afb-xreq.h" #include "jobs.h" -#define INCR 8 /* CAUTION: must be a power of 2 */ +#define INCR 8 /* CAUTION: must be a power of 2 */ +#define NOT_STARTED -1 + +struct afb_apiset; +struct api_desc; +struct api_class; +struct api_alias; +struct api_depend; + +/** + * array of items + */ +struct api_array { + int count; /* count of items */ + union { + void **anys; + struct api_desc **apis; + struct api_class **classes; + struct api_alias **aliases; + struct api_depend **depends; + }; +}; /** * Internal description of an api */ struct api_desc { - int status; - const char *name; /**< name of the api */ - struct afb_api api; /**< handler of the api */ + struct api_desc *next; + const char *name; /**< name of the api */ + int status; /**< initialisation status: + - NOT_STARTED not started, + - 0 started without error, + - greater than 0, error number of start */ + struct afb_api_item api; /**< handler of the api */ + struct { + struct api_array classes; + struct api_array apis; + } require; +}; + +/** + * internal description of aliases + */ +struct api_alias +{ + struct api_alias *next; + struct api_desc *api; + char name[]; +}; + +/** + * + */ +struct api_class +{ + struct api_class *next; + struct api_array providers; + char name[]; +}; + +/** + * + */ +struct api_depend +{ + struct afb_apiset *set; + char name[]; }; /** @@ -48,14 +105,132 @@ struct api_desc */ struct afb_apiset { - struct api_desc *apis; /**< description of apis */ + struct api_array apis; /**< the apis */ + struct api_alias *aliases; /**< the aliases */ struct afb_apiset *subset; /**< subset if any */ - int count; /**< count of apis in the set */ + struct { + int (*callback)(void*, struct afb_apiset*, const char*); /* not found handler */ + void *closure; + void (*cleanup)(void*); + } onlack; /** not found handler */ int timeout; /**< the timeout in second for the apiset */ int refcount; /**< reference count for freeing resources */ - char name[1]; /**< name of the apiset */ + char name[]; /**< name of the apiset */ }; +/** + * global apis + */ +static struct api_desc *all_apis; + +/** + * global classes + */ +static struct api_class *all_classes; + +/** + * Ensure enough room in 'array' for 'count' items + */ +static int api_array_ensure_count(struct api_array *array, int count) +{ + int c; + void **anys; + + c = (count + INCR - 1) & ~(INCR - 1); + anys = realloc(array->anys, c * sizeof *anys); + if (!anys) { + errno = ENOMEM; + return -1; + } + + array->count = count; + array->anys = anys; + return 0; +} + +/** + * Insert in 'array' the item 'any' at the 'index' + */ +static int api_array_insert(struct api_array *array, void *any, int index) +{ + int n = array->count; + + if (api_array_ensure_count(array, n + 1) < 0) + return -1; + + while (index < n) { + array->anys[n] = array->anys[n - 1]; + n--; + } + + array->anys[index] = any; + return 0; +} + +/** + * Add the item 'any' to the 'array' + */ +static int api_array_add(struct api_array *array, void *any) +{ + int i, n = array->count; + + for (i = 0 ; i < n ; i++) { + if (array->anys[i] == any) + return 0; + } + + if (api_array_ensure_count(array, n + 1) < 0) + return -1; + + array->anys[n] = any; + return 0; +} + +/** + * Delete the 'api' from the 'array' + * Returns 1 if delete or 0 if not found + */ +static int api_array_del(struct api_array *array, void *any) +{ + int i = array->count; + while (i) { + if (array->anys[--i] == any) { + array->anys[i] = array->anys[--array->count]; + return 1; + } + } + return 0; +} + +/** + * Search the class of 'name' and return it. + * In case where the class of 'namle' isn't found, it returns + * NULL when 'create' is null or a fresh created instance if 'create' isn't + * zero (but NULL on allocation failure). + */ +static struct api_class *class_search(const char *name, int create) +{ + struct api_class *c; + + for (c= all_classes ; c ; c = c->next) { + if (!strcasecmp(name, c->name)) + return c; + } + + if (!create) + return NULL; + + c = calloc(1, strlen(name) + 1 + sizeof *c); + if (!c) + errno = ENOMEM; + else { + strcpy(c->name, name); + c->next = all_classes; + all_classes = c; + } + return c; +} + /** * Search the api of 'name'. * @param set the api set @@ -66,20 +241,16 @@ static struct api_desc *search(struct afb_apiset *set, const char *name) { int i, c, up, lo; struct api_desc *a; + struct api_alias *aliases; /* dichotomic search of the api */ /* initial slice */ lo = 0; - up = set->count; - for (;;) { - /* check remaining slice */ - if (lo >= up) { - /* not found */ - return NULL; - } + up = set->apis.count; + while (lo < up) { /* check the mid of the slice */ i = (lo + up) >> 1; - a = &set->apis[i]; + a = set->apis.apis[i]; c = strcasecmp(a->name, name); if (c == 0) { /* found */ @@ -91,6 +262,37 @@ static struct api_desc *search(struct afb_apiset *set, const char *name) else up = i; } + + /* linear search of aliases */ + aliases = set->aliases; + for(;;) { + if (!aliases) + break; + c = strcasecmp(aliases->name, name); + if (!c) + return aliases->api; + if (c > 0) + break; + aliases = aliases->next; + } + return NULL; +} + +/** + * Search the api of 'name' in the apiset and in its subsets. + * @param set the api set + * @param name the api name to search + * @return the descriptor if found or NULL otherwise + */ +static struct api_desc *searchrec(struct afb_apiset *set, const char *name) +{ + struct api_desc *result; + + do { + result = search(set, name); + } while (result == NULL && (set = set->subset) != NULL); + + return result; } /** @@ -112,9 +314,24 @@ struct afb_apiset *afb_apiset_addref(struct afb_apiset *set) */ void afb_apiset_unref(struct afb_apiset *set) { + struct api_alias *a; + struct api_desc *d; + if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) { afb_apiset_unref(set->subset); - free(set->apis); + if (set->onlack.cleanup) + set->onlack.cleanup(set->onlack.closure); + while((a = set->aliases)) { + set->aliases = a->next; + free(a); + } + while (set->apis.count) { + d = set->apis.apis[--set->apis.count]; + if (d->api.itf->unref) + d->api.itf->unref(d->api.closure); + free(d); + } + free(set->apis.apis); free(set); } } @@ -129,21 +346,48 @@ struct afb_apiset *afb_apiset_create(const char *name, int timeout) { struct afb_apiset *set; - set = malloc((name ? strlen(name) : 0) + sizeof *set); + set = calloc(1, (name ? strlen(name) : 0) + 1 + sizeof *set); if (set) { - set->apis = malloc(INCR * sizeof *set->apis); - set->count = 0; set->timeout = timeout; set->refcount = 1; - set->subset = NULL; if (name) strcpy(set->name, name); - else - set->name[0] = 0; } return set; } +/** + * Create an apiset being the last subset of 'set' + * @param set the set to extend with the created subset (can be NULL) + * @param name the name of the created apiset (can be NULL) + * @param timeout the default timeout in seconds for the created apiset + * @return the created apiset or NULL in case of error + */ +struct afb_apiset *afb_apiset_create_subset_last(struct afb_apiset *set, const char *name, int timeout) +{ + if (set) + while (set->subset) + set = set->subset; + return afb_apiset_create_subset_first(set, name, timeout); +} + +/** + * Create an apiset being the first subset of 'set' + * @param set the set to extend with the created subset (can be NULL) + * @param name the name of the created apiset (can be NULL) + * @param timeout the default timeout in seconds for the created apiset + * @return the created apiset or NULL in case of error + */ +struct afb_apiset *afb_apiset_create_subset_first(struct afb_apiset *set, const char *name, int timeout) +{ + struct afb_apiset *result = afb_apiset_create(name, timeout); + if (result && set) { + result->subset = set->subset; + set->subset = result; + } + return result; +} + /** * the name of the apiset * @param set the api set @@ -188,17 +432,32 @@ struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set) * Set the subset of the set * @param set the api set * @param subset the subset to set + * + * @return 0 in case of success or -1 if it had created a loop */ -void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset) +int afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset) { struct afb_apiset *tmp; - if (subset == set) { - /* avoid infinite loop */ - subset = NULL; - } + + /* avoid infinite loop */ + for (tmp = subset ; tmp ; tmp = tmp->subset) + if (tmp == set) + return -1; + tmp = set->subset; set->subset = afb_apiset_addref(subset); afb_apiset_unref(tmp); + + return 0; +} + +void afb_apiset_onlack_set(struct afb_apiset *set, int (*callback)(void*, struct afb_apiset*, const char*), void *closure, void (*cleanup)(void*)) +{ + if (set->onlack.cleanup) + set->onlack.cleanup(set->onlack.closure); + set->onlack.callback = callback; + set->onlack.closure = closure; + set->onlack.cleanup = cleanup; } /** @@ -211,52 +470,118 @@ void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset) * - EEXIST if name already registered * - ENOMEM when out of memory */ -int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api) +int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item api) { - struct api_desc *apis; + struct api_desc *desc; int i, c; - /* check previously existing plugin */ - for (i = 0 ; i < set->count ; i++) { - c = strcasecmp(set->apis[i].name, name); - if (c == 0) { - ERROR("api of name %s already exists", name); - errno = EEXIST; - goto error; - } + /* check whether it exists already */ + if (search(set, name)) { + ERROR("api of name %s already exists", name); + errno = EEXIST; + goto error; + } + + /* search insertion place */ + for (i = 0 ; i < set->apis.count ; i++) { + c = strcasecmp(set->apis.apis[i]->name, name); if (c > 0) break; } - /* allocates enough memory */ - c = (set->count + INCR) & ~(INCR - 1); - apis = realloc(set->apis, ((unsigned)c) * sizeof * apis); - if (apis == NULL) { - ERROR("out of memory"); - errno = ENOMEM; + /* allocates memory */ + desc = calloc(1, sizeof *desc); + if (!desc) + goto oom; + + desc->status = NOT_STARTED; + desc->api = api; + desc->name = name; + + if (api_array_insert(&set->apis, desc, i) < 0) { + free(desc); goto error; } - set->apis = apis; - - /* copy higher part of the array */ - apis += i; - if (i != set->count) - memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis); - /* record the plugin */ - apis->status = -1; - apis->api = api; - apis->name = name; - set->count++; + desc->next = all_apis; + all_apis = desc; - INFO("API %s added", name); + if (afb_api_is_public(name)) + INFO("API %s added", name); return 0; +oom: + ERROR("out of memory"); + errno = ENOMEM; error: return -1; } +/** + * Adds a the 'alias' name to the api of 'name'. + * @params set the api set + * @param name the name of the api to alias + * @param alias the aliased name to add to the api of name + * @returns 0 in case of success or -1 in case + * of error with errno set: + * - ENOENT if the api doesn't exist + * - EEXIST if name (of alias) already registered + * - ENOMEM when out of memory + */ +int afb_apiset_add_alias(struct afb_apiset *set, const char *name, const char *alias) +{ + struct api_desc *api; + struct api_alias *ali, **pali; + + /* check alias doesn't already exist */ + if (search(set, alias)) { + ERROR("api of name %s already exists", alias); + errno = EEXIST; + goto error; + } + + /* check aliased api exists */ + api = search(set, name); + if (api == NULL) { + ERROR("api of name %s doesn't exists", name); + errno = ENOENT; + goto error; + } + + /* allocates and init the struct */ + ali = malloc(sizeof *ali + strlen(alias) + 1); + if (ali == NULL) { + ERROR("out of memory"); + errno = ENOMEM; + goto error; + } + ali->api = api; + strcpy(ali->name, alias); + + /* insert the alias in the sorted order */ + pali = &set->aliases; + while(*pali && strcmp((*pali)->name, alias) < 0) + pali = &(*pali)->next; + ali->next = *pali; + *pali = ali; + return 0; +error: + return -1; +} + +int afb_apiset_is_alias(struct afb_apiset *set, const char *name) +{ + struct api_desc *api = searchrec(set, name); + return api && strcasecmp(api->name, name); +} + +const char *afb_apiset_unalias(struct afb_apiset *set, const char *name) +{ + struct api_desc *api = searchrec(set, name); + return api ? api->name : NULL; +} + /** * Delete from the 'set' the api of 'name'. * @param set the set to be changed @@ -265,17 +590,68 @@ error: */ int afb_apiset_del(struct afb_apiset *set, const char *name) { + struct api_class *cla; + struct api_alias *ali, **pali; + struct api_desc *desc, **pdesc, *odesc; int i, c; + /* search the alias */ + pali = &set->aliases; + while ((ali = *pali)) { + c = strcasecmp(ali->name, name); + if (!c) { + *pali = ali->next; + free(ali); + return 0; + } + if (c > 0) + break; + pali = &ali->next; + } + /* search the api */ - for (i = 0 ; i < set->count ; i++) { - c = strcasecmp(set->apis[i].name, name); + for (i = 0 ; i < set->apis.count ; i++) { + desc = set->apis.apis[i]; + c = strcasecmp(desc->name, name); if (c == 0) { - set->count--; - while(i < set->count) { - set->apis[i] = set->apis[i + 1]; + /* remove from classes */ + for (cla = all_classes ; cla ; cla = cla->next) + api_array_del(&cla->providers, desc); + + /* unlink from the whole set and their requires */ + pdesc = &all_apis; + while ((odesc = *pdesc) != desc) { + pdesc = &odesc->next; + } + *pdesc = odesc = desc->next; + while (odesc) { + odesc = odesc->next; + } + + /* remove references from classes */ + free(desc->require.classes.classes); + + /* drop the aliases */ + pali = &set->aliases; + while ((ali = *pali)) { + if (ali->api != desc) + pali = &ali->next; + else { + *pali = ali->next; + free(ali); + } + } + + /* unref the api */ + if (desc->api.itf->unref) + desc->api.itf->unref(desc->api.closure); + + set->apis.count--; + while(i < set->apis.count) { + set->apis.apis[i] = set->apis.apis[i + 1]; i++; } + free(desc); return 0; } if (c > 0) @@ -294,8 +670,21 @@ int afb_apiset_del(struct afb_apiset *set, const char *name) */ static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec) { - struct api_desc *i = search(set, name); - return i || !rec || !set->subset ? i : lookup(set->subset, name, rec); + struct api_desc *result; + + result = search(set, name); + while (!result) { + /* lacking the api, try onlack behaviour */ + if (set->onlack.callback && set->onlack.callback(set->onlack.closure, set, name) > 0) { + result = search(set, name); + if (result) + break; + } + if (!rec || !(set = set->subset)) + break; + result = search(set, name); + } + return result; } /** @@ -305,7 +694,7 @@ static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec * @param rec if not zero look also recursively in subsets * @return the api pointer in case of success or NULL in case of error */ -const struct afb_api *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec) +const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec) { struct api_desc *i; @@ -316,42 +705,126 @@ const struct afb_api *afb_apiset_lookup(struct afb_apiset *set, const char *name return NULL; } +static int start_api(struct api_desc *api); + +/** + * Start the apis of the 'array' + */ +static int start_array_apis(struct api_array *array) +{ + int i, rc = 0, rc2; + + i = 0; + while (i < array->count) { + rc2 = array->apis[i]->status; + if (rc2 == NOT_STARTED) { + rc2 = start_api(array->apis[i]); + if (rc2 < 0) + rc = rc2; + i = 0; + } else { + if (rc2) + rc = -1; + i++; + } + } + return rc; +} + +/** + * Start the class 'cla' (start the apis that provide it). + */ +static int start_class(struct api_class *cla) +{ + return start_array_apis(&cla->providers); +} + +/** + * Start the classes of the 'array' + */ +static int start_array_classes(struct api_array *array) +{ + int i, rc = 0, rc2; + + i = array->count; + while (i) { + rc2 = start_class(array->classes[--i]); + if (rc2 < 0) { + rc = rc2; + } + } + return rc; +} + +/** + * Start the depends of the 'array' + */ +static int start_array_depends(struct api_array *array) +{ + struct api_desc *api; + int i, rc = 0, rc2; + + i = 0; + while (i < array->count) { + api = searchrec(array->depends[i]->set, array->depends[i]->name); + if (!api) { + rc = -1; + i++; + } else { + rc2 = api->status; + if (rc2 == NOT_STARTED) { + rc2 = start_api(api); + if (rc2 < 0) + rc = rc2; + i = 0; + } else { + if (rc2) + rc = -1; + i++; + } + } + } + + return rc; +} + /** * Starts the service 'api'. * @param api the api - * @param share_session if true start the servic"e in a shared session - * if false start it in its own session - * @param onneed if true start the service if possible, if false the api - * must be a service - * @return a positive number on success + * @return zero on success, -1 on error */ -static int start_api(struct afb_apiset *set, struct api_desc *api, int share_session, int onneed) +static int start_api(struct api_desc *api) { int rc; - if (api->status == 0) + if (api->status != NOT_STARTED) { + if (api->status > 0) { + errno = api->status; + return -1; + } return 0; - else if (api->status > 0) { - errno = api->status; - return -1; } - INFO("API %s starting...", api->name); - if (api->api.itf->service_start) { - api->status = EBUSY; - rc = api->api.itf->service_start(api->api.closure, share_session, onneed, set); - if (rc < 0) { - api->status = errno ?: ECANCELED; - ERROR("The api %s failed to start (%d)", api->name, rc); - return -1; + NOTICE("API %s starting...", api->name); + api->status = EBUSY; + rc = start_array_classes(&api->require.classes); + if (rc < 0) + ERROR("Cannot start classes needed by api %s", api->name); + else { + rc = start_array_depends(&api->require.apis); + if (rc < 0) + ERROR("Cannot start apis needed by api %s", api->name); + else if (api->api.itf->service_start) { + rc = api->api.itf->service_start(api->api.closure); + if (rc < 0) + ERROR("The api %s failed to start", api->name); } - } else if (!onneed) { - /* already started: it is an error */ - ERROR("The api %s is not a startable service", api->name); - api->status = EINVAL; + } + if (rc < 0) { + api->status = errno ?: ECANCELED; return -1; } - NOTICE("API %s started", api->name); + INFO("API %s started", api->name); api->status = 0; return 0; } @@ -361,67 +834,79 @@ static int start_api(struct afb_apiset *set, struct api_desc *api, int share_ses * @param set the set of API * @param name the name of the API to get * @param rec if not zero look also recursively in subsets - * @return 0 in case of success or -1 in case of error + * @return a pointer to the API item in case of success or NULL in case of error */ -const struct afb_api *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec) +const struct afb_api_item *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec) { - struct api_desc *i; + struct api_desc *desc; - i = lookup(set, name, rec); - if (i) - return i->status && start_api(set, i, 1, 1) ? NULL : &i->api; - errno = ENOENT; - return NULL; + desc = lookup(set, name, rec); + if (!desc) { + errno = ENOENT; + return NULL; + } + if (start_api(desc)) { + errno = desc->status; + return NULL; + } + return &desc->api; } /** * Starts a service by its 'api' name. * @param set the api set * @param name name of the service to start - * @param share_session if true start the servic"e in a shared session - * if false start it in its own session - * @param onneed if true start the service if possible, if false the api - * must be a service - * @return a positive number on success + * @return zero on success, -1 on error */ -int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed) +int afb_apiset_start_service(struct afb_apiset *set, const char *name) { struct api_desc *a; - a = search(set, name); + a = searchrec(set, name); if (!a) { ERROR("can't find service %s", name); errno = ENOENT; return -1; } - return start_api(set, a, share_session, onneed); + return start_api(a); } /** * Starts all possible services but stops at first error. * @param set the api set - * @param share_session if true start the servic"e in a shared session - * if false start it in its own session * @return 0 on success or a negative number when an error is found */ -int afb_apiset_start_all_services(struct afb_apiset *set, int share_session) +int afb_apiset_start_all_services(struct afb_apiset *set) { - int rc; - struct api_desc *i, *e; + struct afb_apiset *rootset; + int rc, ret; + int i; - i = set->apis; - e = &set->apis[set->count]; - while (i != e) { - rc = start_api(set, i, share_session, 1); - if (rc < 0) - return rc; - i++; + rootset = set; + ret = 0; + while (set) { + i = 0; + while (i < set->apis.count) { + rc = set->apis.apis[i]->status; + if (rc == NOT_STARTED) { + rc = start_api(set->apis.apis[i]); + if (rc < 0) + ret = rc; + set = rootset; + i = 0; + } else { + if (rc) + ret = -1; + i++; + } + } + set = set->subset; } - - return set->subset ? afb_apiset_start_all_services(set->subset, share_session) : 0; + return ret; } +#if WITH_AFB_HOOK /** * Ask to update the hook flags of the 'api' * @param set the api set @@ -429,107 +914,157 @@ int afb_apiset_start_all_services(struct afb_apiset *set, int share_session) */ void afb_apiset_update_hooks(struct afb_apiset *set, const char *name) { - const struct api_desc *i, *e; + struct api_desc **i, **e, *d; if (!name) { - i = set->apis; - e = &set->apis[set->count]; + i = set->apis.apis; + e = &set->apis.apis[set->apis.count]; + while (i != e) { + d = *i++; + if (d->api.itf->update_hooks) + d->api.itf->update_hooks(d->api.closure); + } } else { - i = search(set, name); - e = &i[!!i]; - } - while (i != e) { - if (i->api.itf->update_hooks) - i->api.itf->update_hooks(i->api.closure); - i++; + d = searchrec(set, name); + if (d && d->api.itf->update_hooks) + d->api.itf->update_hooks(d->api.closure); } } +#endif /** - * Set the verbosity level of the 'api' + * Set the logmask of the 'api' to 'mask' * @param set the api set * @param name the api to set (NULL set all) */ -void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level) +void afb_apiset_set_logmask(struct afb_apiset *set, const char *name, int mask) { - const struct api_desc *i, *e; + int i; + struct api_desc *d; if (!name) { - i = set->apis; - e = &set->apis[set->count]; + for (i = 0 ; i < set->apis.count ; i++) { + d = set->apis.apis[i];; + if (d->api.itf->set_logmask) + d->api.itf->set_logmask(d->api.closure, mask); + } } else { - i = search(set, name); - e = &i[!!i]; - } - while (i != e) { - if (i->api.itf->set_verbosity) - i->api.itf->set_verbosity(i->api.closure, level); - i++; + d = searchrec(set, name); + if (d && d->api.itf->set_logmask) + d->api.itf->set_logmask(d->api.closure, mask); } } /** - * Get the verbosity level of the 'api' + * Get the logmask level of the 'api' * @param set the api set * @param name the api to get - * @return the verbosity level or -1 in case of error + * @return the logmask level or -1 in case of error */ -int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name) +int afb_apiset_get_logmask(struct afb_apiset *set, const char *name) { const struct api_desc *i; - i = name ? search(set, name) : NULL; + i = name ? searchrec(set, name) : NULL; if (!i) { errno = ENOENT; return -1; } - if (!i->api.itf->get_verbosity) - return verbosity; + if (!i->api.itf->get_logmask) + return logmask; - return i->api.itf->get_verbosity(i->api.closure); + return i->api.itf->get_logmask(i->api.closure); } -/** - * Get the description of the API of 'name' - * @param set the api set - * @param name the api whose description is required - * @return the description or NULL - */ -struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name) +void afb_apiset_describe(struct afb_apiset *set, const char *name, void (*describecb)(void *, struct json_object *), void *closure) { const struct api_desc *i; + struct json_object *r; - i = name ? search(set, name) : NULL; - return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL; + r = NULL; + if (name) { + i = searchrec(set, name); + if (i) { + if (i->api.itf->describe) { + i->api.itf->describe(i->api.closure, describecb, closure); + return; + } + } + } + describecb(closure, r); } + +struct get_names { + union { + struct { + size_t count; + size_t size; + }; + struct { + const char **ptr; + char *data; + }; + }; + int type; +}; + +static void get_names_count(void *closure, struct afb_apiset *set, const char *name, int isalias) +{ + struct get_names *gc = closure; + if ((1 + isalias) & gc->type) { + gc->size += strlen(name); + gc->count++; + } +} + +static void get_names_value(void *closure, struct afb_apiset *set, const char *name, int isalias) +{ + struct get_names *gc = closure; + if ((1 + isalias) & gc->type) { + *gc->ptr++ = gc->data; + gc->data = stpcpy(gc->data, name) + 1; + } +} + +#if !defined(APISET_NO_SORT) +static int get_names_sortcb(const void *a, const void *b) +{ + return strcasecmp(*(const char **)a, *(const char **)b); +} +#endif + /** * Get the list of api names * @param set the api set + * @param rec recursive + * @param type expected type: 1 names, 3 names+aliases, 2 aliases * @return a NULL terminated array of api names. Must be freed. */ -const char **afb_apiset_get_names(struct afb_apiset *set) +const char **afb_apiset_get_names(struct afb_apiset *set, int rec, int type) { + struct get_names gc; size_t size; - char *dest; const char **names; - int i; - size = set->count * (1 + sizeof(*names)) + sizeof(*names); - for (i = 0 ; i < set->count ; i++) - size += strlen(set->apis[i].name); + gc.count = gc.size = 0; + gc.type = type >= 1 && type <= 3 ? type : 1; + afb_apiset_enum(set, rec, get_names_count, &gc); + size = gc.size + gc.count * (1 + sizeof *names) + sizeof(*names); names = malloc(size); + if (!names) errno = ENOMEM; else { - dest = (void*)&names[set->count+1]; - for (i = 0 ; i < set->count ; i++) { - names[i] = dest; - dest = stpcpy(dest, set->apis[i].name) + 1; - } - names[i] = NULL; + gc.data = (char*)&names[gc.count + 1]; + gc.ptr = names; + afb_apiset_enum(set, rec, get_names_value, &gc); +#if !defined(APISET_NO_SORT) + qsort(names, gc.ptr - names, sizeof *names, get_names_sortcb); +#endif + *gc.ptr = NULL; } return names; } @@ -537,24 +1072,96 @@ const char **afb_apiset_get_names(struct afb_apiset *set) /** * Enumerate the api names to a callback. * @param set the api set + * @param rec should the enumeration be recursive * @param callback the function to call for each name * @param closure the closure for the callback */ -void afb_apiset_enum(struct afb_apiset *set, int rec, void (*callback)(struct afb_apiset *set, const char *name, void *closure), void *closure) +void afb_apiset_enum( + struct afb_apiset *set, + int rec, + void (*callback)(void *closure, struct afb_apiset *set, const char *name, int isalias), + void *closure) { + int i; struct afb_apiset *iset; - struct api_desc *i, *e; + struct api_desc *d; + struct api_alias *a; iset = set; while (iset) { - i = iset->apis; - e = &i[iset->count]; - while (i != e) { - if (lookup(set, i->name, 1) == i) - callback(iset, i->name, closure); - i++; + for (i = 0 ; i < set->apis.count ; i++) { + d = set->apis.apis[i];; + if (searchrec(set, d->name) == d) + callback(closure, iset, d->name, 0); + } + a = iset->aliases; + while (a) { + if (searchrec(set, a->name) == a->api) + callback(closure, iset, a->name, 1); + a = a->next; } iset = rec ? iset->subset : NULL; } } +/** + * Declare that the api of 'name' requires the api of name 'required'. + * The api is searched in the apiset 'set' and if 'rec' isn't null also in its subset. + * Returns 0 if the declaration successed or -1 in case of failure + * (ENOMEM: allocation failure, ENOENT: api name not found) + */ +int afb_apiset_require(struct afb_apiset *set, const char *name, const char *required) +{ + struct api_desc *a; + struct api_depend *d; + int rc = -1; + + a = searchrec(set, name); + if (!a) + errno = ENOENT; + else { + d = malloc(strlen(required) + 1 + sizeof *d); + if (!d) + errno = ENOMEM; + else { + d->set = set; + strcpy(d->name, required); + rc = api_array_add(&a->require.apis, d); + } + } + return rc; +} + +/** + * Declare that the api of name 'apiname' requires the class of name 'classname'. + * Returns 0 if the declaration successed or -1 in case of failure + * (ENOMEM: allocation failure, ENOENT: api name not found) + */ +int afb_apiset_require_class(struct afb_apiset *set, const char *apiname, const char *classname) +{ + struct api_desc *a = searchrec(set, apiname); + struct api_class *c = class_search(classname, 1); + return a && c ? api_array_add(&a->require.classes, c) : (errno = ENOENT, -1); +} + +/** + * Declare that the api of name 'apiname' provides the class of name 'classname' + * Returns 0 if the declaration successed or -1 in case of failure + * (ENOMEM: allocation failure, ENOENT: api name not found) + */ +int afb_apiset_provide_class(struct afb_apiset *set, const char *apiname, const char *classname) +{ + struct api_desc *a = searchrec(set, apiname); + struct api_class *c = class_search(classname, 1); + return a && c ? api_array_add(&c->providers, a) : (errno = ENOENT, -1); +} + +/** + * Start any API that provides the class of name 'classname' + */ +int afb_apiset_class_start(const char *classname) +{ + struct api_class *cla = class_search(classname, 0); + return cla ? start_class(cla) : (errno = ENOENT, -1); +} +