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 void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
433 struct afb_apiset *tmp;
435 /* avoid infinite loop */
439 set->subset = afb_apiset_addref(subset);
440 afb_apiset_unref(tmp);
443 void afb_apiset_onlack_set(struct afb_apiset *set, int (*callback)(void*, struct afb_apiset*, const char*), void *closure, void (*cleanup)(void*))
445 if (set->onlack.cleanup)
446 set->onlack.cleanup(set->onlack.closure);
447 set->onlack.callback = callback;
448 set->onlack.closure = closure;
449 set->onlack.cleanup = cleanup;
453 * Adds the api of 'name' described by 'api'.
454 * @param set the api set
455 * @param name the name of the api to add (have to survive, not copied!)
457 * @returns 0 in case of success or -1 in case
458 * of error with errno set:
459 * - EEXIST if name already registered
460 * - ENOMEM when out of memory
462 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item api)
464 struct api_desc *desc;
467 /* check whether it exists already */
468 if (search(set, name)) {
469 ERROR("api of name %s already exists", name);
474 /* search insertion place */
475 for (i = 0 ; i < set->apis.count ; i++) {
476 c = strcasecmp(set->apis.apis[i]->name, name);
481 /* allocates memory */
482 desc = calloc(1, sizeof *desc);
490 if (api_array_insert(&set->apis, desc, i) < 0) {
495 desc->next = all_apis;
498 INFO("API %s added", name);
503 ERROR("out of memory");
510 * Adds a the 'alias' name to the api of 'name'.
511 * @params set the api set
512 * @param name the name of the api to alias
513 * @param alias the aliased name to add to the api of name
514 * @returns 0 in case of success or -1 in case
515 * of error with errno set:
516 * - ENOENT if the api doesn't exist
517 * - EEXIST if name (of alias) already registered
518 * - ENOMEM when out of memory
520 int afb_apiset_add_alias(struct afb_apiset *set, const char *name, const char *alias)
522 struct api_desc *api;
523 struct api_alias *ali, **pali;
525 /* check alias doesn't already exist */
526 if (search(set, alias)) {
527 ERROR("api of name %s already exists", alias);
532 /* check aliased api exists */
533 api = search(set, name);
535 ERROR("api of name %s doesn't exists", name);
540 /* allocates and init the struct */
541 ali = malloc(sizeof *ali + strlen(alias));
543 ERROR("out of memory");
548 strcpy(ali->name, alias);
550 /* insert the alias in the sorted order */
551 pali = &set->aliases;
552 while(*pali && strcmp((*pali)->name, alias) < 0)
553 pali = &(*pali)->next;
561 int afb_apiset_is_alias(struct afb_apiset *set, const char *name)
563 struct api_desc *api = searchrec(set, name);
564 return api && strcasecmp(api->name, name);
567 const char *afb_apiset_unalias(struct afb_apiset *set, const char *name)
569 struct api_desc *api = searchrec(set, name);
570 return api ? api->name : NULL;
574 * Delete from the 'set' the api of 'name'.
575 * @param set the set to be changed
576 * @param name the name of the API to remove
577 * @return 0 in case of success or -1 in case where the API doesn't exist.
579 int afb_apiset_del(struct afb_apiset *set, const char *name)
581 struct api_class *cla;
582 struct api_alias *ali, **pali;
583 struct api_desc *desc, **pdesc, *odesc;
586 /* search the alias */
587 pali = &set->aliases;
588 while ((ali = *pali)) {
589 c = strcasecmp(ali->name, name);
601 for (i = 0 ; i < set->apis.count ; i++) {
602 desc = set->apis.apis[i];
603 c = strcasecmp(desc->name, name);
605 /* remove from classes */
606 for (cla = all_classes ; cla ; cla = cla->next)
607 api_array_del(&cla->providers, desc);
609 /* unlink from the whole set and their requires */
611 while ((odesc = *pdesc) != desc) {
612 pdesc = &odesc->next;
614 *pdesc = odesc = desc->next;
619 /* remove references from classes */
620 free(desc->require.classes.classes);
622 /* drop the aliases */
623 pali = &set->aliases;
624 while ((ali = *pali)) {
625 if (ali->api != desc)
634 if (desc->api.itf->unref)
635 desc->api.itf->unref(desc->api.closure);
638 while(i < set->apis.count) {
639 set->apis.apis[i] = set->apis.apis[i + 1];
653 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
654 * @param set the set of API
655 * @param name the name of the API to get
656 * @param rec if not zero look also recursively in subsets
657 * @return the api pointer in case of success or NULL in case of error
659 static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
661 struct api_desc *result;
663 result = search(set, name);
665 /* lacking the api, try onlack behaviour */
666 if (set->onlack.callback && set->onlack.callback(set->onlack.closure, set, name) > 0) {
667 result = search(set, name);
671 if (!rec || !(set = set->subset))
673 result = search(set, name);
679 * Get from the 'set' the API of 'name' in 'api'
680 * @param set the set of API
681 * @param name the name of the API to get
682 * @param rec if not zero look also recursively in subsets
683 * @return the api pointer in case of success or NULL in case of error
685 const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
689 i = lookup(set, name, rec);
696 static int start_api(struct api_desc *api, int share_session, int onneed);
699 * Start the apis of the 'array'
700 * The attribute 'share_session' is sent to the start function.
702 static int start_array_apis(struct api_array *array, int share_session)
708 rc2 = start_api(array->apis[--i], share_session, 1);
717 * Start the class 'cla' (start the apis that provide it).
718 * The attribute 'share_session' is sent to the start function.
720 static int start_class(struct api_class *cla, int share_session)
722 return start_array_apis(&cla->providers, share_session);
726 * Start the classes of the 'array'
727 * The attribute 'share_session' is sent to the start function.
729 static int start_array_classes(struct api_array *array, int share_session)
735 rc2 = start_class(array->classes[--i], share_session);
744 * Start the depends of the 'array'
745 * The attribute 'share_session' is sent to the start function.
747 static int start_array_depends(struct api_array *array, int share_session)
749 struct api_desc *api;
755 api = searchrec(array->depends[i]->set, array->depends[i]->name);
759 rc2 = start_api(api, share_session, 1);
769 * Starts the service 'api'.
771 * @param share_session if true start the servic"e in a shared session
772 * if false start it in its own session
773 * @param onneed if true start the service if possible, if false the api
775 * @return a positive number on success
777 static int start_api(struct api_desc *api, int share_session, int onneed)
781 if (api->status == 0)
783 else if (api->status > 0) {
788 INFO("API %s starting...", api->name);
789 rc = start_array_classes(&api->require.classes, share_session);
790 rc = start_array_depends(&api->require.apis, share_session);
791 if (api->api.itf->service_start) {
793 rc = api->api.itf->service_start(api->api.closure, share_session, onneed);
795 api->status = errno ?: ECANCELED;
796 ERROR("The api %s failed to start (%d)", api->name, rc);
799 } else if (!onneed) {
800 /* already started: it is an error */
801 ERROR("The api %s is not a startable service", api->name);
802 api->status = EINVAL;
805 NOTICE("API %s started", api->name);
811 * Get from the 'set' the API of 'name' in 'api'
812 * @param set the set of API
813 * @param name the name of the API to get
814 * @param rec if not zero look also recursively in subsets
815 * @return 0 in case of success or -1 in case of error
817 const struct afb_api_item *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
821 i = lookup(set, name, rec);
823 return i->status && start_api(i, 1, 1) ? NULL : &i->api;
829 * Starts a service by its 'api' name.
830 * @param set the api set
831 * @param name name of the service to start
832 * @param share_session if true start the servic"e in a shared session
833 * if false start it in its own session
834 * @param onneed if true start the service if possible, if false the api
836 * @return a positive number on success
838 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
842 a = searchrec(set, name);
844 ERROR("can't find service %s", name);
849 return start_api(a, share_session, onneed);
853 * Starts all possible services but stops at first error.
854 * @param set the api set
855 * @param share_session if true start the servic"e in a shared session
856 * if false start it in its own session
857 * @return 0 on success or a negative number when an error is found
859 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
866 while (i < set->apis.count) {
867 rc = start_api(set->apis.apis[i], share_session, 1);
878 * Ask to update the hook flags of the 'api'
879 * @param set the api set
880 * @param name the api to update (NULL updates all)
882 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
884 struct api_desc **i, **e, *d;
888 e = &set->apis.apis[set->apis.count];
891 if (d->api.itf->update_hooks)
892 d->api.itf->update_hooks(d->api.closure);
895 d = searchrec(set, name);
896 if (d && d->api.itf->update_hooks)
897 d->api.itf->update_hooks(d->api.closure);
902 * Set the logmask of the 'api' to 'mask'
903 * @param set the api set
904 * @param name the api to set (NULL set all)
906 void afb_apiset_set_logmask(struct afb_apiset *set, const char *name, int mask)
912 for (i = 0 ; i < set->apis.count ; i++) {
913 d = set->apis.apis[i];;
914 if (d->api.itf->set_logmask)
915 d->api.itf->set_logmask(d->api.closure, mask);
918 d = searchrec(set, name);
919 if (d && d->api.itf->set_logmask)
920 d->api.itf->set_logmask(d->api.closure, mask);
925 * Get the logmask level of the 'api'
926 * @param set the api set
927 * @param name the api to get
928 * @return the logmask level or -1 in case of error
930 int afb_apiset_get_logmask(struct afb_apiset *set, const char *name)
932 const struct api_desc *i;
934 i = name ? searchrec(set, name) : NULL;
940 if (!i->api.itf->get_logmask)
943 return i->api.itf->get_logmask(i->api.closure);
947 * Get the description of the API of 'name'
948 * @param set the api set
949 * @param name the api whose description is required
950 * @return the description or NULL
952 struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
954 const struct api_desc *i;
956 i = name ? searchrec(set, name) : NULL;
957 return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
974 static void get_names_count(void *closure, struct afb_apiset *set, const char *name, int isalias)
976 struct get_names *gc = closure;
977 if ((1 + isalias) & gc->type) {
978 gc->size += strlen(name);
983 static void get_names_value(void *closure, struct afb_apiset *set, const char *name, int isalias)
985 struct get_names *gc = closure;
986 if ((1 + isalias) & gc->type) {
987 *gc->ptr++ = gc->data;
988 gc->data = stpcpy(gc->data, name) + 1;
992 #if !defined(APISET_NO_SORT)
993 static int get_names_sortcb(const void *a, const void *b)
995 return strcasecmp(*(const char **)a, *(const char **)b);
1000 * Get the list of api names
1001 * @param set the api set
1002 * @param rec recursive
1003 * @param type expected type: 1 names, 3 names+aliases, 2 aliases
1004 * @return a NULL terminated array of api names. Must be freed.
1006 const char **afb_apiset_get_names(struct afb_apiset *set, int rec, int type)
1008 struct get_names gc;
1012 gc.count = gc.size = 0;
1013 gc.type = type >= 1 && type <= 3 ? type : 1;
1014 afb_apiset_enum(set, rec, get_names_count, &gc);
1016 size = gc.size + gc.count * (1 + sizeof *names) + sizeof(*names);
1017 names = malloc(size);
1022 gc.data = (char*)&names[gc.count + 1];
1024 afb_apiset_enum(set, rec, get_names_value, &gc);
1025 #if !defined(APISET_NO_SORT)
1026 qsort(names, gc.ptr - names, sizeof *names, get_names_sortcb);
1034 * Enumerate the api names to a callback.
1035 * @param set the api set
1036 * @param rec should the enumeration be recursive
1037 * @param callback the function to call for each name
1038 * @param closure the closure for the callback
1040 void afb_apiset_enum(
1041 struct afb_apiset *set,
1043 void (*callback)(void *closure, struct afb_apiset *set, const char *name, int isalias),
1047 struct afb_apiset *iset;
1049 struct api_alias *a;
1053 for (i = 0 ; i < set->apis.count ; i++) {
1054 d = set->apis.apis[i];;
1055 if (searchrec(set, d->name) == d)
1056 callback(closure, iset, d->name, 0);
1060 if (searchrec(set, a->name) == a->api)
1061 callback(closure, iset, a->name, 1);
1064 iset = rec ? iset->subset : NULL;
1069 * Declare that the api of 'name' requires the api of name 'required'.
1070 * The api is searched in the apiset 'set' and if 'rec' isn't null also in its subset.
1071 * Returns 0 if the declaration successed or -1 in case of failure
1072 * (ENOMEM: allocation failure, ENOENT: api name not found)
1074 int afb_apiset_require(struct afb_apiset *set, const char *name, const char *required)
1077 struct api_depend *d;
1080 a = searchrec(set, name);
1084 d = malloc(strlen(required) + sizeof *d);
1089 strcpy(d->name, required);
1090 rc = api_array_add(&a->require.apis, d);
1097 * Declare that the api of name 'apiname' requires the class of name 'classname'.
1098 * Returns 0 if the declaration successed or -1 in case of failure
1099 * (ENOMEM: allocation failure, ENOENT: api name not found)
1101 int afb_apiset_require_class(struct afb_apiset *set, const char *apiname, const char *classname)
1103 struct api_desc *a = searchrec(set, apiname);
1104 struct api_class *c = class_search(classname, 1);
1105 return a && c ? api_array_add(&a->require.classes, c) : (errno = ENOENT, -1);
1109 * Declare that the api of name 'apiname' provides the class of name 'classname'
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_provide_class(struct afb_apiset *set, const char *apiname, const char *classname)
1115 struct api_desc *a = searchrec(set, apiname);
1116 struct api_class *c = class_search(classname, 1);
1117 return a && c ? api_array_add(&c->providers, a) : (errno = ENOENT, -1);
1121 * Start any API that provides the class of name 'classname'
1122 * The attribute 'share_session' is sent to the start function.
1124 int afb_apiset_class_start(const char *classname, int share_session)
1126 struct api_class *cla = class_search(classname, 0);
1127 return cla ? start_class(cla, share_session) : (errno = ENOENT, -1);