Introduce apiset for grouping apis
[src/app-framework-binder.git] / src / afb-apiset.c
similarity index 53%
rename from src/afb-apis.c
rename to src/afb-apiset.c
index 6e09355..d9a4e5e 100644 (file)
 
 #include "afb-session.h"
 #include "verbose.h"
-#include "afb-apis.h"
+#include "afb-api.h"
+#include "afb-apiset.h"
 #include "afb-context.h"
 #include "afb-xreq.h"
 #include "jobs.h"
 
 #include <afb/afb-req-itf.h>
 
+#define INCR 8         /* CAUTION: must be a power of 2 */
+
 /**
  * Internal description of an api
  */
@@ -40,24 +43,58 @@ struct api_desc {
        struct afb_api api;     /**< handler of the api */
 };
 
-static struct api_desc *apis_array = NULL;
-static int apis_count = 0;
-static int apis_timeout = 15;
+struct afb_apiset
+{
+       struct afb_apiset *subset;
+       struct api_desc *apis;
+       int count;
+       int timeout;
+       int refcount;
+       char name[1];
+};
 
 /**
- * Set the API timeout
- * @param to the timeout in seconds
+ * 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
  */
-void afb_apis_set_timeout(int to)
+static const struct api_desc *search(struct afb_apiset *set, const char *name)
 {
-       apis_timeout = to;
+       int i, c, up, lo;
+       const struct api_desc *a;
+
+       /* dichotomic search of the api */
+       /* initial slice */
+       lo = 0;
+       up = set->count;
+       for (;;) {
+               /* check remaining slice */
+               if (lo >= up) {
+                       /* not found */
+                       return NULL;
+               }
+               /* check the mid of the slice */
+               i = (lo + up) >> 1;
+               a = &set->apis[i];
+               c = strcasecmp(a->name, name);
+               if (c == 0) {
+                       /* found */
+                       return a;
+               }
+               /* update the slice */
+               if (c < 0)
+                       lo = i + 1;
+               else
+                       up = i;
+       }
 }
 
 /**
  * Checks wether 'name' is a valid API name.
  * @return 1 if valid, 0 otherwise
  */
-int afb_apis_is_valid_api_name(const char *name)
+int afb_apiset_valid_name(const char *name)
 {
        unsigned char c;
 
@@ -90,8 +127,90 @@ int afb_apis_is_valid_api_name(const char *name)
        return 1;
 }
 
+struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
+{
+       if (set)
+               __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
+       return set;
+}
+
+void afb_apiset_unref(struct afb_apiset *set)
+{
+       if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
+               afb_apiset_unref(set->subset);
+               free(set->apis);
+               free(set);
+       }
+}
+
+/**
+ * Create an apiset
+ */
+struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset)
+{
+       struct afb_apiset *set;
+
+       set = malloc((name ? strlen(name) : 0) + 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;
+}
+
+
+/**
+ * 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)
+{
+       return set->timeout;
+}
+
+/**
+ * Set the API timeout of the set
+ * @param set the api set
+ * @param to the timeout in seconds
+ */
+void afb_apiset_set_timeout(struct afb_apiset *set, int to)
+{
+       set->timeout = to;
+}
+
+/**
+ * Get the subset of the set
+ * @param set the api set
+ * @return the subset of set
+ */
+struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set)
+{
+       return set->subset;
+}
+
+/**
+ * Set the subset of the set
+ * @param set the api set
+ * @param subset the subset to set
+ */
+void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset)
+{
+       struct afb_apiset *tmp = set->subset;
+       set->subset = afb_apiset_addref(subset);
+       afb_apiset_unref(tmp);
+}
+
 /**
  * Adds the api of 'name' described by 'api'.
+ * @param set the api set
  * @param name the name of the api to add (have to survive, not copied!)
  * @param api the api
  * @returns 0 in case of success or -1 in case
@@ -100,21 +219,21 @@ int afb_apis_is_valid_api_name(const char *name)
  *   - EEXIST if name already registered
  *   - ENOMEM when out of memory
  */
