2 * Copyright (C) 2016, 2017, 2018 "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.
25 #include "afb-session.h"
28 #include "afb-apiset.h"
29 #include "afb-context.h"
32 #define INCR 8 /* CAUTION: must be a power of 2 */
44 int count; /* count of items */
47 struct api_desc **apis;
48 struct api_class **classes;
49 struct api_alias **aliases;
50 struct api_depend **depends;
55 * Internal description of an api
59 struct api_desc *next;
60 const char *name; /**< name of the api */
61 int status; /**< initialisation status */
62 struct afb_api_item api; /**< handler of the api */
64 struct api_array classes;
65 struct api_array apis;
70 * internal description of aliases
74 struct api_alias *next;
84 struct api_class *next;
85 struct api_array providers;
94 struct afb_apiset *set;
99 * Data structure for apiset
103 struct api_array apis; /**< the apis */
104 struct api_alias *aliases; /**< the aliases */
105 struct afb_apiset *subset; /**< subset if any */
107 int (*callback)(void*, struct afb_apiset*, const char*); /* not found handler */
109 void (*cleanup)(void*);
110 } onlack; /** not found handler */
111 int timeout; /**< the timeout in second for the apiset */
112 int refcount; /**< reference count for freeing resources */
113 char name[1]; /**< name of the apiset */
119 static struct api_desc *all_apis;
124 static struct api_class *all_classes;
127 * Ensure enough room in 'array' for 'count' items
129 static int api_array_ensure_count(struct api_array *array, int count)
134 c = (count + INCR - 1) & ~(INCR - 1);
135 anys = realloc(array->anys, c * sizeof *anys);
141 array->count = count;
147 * Insert in 'array' the item 'any' at the 'index'
149 static int api_array_insert(struct api_array *array, void *any, int index)
151 int n = array->count;
153 if (api_array_ensure_count(array, n + 1) < 0)
157 array->anys[n] = array->anys[n - 1];
161 array->anys[index] = any;
166 * Add the item 'any' to the 'array'
168 static int api_array_add(struct api_array *array, void *any)
170 int i, n = array->count;
172 for (i = 0 ; i < n ; i++) {
173 if (array->anys[i] == any)
177 if (api_array_ensure_count(array, n + 1) < 0)
180 array->anys[n] = any;
185 * Delete the 'api' from the 'array'
186 * Returns 1 if delete or 0 if not found
188 static int api_array_del(struct api_array *array, void *any)
190 int i = array->count;
192 if (array->anys[--i] == any) {
193 array->anys[i] = array->anys[--array->count];
201 * Search the class of 'name' and return it.
202 * In case where the class of 'namle' isn't found, it returns
203 * NULL when 'create' is null or a fresh created instance if 'create' isn't
204 * zero (but NULL on allocation failure).
206 static struct api_class *class_search(const char *name, int create)
210 for (c= all_classes ; c ; c = c->next) {
211 if (!strcasecmp(name, c->name))
218 c = calloc(1, strlen(name) + sizeof *c);
222 strcpy(c->name, name);
223 c->next = all_classes;
230 * Search the api of 'name'.
231 * @param set the api set
232 * @param name the api name to search
233 * @return the descriptor if found or NULL otherwise
235 static struct api_desc *search(struct afb_apiset *set, const char *name)
239 struct api_alias *aliases;
241 /* dichotomic search of the api */
244 up = set->apis.count;
246 /* check the mid of the slice */
248 a = set->apis.apis[i];
249 c = strcasecmp(a->name, name);
254 /* update the slice */
261 /* linear search of aliases */
262 aliases = set->aliases;
266 c = strcasecmp(aliases->name, name);
271 aliases = aliases->next;
277 * Search the api of 'name' in the apiset and in its subsets.
278 * @param set the api set
279 * @param name the api name to search
280 * @return the descriptor if found or NULL otherwise
282 static struct api_desc *searchrec(struct afb_apiset *set, const char *name)
284 struct api_desc *result;
287 result = search(set, name);
288 } while (result == NULL && (set = set->subset) != NULL);
294 * Increases the count of references to the apiset and return its address
295 * @param set the set whose reference count is to be increased
296 * @return the given apiset
298 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
301 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
306 * Decreases the count of references to the apiset and frees its
307 * resources when no more references exists.
308 * @param set the set to unrefrence
310 void afb_apiset_unref(struct afb_apiset *set)
315 if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
316 afb_apiset_unref(set->subset);
317 if (set->onlack.cleanup)
318 set->onlack.cleanup(set->onlack.closure);
319 while((a = set->aliases)) {
320 set->aliases = a->next;
323 while (set->apis.count) {
324 d = set->apis.apis[--set->apis.count];
325 if (d->api.itf->unref)
326 d->api.itf->unref(d->api.closure);
329 free(set->apis.apis);
336 * @param name the name of the apiset
337 * @param timeout the default timeout in seconds for the apiset
338 * @return the created apiset or NULL in case of error
340 struct afb_apiset *afb_apiset_create(const char *name, int timeout)
342 struct afb_apiset *set;
344 set = calloc(1, (name ? strlen(name) : 0) + sizeof *set);
346 set->timeout = timeout;
349 strcpy(set->name, name);
355 * Create an apiset being the last subset of 'set'
356 * @param set the set to extend with the created subset (can be NULL)
357 * @param name the name of the created apiset (can be NULL)
358 * @param timeout the default timeout in seconds for the created apiset
359 * @return the created apiset or NULL in case of error
361 struct afb_apiset *afb_apiset_create_subset_last(struct afb_apiset *set, const char *name, int timeout)
366 return afb_apiset_create_subset_first(set, name, timeout);
370 * Create an apiset being the first subset of 'set'
371 * @param set the set to extend with the created subset (can be NULL)
372 * @param name the name of the created apiset (can be NULL)
373 * @param timeout the default timeout in seconds for the created apiset
374 * @return the created apiset or NULL in case of error
376 struct afb_apiset *afb_apiset_create_subset_first(struct afb_apiset *set, const char *name, int timeout)
378 struct afb_apiset *result = afb_apiset_create(name, timeout);
380 result->subset = set->subset;
381 set->subset = result;
387 * the name of the apiset
388 * @param set the api set
389 * @return the name of the set
391 const char *afb_apiset_name(struct afb_apiset *set)
397 * Get the API timeout of the set
398 * @param set the api set
399 * @return the timeout in seconds
401 int afb_apiset_timeout_get(struct afb_apiset *set)
407 * Set the API timeout of the set
408 * @param set the api set
409 * @param to the timeout in seconds
411 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
417 * Get the subset of the set
418 * @param set the api set
419 * @return the subset of set
421 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
427 * Set the subset of the set
428 * @param set the api set
429 * @param subset the subset to set
431 * @return 0 in case of success or -1 if it had created a loop
433 int afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
435 struct afb_apiset *tmp;
437 /* avoid infinite loop */
438 for (tmp = subset ; tmp ; tmp = tmp->subset)
443 set->subset = afb_apiset_addref(subset);
444 afb_apiset_unref(tmp);
449 void afb_apiset_onlack_set(struct afb_apiset *set, int (*callback)(void*, struct afb_apiset*, const char*), void *closure, void (*cleanup)(void*))
451 if (set->onlack.cleanup)
452 set->onlack.cleanup(set->onlack.closure);
453 set->onlack.callback = callback;
454 set->onlack.closure = closure;
455 set->onlack.cleanup = cleanup;
459 * Adds the api of 'name' described by 'api'.
460 * @param set the api set
461 * @param name the name of the api to add (have to survive, not copied!)
463 * @returns 0 in case of success or -1 in case
464 * of error with errno set:
465 * - EEXIST if name already registered
466 * - ENOMEM when out of memory
468 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item api)
470 struct api_desc *desc;
473 /* check whether it exists already */
474 if (search(set, name)) {
475 ERROR("api of name %s already exists", name);
480 /* search insertion place */
481 for (i = 0 ; i < set->apis.count ; i++) {
482 c = strcasecmp(set->apis.apis[i]->name, name);
487 /* allocates memory */
488 desc = calloc(1, sizeof *desc);
496 if (api_array_insert(&set->apis, desc, i) < 0) {
501 desc->next = all_apis;
504 if (afb_api_is_public(name))
505 INFO("API %s added", name);
510 ERROR("out of memory");
517 * Adds a the 'alias' name to the api of 'name'.
518 * @params set the api set
519 * @param name the name of the api to alias
520 * @param alias the aliased name to add to the api of name
521 * @returns 0 in case of success or -1 in case
522 * of error with errno set:
523 * - ENOENT if the api doesn't exist
524 * - EEXIST if name (of alias) already registered
525 * - ENOMEM when out of memory
527 int afb_apiset_add_alias(struct afb_apiset *set, const char *name, const char *alias)
529 struct api_desc *api;
530 struct api_alias *ali, **pali;
532 /* check alias doesn't already exist */
533 if (search(set, alias)) {
534 ERROR("api of name %s already exists", alias);
539 /* check aliased api exists */
540 api = search(set, name);
542 ERROR("api of name %s doesn't exists", name);
547 /* allocates and init the struct */
548 ali = malloc(sizeof *ali + strlen(alias));
550 ERROR("out of memory");
555 strcpy(ali->name, alias);
557 /* insert the alias in the sorted order */
558 pali = &set->aliases;
559 while(*pali && strcmp((*pali)->name, alias) < 0)
560 pali = &(*pali)->next;
568 int afb_apiset_is_alias(struct afb_apiset *set, const char *name)
570 struct api_desc *api = searchrec(set, name);
571 return api && strcasecmp(api->name, name);
574 const char *afb_apiset_unalias(struct afb_apiset *set, const char *name)
576 struct api_desc *api = searchrec(set, name);
577 return api ? api->name : NULL;
581 * Delete from the 'set' the api of 'name'.
582 * @param set the set to be changed
583 * @param name the name of the API to remove
584 * @return 0 in case of success or -1 in case where the API doesn't exist.
586 int afb_apiset_del(struct afb_apiset *set, const char *name)
588 struct api_class *cla;
589 struct api_alias *ali, **pali;
590 struct api_desc *desc, **pdesc, *odesc;
593 /* search the alias */
594 pali = &set->aliases;
595 while ((ali = *pali)) {
596 c = strcasecmp(ali->name, name);
608 for (i = 0 ; i < set->apis.count ; i++) {
609 desc = set->apis.apis[i];
610 c = strcasecmp(desc->name, name);
612 /* remove from classes */
613 for (cla = all_classes ; cla ; cla = cla->next)
614 api_array_del(&cla->providers, desc);
616 /* unlink from the whole set and their requires */
618 while ((odesc = *pdesc) != desc) {
619 pdesc = &odesc->next;
621 *pdesc = odesc = desc->next;
626 /* remove references from classes */
627 free(desc->require.classes.classes);
629 /* drop the aliases */
630 pali = &set->aliases;
631 while ((ali = *pali)) {
632 if (ali->api != desc)
641 if (desc->api.itf->unref)
642 desc->api.itf->unref(desc->api.closure);
645 while(i < set->apis.count) {
646 set->apis.apis[i] = set->apis.apis[i + 1];
660 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
661 * @param set the set of API
662 * @param name the name of the API to get
663 * @param rec if not zero look also recursively in subsets
664 * @return the api pointer in case of success or NULL in case of error
666 static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
668 struct api_desc *result;
670 result = search(set, name);
672 /* lacking the api, try onlack behaviour */
673 if (set->onlack.callback && set->onlack.callback(set->onlack.closure, set, name) > 0) {
674 result = search(set, name);
678 if (!rec || !(set = set->subset))
680 result = search(set, name);
686 * Get from the 'set' the API of 'name' in 'api'
687 * @param set the set of API
688 * @param name the name of the API to get
689 * @param rec if not zero look also recursively in subsets
690 * @return the api pointer in case of success or NULL in case of error
692 const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
696 i = lookup(set, name, rec);
703 static int start_api(struct api_desc *api);
706 * Start the apis of the 'array'
708 static int start_array_apis(struct api_array *array)
714 rc2 = start_api(array->apis[--i]);
723 * Start the class 'cla' (start the apis that provide it).
725 static int start_class(struct api_class *cla)
727 return start_array_apis(&cla->providers);
731 * Start the classes of the 'array'
733 static int start_array_classes(struct api_array *array)
739 rc2 = start_class(array->classes[--i]);
748 * Start the depends of the 'array'
750 static int start_array_depends(struct api_array *array)
752 struct api_desc *api;
758 api = searchrec(array->depends[i]->set, array->depends[i]->name);
762 rc2 = start_api(api);
772 * Starts the service 'api'.
774 * @return a positive number on success
776 static int start_api(struct api_desc *api)
780 if (api->status == 0)
782 else if (api->status > 0) {
787 NOTICE("API %s starting...", api->name);
789 rc = start_array_classes(&api->require.classes);
791 ERROR("Cannot start classes needed by api %s", api->name);
793 rc = start_array_depends(&api->require.apis);
795 ERROR("Cannot start apis needed by api %s", api->name);
796 else if (api->api.itf->service_start) {
797 rc = api->api.itf->service_start(api->api.closure);
799 ERROR("The api %s failed to start", api->name);
803 api->status = errno ?: ECANCELED;
806 INFO("API %s started", api->name);
812 * Get from the 'set' the API of 'name' in 'api'
813 * @param set the set of API
814 * @param name the name of the API to get
815 * @param rec if not zero look also recursively in subsets
816 * @return 0 in case of success or -1 in case of error
818 const struct afb_api_item *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
822 i = lookup(set, name, rec);
824 return i->status && start_api(i) ? NULL : &i->api;
830 * Starts a service by its 'api' name.
831 * @param set the api set
832 * @param name name of the service to start
833 * @return a positive number on success
835 int afb_apiset_start_service(struct afb_apiset *set, const char *name)
839 a = searchrec(set, name);
841 ERROR("can't find service %s", name);
850 * Starts all possible services but stops at first error.
851 * @param set the api set
852 * @return 0 on success or a negative number when an error is found
854 int afb_apiset_start_all_services(struct afb_apiset *set)
862 while (i < set->apis.count) {
863 rc = start_api(set->apis.apis[i]);
874 * Ask to update the hook flags of the 'api'
875 * @param set the api set
876 * @param name the api to update (NULL updates all)
878 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
880 struct api_desc **i, **e, *d;
884 e = &set->apis.apis[set->apis.count];
887 if (d->api.itf->update_hooks)
888 d->api.itf->update_hooks(d->api.closure);
891 d = searchrec(set, name);
892 if (d && d->api.itf->update_hooks)
893 d->api.itf->update_hooks(d->api.closure);
898 * Set the logmask of the 'api' to 'mask'
899 * @param set the api set
900 * @param name the api to set (NULL set all)
902 void afb_apiset_set_logmask(struct afb_apiset *set, const char *name, int mask)
908 for (i = 0 ; i < set->apis.count ; i++) {
909 d = set->apis.apis[i];;
910 if (d->api.itf->set_logmask)
911 d->api.itf->set_logmask(d->api.closure, mask);
914 d = searchrec(set, name);
915 if (d && d->api.itf->set_logmask)
916 d->api.itf->set_logmask(d->api.closure, mask);
921 * Get the logmask level of the 'api'
922 * @param set the api set
923 * @param name the api to get
924 * @return the logmask level or -1 in case of error
926 int afb_apiset_get_logmask(struct afb_apiset *set, const char *name)
928 const struct api_desc *i;
930 i = name ? searchrec(set, name) : NULL;
936 if (!i->api.itf->get_logmask)
939 return i->api.itf->get_logmask(i->api.closure);
943 * Get the description of the API of 'name'
944 * @param set the api set
945 * @param name the api whose description is required
946 * @return the description or NULL
948 struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
950 const struct api_desc *i;
952 i = name ? searchrec(set, name) : NULL;
953 return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
970 static void get_names_count(void *closure, struct afb_apiset *set, const char *name, int isalias)
972 struct get_names *gc = closure;
973 if ((1 + isalias) & gc->type) {
974 gc->size += strlen(name);
979 static void get_names_value(void *closure, struct afb_apiset *set, const char *name, int isalias)
981 struct get_names *gc = closure;
982 if ((1 + isalias) & gc->type) {
983 *gc->ptr++ = gc->data;
984 gc->data = stpcpy(gc->data, name) + 1;
988 #if !defined(APISET_NO_SORT)
989 static int get_names_sortcb(const void *a, const void *b)
991 return strcasecmp(*(const char **)a, *(const char **)b);
996 * Get the list of api names
997 * @param set the api set
998 * @param rec recursive
999 * @param type expected type: 1 names, 3 names+aliases, 2 aliases
1000 * @return a NULL terminated array of api names. Must be freed.
1002 const char **afb_apiset_get_names(struct afb_apiset *set, int rec, int type)
1004 struct get_names gc;
1008 gc.count = gc.size = 0;
1009 gc.type = type >= 1 && type <= 3 ? type : 1;
1010 afb_apiset_enum(set, rec, get_names_count, &gc);
1012 size = gc.size + gc.count * (1 + sizeof *names) + sizeof(*names);
1013 names = malloc(size);
1018 gc.data = (char*)&names[gc.count + 1];
1020 afb_apiset_enum(set, rec, get_names_value, &gc);
1021 #if !defined(APISET_NO_SORT)
1022 qsort(names, gc.ptr - names, sizeof *names, get_names_sortcb);
1030 * Enumerate the api names to a callback.
1031 * @param set the api set
1032 * @param rec should the enumeration be recursive
1033 * @param callback the function to call for each name
1034 * @param closure the closure for the callback
1036 void afb_apiset_enum(
1037 struct afb_apiset *set,
1039 void (*callback)(void *closure, struct afb_apiset *set, const char *name, int isalias),
1043 struct afb_apiset *iset;
1045 struct api_alias *a;
1049 for (i = 0 ; i < set->apis.count ; i++) {
1050 d = set->apis.apis[i];;
1051 if (searchrec(set, d->name) == d)
1052 callback(closure, iset, d->name, 0);
1056 if (searchrec(set, a->name) == a->api)
1057 callback(closure, iset, a->name, 1);
1060 iset = rec ? iset->subset : NULL;
1065 * Declare that the api of 'name' requires the api of name 'required'.
1066 * The api is searched in the apiset 'set' and if 'rec' isn't null also in its subset.
1067 * Returns 0 if the declaration successed or -1 in case of failure
1068 * (ENOMEM: allocation failure, ENOENT: api name not found)
1070 int afb_apiset_require(struct afb_apiset *set, const char *name, const char *required)
1073 struct api_depend *d;
1076 a = searchrec(set, name);
1080 d = malloc(strlen(required) + sizeof *d);
1085 strcpy(d->name, required);
1086 rc = api_array_add(&a->require.apis, d);
1093 * Declare that the api of name 'apiname' requires the class of name 'classname'.
1094 * Returns 0 if the declaration successed or -1 in case of failure
1095 * (ENOMEM: allocation failure, ENOENT: api name not found)
1097 int afb_apiset_require_class(struct afb_apiset *set, const char *apiname, const char *classname)
1099 struct api_desc *a = searchrec(set, apiname);
1100 struct api_class *c = class_search(classname, 1);
1101 return a && c ? api_array_add(&a->require.classes, c) : (errno = ENOENT, -1);
1105 * Declare that the api of name 'apiname' provides the class of name 'classname'
1106 * Returns 0 if the declaration successed or -1 in case of failure
1107 * (ENOMEM: allocation failure, ENOENT: api name not found)
1109 int afb_apiset_provide_class(struct afb_apiset *set, const char *apiname, const char *classname)
1111 struct api_desc *a = searchrec(set, apiname);
1112 struct api_class *c = class_search(classname, 1);
1113 return a && c ? api_array_add(&c->providers, a) : (errno = ENOENT, -1);
1117 * Start any API that provides the class of name 'classname'
1119 int afb_apiset_class_start(const char *classname)
1121 struct api_class *cla = class_search(classname, 0);
1122 return cla ? start_class(cla) : (errno = ENOENT, -1);