Update copyright dates
[src/app-framework-binder.git] / src / afb-apiset.c
index cf05446..b4a57ee 100644 (file)
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2016, 2017 "IoT.bzh"
- * Author "Fulup Ar Foll"
+ * Copyright (C) 2015-2020 "IoT.bzh"
  * Author José Bollo <jose.bollo@iot.bzh>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,8 +21,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <stdint.h>
 
-#include "afb-session.h"
 #include "verbose.h"
 #include "afb-api.h"
 #include "afb-apiset.h"
 #include "afb-xreq.h"
 #include "jobs.h"
 
-#include <afb/afb-req-itf.h>
+#define INCR           8       /* CAUTION: must be a power of 2 */
+#define NOT_STARTED    -1
 
-#define INCR 8         /* CAUTION: must be a power of 2 */
+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 {
-       const char *name;       /**< name of the api */
-       struct afb_api api;     /**< handler of the api */
+struct api_desc
+{
+       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[];
 };
 
+/**
+ * Data structure for apiset
+ */
 struct afb_apiset
 {
-       struct afb_apiset *subset;
-       struct api_desc *apis;
-       int count;
-       int timeout;
-       int refcount;
-       char name[1];
+       struct api_array apis;          /**< the apis */
+       struct api_alias *aliases;      /**< the aliases */
+       struct afb_apiset *subset;      /**< subset if any */
+       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[];                    /**< 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
  * @param name the api name to search
  * @return the descriptor if found or NULL otherwise
  */
-static const struct api_desc *search(struct afb_apiset *set, const char *name)
+static struct api_desc *search(struct afb_apiset *set, const char *name)
 {
        int i, c, up, lo;
-       const struct api_desc *a;
+       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 */
@@ -88,9 +262,44 @@ static const 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;
+}
+
+/**
+ * Increases the count of references to the apiset and return its address
+ * @param set the set whose reference count is to be increased
+ * @return the given apiset
+ */
 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
 {
        if (set)
@@ -98,44 +307,103 @@ struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
        return set;
 }
 
+/**
+ * Decreases the count of references to the apiset and frees its
+ * resources when no more references exists.
+ * @param set the set to unrefrence
+ */
 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);
        }
 }
 
 /**
  * Create an apiset
+ * @param name the name of the apiset
+ * @param timeout the default timeout in seconds for the apiset
+ * @return the created apiset or NULL in case of error
  */
-struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset)
+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->subset = afb_apiset_addref(subset);
-               set->apis = malloc(INCR * sizeof *set->apis);
-               set->count = 0;
                set->timeout = timeout;
                set->refcount = 1;
                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
+ * @return the name of the set
+ */
+const char *afb_apiset_name(struct afb_apiset *set)
+{
+       return set->name;
+}
 
 /**
  * Get the API timeout of the set
  * @param set the api set
  * @return the timeout in seconds
  */
-int afb_apiset_get_timeout(struct afb_apiset *set)
+int afb_apiset_timeout_get(struct afb_apiset *set)
 {
        return set->timeout;
 }
@@ -145,7 +413,7 @@ int afb_apiset_get_timeout(struct afb_apiset *set)
  * @param set the api set
  * @param to the timeout in seconds
  */
-void afb_apiset_set_timeout(struct afb_apiset *set, int to)
+void afb_apiset_timeout_set(struct afb_apiset *set, int to)
 {
        set->timeout = to;
 }
@@ -155,7 +423,7 @@ void afb_apiset_set_timeout(struct afb_apiset *set, int to)
  * @param set the api set
  * @return the subset of set
  */
-struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set)
+struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
 {
        return set->subset;
 }
@@ -164,12 +432,32 @@ struct afb_apiset *afb_apiset_get_subset(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_set_subset(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 = set->subset;
+       struct afb_apiset *tmp;
+
+       /* 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;
 }
 
 /**
@@ -179,75 +467,191 @@ void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset)
  * @param api the api
  * @returns 0 in case of success or -1 in case
  * of error with errno set:
- *   - EINVAL if name isn't valid
  *   - 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;
 
-       /* Checks the api name */
-       if (!afb_api_is_valid_name(name)) {
-               ERROR("invalid api name forbidden (name is '%s')", name);
-               errno = EINVAL;
+       /* check whether it exists already */
+       if (search(set, name)) {
+               ERROR("api of name %s already exists", name);
+               errno = EEXIST;
                goto error;
        }
 
-       /* 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;
-               }
+       /* 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);
+       desc->next = all_apis;
+       all_apis = desc;
 
-       /* record the plugin */
-       apis->api = api;
-       apis->name = name;
-       set->count++;
-
-       NOTICE("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
+ * @param name the name of the API to remove
+ * @return 0 in case of success or -1 in case where the API doesn't exist.
+ */
 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)
@@ -257,80 +661,252 @@ int afb_apiset_del(struct afb_apiset *set, const char *name)
        return -1;
 }
 
-int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
+/**
+ * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
+ * @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 the api pointer in case of success or NULL in case of error
+ */
+static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
 {
-       const struct api_desc *i;
+       struct api_desc *result;
 
-       i = search(set, name);
-       if (i) {
-               *api = i->api;
-               return 0;
+       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);
        }
-       if (set->subset)
-               return afb_apiset_get(set->subset, name, api);
+       return result;
+}
 
+/**
+ * Get from the 'set' the API of 'name' in 'api'
+ * @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 the api pointer in case of success or NULL in case of error
+ */
+const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
+{
+       struct api_desc *i;
+
+       i = lookup(set, name, rec);
+       if (i)
+               return &i->api;
        errno = ENOENT;
-       return -1;
+       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
+ * @return zero on success, -1 on error
+ */
+static int start_api(struct api_desc *api)
+{
+       int rc;
+
+       if (api->status != NOT_STARTED) {
+               if (api->status > 0) {
+                       errno = api->status;
+                       return -1;
+               }
+               return 0;
+       }
+
+       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);
+               }
+       }
+       if (rc < 0) {
+               api->status = errno ?: ECANCELED;
+               return -1;
+       }
+       INFO("API %s started", api->name);
+       api->status = 0;
+       return 0;
+}
+
+/**
+ * Get from the 'set' the API of 'name' in 'api'
+ * @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 a pointer to the API item in case of success or NULL in case of error
+ */
+const struct afb_api_item *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
+{
+       struct api_desc *desc;
+
+       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)
 {
-       const struct api_desc *a;
+       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;
        }
 
