2 * Copyright (C) 2015-2020 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
28 #include "afb-apiset.h"
29 #include "afb-context.h"
33 #define INCR 8 /* CAUTION: must be a power of 2 */
34 #define NOT_STARTED -1
46 int count; /* count of items */
49 struct api_desc **apis;
50 struct api_class **classes;
51 struct api_alias **aliases;
52 struct api_depend **depends;
57 * Internal description of an api
61 struct api_desc *next;
62 const char *name; /**< name of the api */
63 int status; /**< initialisation status:
64 - NOT_STARTED not started,
65 - 0 started without error,
66 - greater than 0, error number of start */
67 struct afb_api_item api; /**< handler of the api */
69 struct api_array classes;
70 struct api_array apis;
75 * internal description of aliases
79 struct api_alias *next;
89 struct api_class *next;
90 struct api_array providers;
99 struct afb_apiset *set;
104 * Data structure for apiset
108 struct api_array apis; /**< the apis */
109 struct api_alias *aliases; /**< the aliases */
110 struct afb_apiset *subset; /**< subset if any */
112 int (*callback)(void*, struct afb_apiset*, const char*); /* not found handler */
114 void (*cleanup)(void*);
115 } onlack; /** not found handler */
116 int timeout; /**< the timeout in second for the apiset */
117 int refcount; /**< reference count for freeing resources */
118 char name[]; /**< name of the apiset */
124 static struct api_desc *all_apis;
129 static struct api_class *all_classes;
132 * Ensure enough room in 'array' for 'count' items
134 static int api_array_ensure_count(struct api_array *array, int count)
139 c = (count + INCR - 1) & ~(INCR - 1);
140 anys = realloc(array->anys, c * sizeof *anys);
146 array->count = count;
152 * Insert in 'array' the item 'any' at the 'index'
154 static int api_array_insert(struct api_array *array, void *any, int index)
156 int n = array->count;
158 if (api_array_ensure_count(array, n + 1) < 0)
162 array->anys[n] = array->anys[n - 1];
166 array->anys[index] = any;
171 * Add the item 'any' to the 'array'
173 static int api_array_add(struct api_array *array, void *any)
175 int i, n = array->count;
177 for (i = 0 ; i < n ; i++) {
178 if (array->anys[i] == any)
182 if (api_array_ensure_count(array, n + 1) < 0)
185 array->anys[n] = any;
190 * Delete the 'api' from the 'array'
191 * Returns 1 if delete or 0 if not found
193 static int api_array_del(struct api_array *array, void *any)
195 int i = array->count;
197 if (array->anys[--i] == any) {
198 array->anys[i] = array->anys[--array->count];
206 * Search the class of 'name' and return it.
207 * In case where the class of 'namle' isn't found, it returns
208 * NULL when 'create' is null or a fresh created instance if 'create' isn't
209 * zero (but NULL on allocation failure).
211 static struct api_class *class_search(const char *name, int create)
215 for (c= all_classes ; c ; c = c->next) {
216 if (!strcasecmp(name, c->name))
223 c = calloc(1, strlen(name) + 1 + sizeof *c);
227 strcpy(c->name, name);
228 c->next = all_classes;
235 * Search the api of 'name'.
236 * @param set the api set
237 * @param name the api name to search
238 * @return the descriptor if found or NULL otherwise
240 static struct api_desc *search(struct afb_apiset *set, const char *name)
244 struct api_alias *aliases;
246 /* dichotomic search of the api */
249 up = set->apis.count;
251 /* check the mid of the slice */
253 a = set->apis.apis[i];
254 c = strcasecmp(a->name, name);
259 /* update the slice */
266 /* linear search of aliases */
267 aliases = set->aliases;
271 c = strcasecmp(aliases->name, name);
276 aliases = aliases->next;
282 * Search the api of 'name' in the apiset and in its subsets.
283 * @param set the api set
284 * @param name the api name to search
285 * @return the descriptor if found or NULL otherwise
287 static struct api_desc *searchrec(struct afb_apiset *set, const char *name)
289 struct api_desc *result;
292 result = search(set, name);
293 } while (result == NULL && (set = set->subset) != NULL);
299 * Increases the count of references to the apiset and return its address
300 * @param set the set whose reference count is to be increased
301 * @return the given apiset
303 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
306 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
311 * Decreases the count of references to the apiset and frees its
312 * resources when no more references exists.
313 * @param set the set to unrefrence
315 void afb_apiset_unref(struct afb_apiset *set)
320 if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
321 afb_apiset_unref(set->subset);
322 if (set->onlack.cleanup)
323 set->onlack.cleanup(set->onlack.closure);
324 while((a = set->aliases)) {
325 set->aliases = a->next;
328 while (set->apis.count) {
329 d = set->apis.apis[--set->apis.count];
330 if (d->api.itf->unref)
331 d->api.itf->unref(d->api.closure);
334 free(set->apis.apis);
341 * @param name the name of the apiset
342 * @param timeout the default timeout in seconds for the apiset
343 * @return the created apiset or NULL in case of error
345 struct afb_apiset *afb_apiset_create(const char *name, int timeout)
347 struct afb_apiset *set;
349 set = calloc(1, (name ? strlen(name) : 0) + 1 + sizeof *set);
351 set->timeout = timeout;
354 strcpy(set->name, name);
360 * Create an apiset being the last subset of 'set'
361 * @param set the set to extend with the created subset (can be NULL)
362 * @param name the name of the created apiset (can be NULL)
363 * @param timeout the default timeout in seconds for the created apiset
364 * @return the created apiset or NULL in case of error
366 struct afb_apiset *afb_apiset_create_subset_last(struct afb_apiset *set, const char *name, int timeout)
371 return afb_apiset_create_subset_first(set, name, timeout);
375 * Create an apiset being the first subset of 'set'
376 * @param set the set to extend with the created subset (can be NULL)
377 * @param name the name of the created apiset (can be NULL)
378 * @param timeout the default timeout in seconds for the created apiset
379 * @return the created apiset or NULL in case of error
381 struct afb_apiset *afb_apiset_create_subset_first(struct afb_apiset *set, const char *name, int timeout)
383 struct afb_apiset *result = afb_apiset_create(name, timeout);
385 result->subset = set->subset;
386 set->subset = result;
392 * the name of the apiset
393 * @param set the api set
394 * @return the name of the set
396 const char *afb_apiset_name(struct afb_apiset *set)
402 * Get the API timeout of the set
403 * @param set the api set
404 * @return the timeout in seconds
406 int afb_apiset_timeout_get(struct afb_apiset *set)
412 * Set the API timeout of the set
413 * @param set the api set
414 * @param to the timeout in seconds
416 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
422 * Get the subset of the set
423 * @param set the api set
424 * @return the subset of set
426 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
432 * Set the subset of the set
433 * @param set the api set
434 * @param subset the subset to set
436 * @return 0 in case of success or -1 if it had created a loop
438 int afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
440 struct afb_apiset *tmp;
442 /* avoid infinite loop */
443 for (tmp = subset ; tmp ; tmp = tmp->subset)
448 set->subset = afb_apiset_addref(subset);
449 afb_apiset_unref(tmp);
454 void afb_apiset_onlack_set(struct afb_apiset *set, int (*callback)(void*, struct afb_apiset*, const char*), void *closure, void (*cleanup)(void*))
456 if (set->onlack.cleanup)
457 set->onlack.cleanup(set->onlack.closure);
458 set->onlack.callback = callback;
459 set->onlack.closure = closure;
460 set->onlack.cleanup = cleanup;
464 * Adds the api of 'name' described by 'api'.
465 * @param set the api set
466 * @param name the name of the api to add (have to survive, not copied!)
468 * @returns 0 in case of success or -1 in case
469 * of error with errno set:
470 * - EEXIST if name already registered
471 * - ENOMEM when out of memory
473 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item api)
475 struct api_desc *desc;
478 /* check whether it exists already */
479 if (search(set, name)) {
480 ERROR("api of name %s already exists", name);
485 /* search insertion place */
486 for (i = 0 ; i < set->apis.count ; i++) {
487 c = strcasecmp(set->apis.apis[i]->name, name);
492 /* allocates memory */
493 desc = calloc(1, sizeof *desc);
497 desc->status = NOT_STARTED;
501 if (api_array_insert(&set->apis, desc, i) < 0) {
506 desc->next = all_apis;
509 if (afb_api_is_public(name))
510 INFO("API %s added", name);
515 ERROR("out of memory");
522 * Adds a the 'alias' name to the api of 'name'.
523 * @params set the api set
524 * @param name the name of the api to alias
525 * @param alias the aliased name to add to the api of name
526 * @returns 0 in case of success or -1 in case
527 * of error with errno set:
528 * - ENOENT if the api doesn't exist
529 * - EEXIST if name (of alias) already registered
530 * - ENOMEM when out of memory
532 int afb_apiset_add_alias(struct afb_apiset *set, const char *name, const char *alias)
534 struct api_desc *api;
535 struct api_alias *ali, **pali;
537 /* check alias doesn't already exist */
538 if (search(set, alias)) {
539 ERROR("api of name %s already exists", alias);
544 /* check aliased api exists */
545 api = search(set, name);
547 ERROR("api of name %s doesn't exists", name);
552 /* allocates and init the struct */
553 ali = malloc(sizeof *ali + strlen(alias) + 1);
555 ERROR("out of memory");
560 strcpy(ali->name, alias);
562 /* insert the alias in the sorted order */
563 pali = &set->aliases;
564 while(*pali && strcmp((*pali)->name, alias) < 0)
565 pali = &(*pali)->next;
573 int afb_apiset_is_alias(struct afb_apiset *set, const char *name)
575 struct api_desc *api = searchrec(set, name);
576 return api && strcasecmp(api->name, name);
579 const char *afb_apiset_unalias(struct afb_apiset *set, const char *name)
581 struct api_desc *api = searchrec(set, name);
582 return api ? api->name : NULL;
586 * Delete from the 'set' the api of 'name'.
587 * @param set the set to be changed
588 * @param name the name of the API to remove
589 * @return 0 in case of success or -1 in case where the API doesn't exist.
591 int afb_apiset_del(struct afb_apiset *set, const char *name)
593 struct api_class *cla;
594 struct api_alias *ali, **pali;
595 struct api_desc *desc, **pdesc, *odesc;
598 /* search the alias */
599 pali = &set->aliases;
600 while ((ali = *pali)) {
601 c = strcasecmp(ali->name, name);
613 for (i = 0 ; i < set->apis.count ; i++) {
614 desc = set->apis.apis[i];
615 c = strcasecmp(desc->name, name);
617 /* remove from classes */
618 for (cla = all_classes ; cla ; cla = cla->next)
619 api_array_del(&cla->providers, desc);
621 /* unlink from the whole set and their requires */
623 while ((odesc = *pdesc) != desc) {
624 pdesc = &odesc->next;
626 *pdesc = odesc = desc->next;
631 /* remove references from classes */
632 free(desc->require.classes.classes);
634 /* drop the aliases */
635 pali = &set->aliases;
636 while ((ali = *pali)) {
637 if (ali->api != desc)
646 if (desc->api.itf->unref)
647 desc->api.itf->unref(desc->api.closure);
650 while(i < set->apis.count) {
651 set->apis.apis[i] = set->apis.apis[i + 1];
665 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
666 * @param set the set of API
667 * @param name the name of the API to get
668 * @param rec if not zero look also recursively in subsets
669 * @return the api pointer in case of success or NULL in case of error
671 static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
673 struct api_desc *result;
675 result = search(set, name);
677 /* lacking the api, try onlack behaviour */
678 if (set->onlack.callback && set->onlack.callback(set->onlack.closure, set, name) > 0) {
679 result = search(set, name);
683 if (!rec || !(set = set->subset))
685 result = search(set, name);
691 * Get from the 'set' the API of 'name' in 'api'
692 * @param set the set of API
693 * @param name the name of the API to get
694 * @param rec if not zero look also recursively in subsets
695 * @return the api pointer in case of success or NULL in case of error
697 const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
701 i = lookup(set, name, rec);
708 static int start_api(struct api_desc *api);
711 * Start the apis of the 'array'
713 static int start_array_apis(struct api_array *array)
718 while (i < array->count) {
719 rc2 = array->apis[i]->status;
720 if (rc2 == NOT_STARTED) {
721 rc2 = start_api(array->apis[i]);
735 * Start the class 'cla' (start the apis that provide it).
737 static int start_class(struct api_class *cla)
739 return start_array_apis(&cla->providers);
743 * Start the classes of the 'array'
745 static int start_array_classes(struct api_array *array)
751 rc2 = start_class(array->classes[--i]);
760 * Start the depends of the 'array'
762 static int start_array_depends(struct api_array *array)
764 struct api_desc *api;
768 while (i < array->count) {
769 api = searchrec(array->depends[i]->set, array->depends[i]->name);
775 if (rc2 == NOT_STARTED) {
776 rc2 = start_api(api);
792 * Starts the service 'api'.
794 * @return zero on success, -1 on error
796 static int start_api(struct api_desc *api)
800 if (api->status != NOT_STARTED) {
801 if (api->status > 0) {
808 NOTICE("API %s starting...", api->name);
810 rc = start_array_classes(&api->require.classes);
812 ERROR("Cannot start classes needed by api %s", api->name);
814 rc = start_array_depends(&api->require.apis);
816 ERROR("Cannot start apis needed by api %s", api->name);
817 else if (api->api.itf->service_start) {
818 rc = api->api.itf->service_start(api->api.closure);
820 ERROR("The api %s failed to start", api->name);
824 api->status = errno ?: ECANCELED;
827 INFO("API %s started", api->name);
833 * Get from the 'set' the API of 'name' in 'api'
834 * @param set the set of API
835 * @param name the name of the API to get
836 * @param rec if not zero look also recursively in subsets
837 * @return a pointer to the API item in case of success or NULL in case of error
839 const struct afb_api_item *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
841 struct api_desc *desc;
843 desc = lookup(set, name, rec);
848 if (start_api(desc)) {
849 errno = desc->status;
856 * Starts a service by its 'api' name.
857 * @param set the api set
858 * @param name name of the service to start
859 * @return zero on success, -1 on error
861 int afb_apiset_start_service(struct afb_apiset *set, const char *name)
865 a = searchrec(set, name);
867 ERROR("can't find service %s", name);
876 * Starts all possible services but stops at first error.
877 * @param set the api set
878 * @return 0 on success or a negative number when an error is found
880 int afb_apiset_start_all_services(struct afb_apiset *set)
882 struct afb_apiset *rootset;
890 while (i < set->apis.count) {
891 rc = set->apis.apis[i]->status;
892 if (rc == NOT_STARTED) {
893 rc = start_api(set->apis.apis[i]);
911 * Ask to update the hook flags of the 'api'
912 * @param set the api set
913 * @param name the api to update (NULL updates all)
915 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
917 struct api_desc **i, **e, *d;
921 e = &set->apis.apis[set->apis.count];
924 if (d->api.itf->update_hooks)
925 d->api.itf->update_hooks(d->api.closure);
928 d = searchrec(set, name);
929 if (d && d->api.itf->update_hooks)
930 d->api.itf->update_hooks(d->api.closure);
936 * Set the logmask of the 'api' to 'mask'
937 * @param set the api set
938 * @param name the api to set (NULL set all)
940 void afb_apiset_set_logmask(struct afb_apiset *set, const char *name, int mask)
946 for (i = 0 ; i < set->apis.count ; i++) {
947 d = set->apis.apis[i];;
948 if (d->api.itf->set_logmask)
949 d->api.itf->set_logmask(d->api.closure, mask);
952 d = searchrec(set, name);
953 if (d && d->api.itf->set_logmask)
954 d->api.itf->set_logmask(d->api.closure, mask);
959 * Get the logmask level of the 'api'
960 * @param set the api set
961 * @param name the api to get
962 * @return the logmask level or -1 in case of error
964 int afb_apiset_get_logmask(struct afb_apiset *set, const char *name)
966 const struct api_desc *i;
968 i = name ? searchrec(set, name) : NULL;
974 if (!i->api.itf->get_logmask)
977 return i->api.itf->get_logmask(i->api.closure);
980 void afb_apiset_describe(struct afb_apiset *set, const char *name, void (*describecb)(void *, struct json_object *), void *closure)
982 const struct api_desc *i;
983 struct json_object *r;
987 i = searchrec(set, name);
989 if (i->api.itf->describe) {
990 i->api.itf->describe(i->api.closure, describecb, closure);
995 describecb(closure, r);
1013 static void get_names_count(void *closure, struct afb_apiset *set, const char *name, int isalias)
1015 struct get_names *gc = closure;
1016 if ((1 + isalias) & gc->type) {
1017 gc->size += strlen(name);
1022 static void get_names_value(void *closure, struct afb_apiset *set, const char *name, int isalias)
1024 struct get_names *gc = closure;
1025 if ((1 + isalias) & gc->type) {
1026 *gc->ptr++ = gc->data;
1027 gc->data = stpcpy(gc->data, name) + 1;
1031 #if !defined(APISET_NO_SORT)
1032 static int get_names_sortcb(const void *a, const void *b)
1034 return strcasecmp(*(const char **)a, *(const char **)b);
1039 * Get the list of api names
1040 * @param set the api set
1041 * @param rec recursive
1042 * @param type expected type: 1 names, 3 names+aliases, 2 aliases
1043 * @return a NULL terminated array of api names. Must be freed.
1045 const char **afb_apiset_get_names(struct afb_apiset *set, int rec, int type)
1047 struct get_names gc;
1051 gc.count = gc.size = 0;
1052 gc.type = type >= 1 && type <= 3 ? type : 1;
1053 afb_apiset_enum(set, rec, get_names_count, &gc);
1055 size = gc.size + gc.count * (1 + sizeof *names) + sizeof(*names);
1056 names = malloc(size);
1061 gc.data = (char*)&names[gc.count + 1];
1063 afb_apiset_enum(set, rec, get_names_value, &gc);
1064 #if !defined(APISET_NO_SORT)
1065 qsort(names, gc.ptr - names, sizeof *names, get_names_sortcb);
1073 * Enumerate the api names to a callback.
1074 * @param set the api set
1075 * @param rec should the enumeration be recursive
1076 * @param callback the function to call for each name
1077 * @param closure the closure for the callback
1079 void afb_apiset_enum(
1080 struct afb_apiset *set,
1082 void (*callback)(void *closure, struct afb_apiset *set, const char *name, int isalias),
1086 struct afb_apiset *iset;
1088 struct api_alias *a;
1092 for (i = 0 ; i < set->apis.count ; i++) {
1093 d = set->apis.apis[i];;
1094 if (searchrec(set, d->name) == d)
1095 callback(closure, iset, d->name, 0);
1099 if (searchrec(set, a->name) == a->api)
1100 callback(closure, iset, a->name, 1);
1103 iset = rec ? iset->subset : NULL;
1108 * Declare that the api of 'name' requires the api of name 'required'.
1109 * The api is searched in the apiset 'set' and if 'rec' isn't null also in its subset.
1110 * Returns 0 if the declaration successed or -1 in case of failure
1111 * (ENOMEM: allocation failure, ENOENT: api name not found)
1113 int afb_apiset_require(struct afb_apiset *set, const char *name, const char *required)
1116 struct api_depend *d;
1119 a = searchrec(set, name);
1123 d = malloc(strlen(required) + 1 + sizeof *d);
1128 strcpy(d->name, required);
1129 rc = api_array_add(&a->require.apis, d);
1136 * Declare that the api of name 'apiname' requires the class of name 'classname'.
1137 * Returns 0 if the declaration successed or -1 in case of failure
1138 * (ENOMEM: allocation failure, ENOENT: api name not found)
1140 int afb_apiset_require_class(struct afb_apiset *set, const char *apiname, const char *classname)
1142 struct api_desc *a = searchrec(set, apiname);
1143 struct api_class *c = class_search(classname, 1);
1144 return a && c ? api_array_add(&a->require.classes, c) : (errno = ENOENT, -1);
1148 * Declare that the api of name 'apiname' provides the class of name 'classname'
1149 * Returns 0 if the declaration successed or -1 in case of failure
1150 * (ENOMEM: allocation failure, ENOENT: api name not found)
1152 int afb_apiset_provide_class(struct afb_apiset *set, const char *apiname, const char *classname)
1154 struct api_desc *a = searchrec(set, apiname);
1155 struct api_class *c = class_search(classname, 1);
1156 return a && c ? api_array_add(&c->providers, a) : (errno = ENOENT, -1);
1160 * Start any API that provides the class of name 'classname'
1162 int afb_apiset_class_start(const char *classname)
1164 struct api_class *cla = class_search(classname, 0);
1165 return cla ? start_class(cla) : (errno = ENOENT, -1);