-int afb_apis_add(const char *name, struct afb_api api)
+int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
 {
        struct api_desc *apis;
        int i, c;
 
        /* Checks the api name */
-       if (!afb_apis_is_valid_api_name(name)) {
+       if (!afb_apiset_valid_name(name)) {
                ERROR("invalid api name forbidden (name is '%s')", name);
                errno = EINVAL;
                goto error;
        }
 
        /* check previously existing plugin */
-       for (i = 0 ; i < apis_count ; i++) {
-               c = strcasecmp(apis_array[i].name, name);
+       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;
@@ -125,26 +244,24 @@ int afb_apis_add(const char *name, struct afb_api api)
        }
 
        /* allocates enough memory */
-       apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis);
+       c = (set->count + INCR) & ~(INCR - 1);
+       apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
        if (apis == NULL) {
                ERROR("out of memory");
                errno = ENOMEM;
                goto error;
        }
-       apis_array = apis;
+       set->apis = apis;
 
        /* copy higher part of the array */
-       c = apis_count;
-       while (c > i) {
-               apis_array[c] = apis_array[c - 1];
-               c--;
-       }
+       apis += i;
+       if (i != set->count)
+               memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
 
        /* record the plugin */
-       apis = &apis_array[i];
        apis->api = api;
        apis->name = name;
-       apis_count++;
+       set->count++;
 
        NOTICE("API %s added", name);
 
@@ -154,158 +271,116 @@ error:
        return -1;
 }
 
-/**
- * Search the 'api'.
- * @param api the api of the verb
- * @return the descriptor if found or NULL otherwise
- */
-static const struct api_desc *search(const char *api)
+int afb_apiset_del(struct afb_apiset *set, const char *name)
 {
-       int i, c, up, lo;
-       const struct api_desc *a;
+       int i, c;
 
-       /* dichotomic search of the api */
-       /* initial slice */
-       lo = 0;
-       up = apis_count;
-       for (;;) {
-               /* check remaining slice */
-               if (lo >= up) {
-                       /* not found */
-                       return NULL;
-               }
-               /* check the mid of the slice */
-               i = (lo + up) >> 1;
-               a = &apis_array[i];
-               c = strcasecmp(a->name, api);
+       /* search the api */
+       for (i = 0 ; i < set->count ; i++) {
+               c = strcasecmp(set->apis[i].name, name);
                if (c == 0) {
-                       /* found */
-                       return a;
+                       set->count--;
+                       while(i < set->count) {
+                               set->apis[i] = set->apis[i + 1];
+                               i++;
+                       }
+                       return 0;
                }
-               /* update the slice */
-               if (c < 0)
-                       lo = i + 1;
-               else
-                       up = i;
+               if (c > 0)
+                       break;
        }
+       errno = ENOENT;
+       return -1;
 }
 
-/**
- * Starts a service by its 'api' name.
- * @param api 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
- */
-int afb_apis_start_service(const char *api, int share_session, int onneed)
+int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
 {
-       int i;
+       const struct api_desc *i;
 
-       for (i = 0 ; i < apis_count ; i++) {
-               if (!strcasecmp(apis_array[i].name, api))
-                       return apis_array[i].api.itf->service_start(apis_array[i].api.closure, share_session, onneed);
+       i = search(set, name);
+       if (i) {
+               *api = i->api;
+               return 0;
        }
-       ERROR("can't find service %s", api);
+       if (set->subset)
+               return afb_apiset_get(set->subset, name, api);
+
        errno = ENOENT;
        return -1;
 }
 
 /**
- * Starts all possible services but stops at first error.
+ * 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
- * @return 0 on success or a negative number when an error is found
- */
-int afb_apis_start_all_services(int share_session)
-{
-       int i, rc;
-
-       for (i = 0 ; i < apis_count ; i++) {
-               rc = apis_array[i].api.itf->service_start(apis_array[i].api.closure, share_session, 1);
-               if (rc < 0)
-                       return rc;
-       }
-       return 0;
-}
-
-/**
- * Internal direct dispatch of the request 'xreq'
- * @param xreq the request to dispatch
+ * @param onneed if true start the service if possible, if false the api
+ *               must be a service
+ * @return a positive number on success
  */
-static void do_call_direct(struct afb_xreq *xreq)
+int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
 {
        const struct api_desc *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);
+       a = search(set, name);
+       if (!a) {
+               ERROR("can't find service %s", name);
+               errno = ENOENT;
+               return -1;
        }
-}
 
-/**
- * Asynchronous dispatch callback for the request 'xreq'
- * @param signum 0 on normal flow or the signal number that interupted the normal flow
- */
-static void do_call_async(int signum, void *arg)
-{
-       struct afb_xreq *xreq = arg;
+       if (a->api.itf->service_start)
+               return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
 
-       if (signum != 0)
-               afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
-       else {
-               do_call_direct(xreq);
-       }
-       afb_xreq_unref(xreq);
-}
+       if (onneed)
+               return 0;
 
-/**
- * Dispatch the request 'xreq' synchronously and directly.
- * @param xreq the request to dispatch
- */
-void afb_apis_call_direct(struct afb_xreq *xreq)
-{
-       afb_xreq_begin(xreq);
-       do_call_direct(xreq);
+       /* already started: it is an error */
+       ERROR("The api %s is not a startable service", name);
+       errno = EINVAL;
+       return -1;
 }
 
 /**
- * Dispatch the request 'xreq' asynchronously.
- * @param xreq the request to dispatch
+ * 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
  */
-void afb_apis_call(struct afb_xreq *xreq)
+int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
 {
        int rc;
+       const struct api_desc *i, *e;
 
-       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);
+       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;
+               }
+               i++;
        }