-       if (a->api.itf->service_start)
-               return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
-
-       if (onneed)
-               return 0;
-
-       /* already started: it is an error */
-       ERROR("The api %s is not a startable service", name);
-       errno = EINVAL;
-       return -1;
+       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;
-       const 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) {
-               if (i->api.itf->service_start) {
-                       rc = i->api.itf->service_start(i->api.closure, share_session, 1, set);
-                       if (rc < 0)
-                               return rc;
+       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++;
+                       }
                }
-               i++;
+               set = set->subset;
        }
-       return 0;
+       return ret;
 }
 
+#if WITH_AFB_HOOK
 /**
  * Ask to update the hook flags of the 'api'
  * @param set the api set
@@ -338,166 +914,254 @@ 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);
        }
 }
 
 /**
- * Set the verbosity level of the 'api'
+ * Get the logmask level of the 'api'
  * @param set the api set
- * @param name the api to set (NULL set all)
+ * @param name the api to get
+ * @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 0;
 
-       return i->api.itf->get_verbosity(i->api.closure);
+       if (!i->api.itf->get_logmask)
+               return logmask;
+
+       return i->api.itf->get_logmask(i->api.closure);
+}
+
+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;
+
+       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;
 }
 
-
-#if 0
-
-
-
 /**
- * Internal direct dispatch of the request 'xreq'
+ * Enumerate the api names to a callback.
  * @param set the api set
- * @param xreq the request to dispatch
+ * @param rec should the enumeration be recursive
+ * @param callback the function to call for each name
+ * @param closure the closure for the callback
  */
-static void do_call_direct(struct afb_xreq *xreq)
+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)
 {
-       const struct api_desc *a;
+       int i;
+       struct afb_apiset *iset;
+       struct api_desc *d;
+       struct api_alias *a;
 
-       /* search the api */
-       a = search(xreq->api);
-       if (!a)
-               afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
-       else {
-               xreq->context.api_key = a->api.closure;
-               a->api.itf->call(a->api.closure, xreq);
+       iset = set;
+       while (iset) {
+               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;
        }
 }
 
 /**
- * Asynchronous dispatch callback for the request 'xreq'
- * @param set the api set
- * @param signum 0 on normal flow or the signal number that interupted the normal flow
+ * 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)
  */
-static void do_call_async(int signum, void *arg)
+int afb_apiset_require(struct afb_apiset *set, const char *name, const char *required)
 {
-       struct afb_xreq *xreq = arg;
+       struct api_desc *a;
+       struct api_depend *d;
+       int rc = -1;
 
-       if (signum != 0)
-               afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
+       a = searchrec(set, name);
+       if (!a)
+               errno = ENOENT;
        else {
-               do_call_direct(xreq);
+               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);
+               }
        }
-       afb_xreq_unref(xreq);
+       return rc;
 }
 
 /**
- * Dispatch the request 'xreq' synchronously and directly.
- * @param set the api set
- * @param xreq the request to dispatch
+ * 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)
  */
-void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq)
+int afb_apiset_require_class(struct afb_apiset *set, const char *apiname, const char *classname)
 {
-       afb_xreq_begin(xreq);
-       do_call_direct(xreq);
+       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);
 }
 
 /**
- * Dispatch the request 'xreq' asynchronously.
- * @param set the api set
- * @param xreq the request to dispatch
+ * 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)
  */
-void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq)
+int afb_apiset_provide_class(struct afb_apiset *set, const char *apiname, const char *classname)
 {
-       int rc;
-
-       afb_xreq_begin(xreq);
-       afb_xreq_addref(xreq);
-       rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq);
-       if (rc < 0) {
-               /* TODO: allows or not to proccess it directly as when no threading? (see above) */
-               ERROR("can't process job with threads: %m");
-               afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task");
-               afb_xreq_unref(xreq);
-       }
+       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);
 }
 
-#endif
+/**
+ * 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);
+}