+       return 0;
 }
 
 /**
  * Ask to update the hook flags of the 'api'
- * @param api the api to update (NULL updates all)
+ * @param set the api set
+ * @param name the api to update (NULL updates all)
  */
-void afb_apis_update_hooks(const char *api)
+void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
 {
        const struct api_desc *i, *e;
 
-       if (!api) {
-               i = apis_array;
-               e = &apis_array[apis_count];
+       if (!name) {
+               i = set->apis;
+               e = &set->apis[set->count];
        } else {
-               i = search(api);
+               i = search(set, name);
                e = &i[!!i];
        }
        while (i != e) {
@@ -317,17 +392,18 @@ void afb_apis_update_hooks(const char *api)
 
 /**
  * Set the verbosity level of the 'api'
- * @param api the api to set (NULL set all)
+ * @param set the api set
+ * @param name the api to set (NULL set all)
  */
-void afb_apis_set_verbosity(const char *api, int level)
+void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
 {
        const struct api_desc *i, *e;
 
-       if (!api) {
-               i = apis_array;
-               e = &apis_array[apis_count];
+       if (!name) {
+               i = set->apis;
+               e = &set->apis[set->count];
        } else {
-               i = search(api);
+               i = search(set, name);
                e = &i[!!i];
        }
        while (i != e) {
@@ -339,13 +415,14 @@ void afb_apis_set_verbosity(const char *api, int level)
 
 /**
  * Set the verbosity level of the 'api'
- * @param api the api to set (NULL set all)
+ * @param set the api set
+ * @param name the api to set (NULL set all)
  */
-int afb_apis_get_verbosity(const char *api)
+int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
 {
        const struct api_desc *i;
 
-       i = api ? search(api) : NULL;
+       i = name ? search(set, name) : NULL;
        if (!i) {
                errno = ENOENT;
                return -1;
@@ -358,30 +435,105 @@ int afb_apis_get_verbosity(const char *api)
 
 /**
  * Get the list of api names
+ * @param set the api set
  * @return a NULL terminated array of api names. Must be freed.
  */
-const char **afb_apis_get_names()
+const char **afb_apiset_get_names(struct afb_apiset *set)
 {
        size_t size;
        char *dest;
        const char **names;
        int i;
 
-       size = apis_count * (1 + sizeof(*names)) + sizeof(*names);
-       for (i = 0 ; i < apis_count ; i++)
-               size += strlen(apis_array[i].name);
+       size = set->count * (1 + sizeof(*names)) + sizeof(*names);
+       for (i = 0 ; i < set->count ; i++)
+               size += strlen(set->apis[i].name);
 
        names = malloc(size);
        if (!names)
                errno = ENOMEM;
        else {
-               dest = (void*)&names[apis_count+1];
-               for (i = 0 ; i < apis_count ; i++) {
+               dest = (void*)&names[set->count+1];
+               for (i = 0 ; i < set->count ; i++) {
                        names[i] = dest;
-                       dest = stpcpy(dest, apis_array[i].name) + 1;
+                       dest = stpcpy(dest, set->apis[i].name) + 1;
                }
                names[i] = NULL;
        }
        return names;
 }
 
+
+#if 0
+
+
+
+/**
+ * Internal direct dispatch of the request 'xreq'
+ * @param set the api set
+ * @param xreq the request to dispatch
+ */
+static void do_call_direct(struct afb_xreq *xreq)
+{
+       const struct api_desc *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);
+       }
+}
+
+/**
+ * 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
+ */
+static void do_call_async(int signum, void *arg)
+{
+       struct afb_xreq *xreq = arg;
+
+       if (signum != 0)
+               afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
+       else {
+               do_call_direct(xreq);
+       }
+       afb_xreq_unref(xreq);
+}
+
+/**
+ * Dispatch the request 'xreq' synchronously and directly.
+ * @param set the api set
+ * @param xreq the request to dispatch
+ */
+void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq)
+{
+       afb_xreq_begin(xreq);
+       do_call_direct(xreq);
+}
+
+/**
+ * Dispatch the request 'xreq' asynchronously.
+ * @param set the api set
+ * @param xreq the request to dispatch
+ */
+void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq)
+{
+       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);
+       }
+}
+
+#endif
+