afb-apiset: manage deletion of apis
[src/app-framework-binder.git] / src / afb-apiset.c
index c17881e..8d76bbf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016, 2017 "IoT.bzh"
+ * Copyright (C) 2016, 2017, 2018 "IoT.bzh"
  * Author "Fulup Ar Foll"
  * Author José Bollo <jose.bollo@iot.bzh>
  *
 #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
  */
-struct api_desc {
+struct api_desc
+{
+       int status;
        const char *name;       /**< name of the api */
        struct afb_api api;     /**< handler of the api */
 };
@@ -50,7 +49,6 @@ struct afb_apiset
 {
        struct api_desc *apis;          /**< description of apis */
        struct afb_apiset *subset;      /**< subset if any */
-       struct afb_api defapi;          /**< default api if any */
        int count;                      /**< count of apis in the set */
        int timeout;                    /**< the timeout in second for the apiset */
        int refcount;                   /**< reference count for freeing resources */
@@ -63,10 +61,10 @@ struct afb_apiset
  * @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;
 
        /* dichotomic search of the api */
        /* initial slice */
@@ -137,7 +135,6 @@ struct afb_apiset *afb_apiset_create(const char *name, int timeout)
                set->timeout = timeout;
                set->refcount = 1;
                set->subset = NULL;
-               set->defapi.itf = NULL;
                if (name)
                        strcpy(set->name, name);
                else
@@ -146,6 +143,15 @@ struct afb_apiset *afb_apiset_create(const char *name, int timeout)
        return set;
 }
 
+/**
+ * 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
@@ -194,51 +200,6 @@ void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
        afb_apiset_unref(tmp);
 }
 
-/**
- * Check if the apiset has a default api
- * @param set the api set
- * @return 1 if the set has a default api or 0 otherwise
- */
-int afb_apiset_default_api_exist(struct afb_apiset *set)
-{
-       return !!set->defapi.itf;
-}
-
-/**
- * Get the default api of the api set.
- * @param set the api set
- * @param api where to store the default api
- * @return 0 in case of success or -1 when no default api is set
- */
-int afb_apiset_default_api_get(struct afb_apiset *set, struct afb_api *api)
-{
-       if (set->defapi.itf) {
-               *api = set->defapi;
-               return 0;
-       }
-       errno = ENOENT;
-       return -1;
-}
-
-/**
- * Set the default api of the api set
- * @param set the api set
- * @param subset the subset to set
- */
-void afb_apiset_default_api_set(struct afb_apiset *set, struct afb_api api)
-{
-       set->defapi = api;
-}
-
-/**
- * Set the default api of the api set
- * @param set the api set
- */
-void afb_apiset_default_api_drop(struct afb_apiset *set)
-{
-       set->defapi.itf = NULL;
-}
-
 /**
  * Adds the api of 'name' described by 'api'.
  * @param set the api set
@@ -246,7 +207,6 @@ void afb_apiset_default_api_drop(struct afb_apiset *set)
  * @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
  */
@@ -255,13 +215,6 @@ 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_api_is_valid_name(name)) {
-               ERROR("invalid api name forbidden (name is '%s')", name);
-               errno = EINVAL;
-               goto error;
-       }
-
        /* check previously existing plugin */
        for (i = 0 ; i < set->count ; i++) {
                c = strcasecmp(set->apis[i].name, name);
@@ -290,11 +243,12 @@ int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
                memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
 
        /* record the plugin */
+       apis->status = -1;
        apis->api = api;
        apis->name = name;
        set->count++;
 
-       NOTICE("API %s added", name);
+       INFO("API %s added", name);
 
        return 0;
 
@@ -310,53 +264,120 @@ error:
  */
 int afb_apiset_del(struct afb_apiset *set, const char *name)
 {
-       int i, c;
+       struct api_desc *i, *e;
+       int c;
 
        /* search the api */
-       for (i = 0 ; i < set->count ; i++) {
-               c = strcasecmp(set->apis[i].name, name);
+       i = set->apis;
+       e = i + set->count;
+       while (i != e) {
+               c = strcasecmp(i->name, name);
                if (c == 0) {
+                       if (i->api.itf->unref)
+                               i->api.itf->unref(i->api.closure);
                        set->count--;
-                       while(i < set->count) {
-                               set->apis[i] = set->apis[i + 1];
+                       e--;
+                       while (i != e) {
+                               i[0] = i[1];
                                i++;
                        }
                        return 0;
                }
                if (c > 0)
                        break;
+               i++;
        }
        errno = ENOENT;
        return -1;
 }
 
+/**
+ * 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)
+{
+       struct api_desc *i = search(set, name);
+       return i || !rec || !set->subset ? i : lookup(set->subset, name, rec);
+}
+
 /**
  * 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 api the structure where to store data about the API of name
- * @return 0 in case of success or -1 in case of error
+ * @param rec if not zero look also recursively in subsets
+ * @return the api pointer in case of success or NULL in case of error
  */
-int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
+const struct afb_api *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
 {
-       const struct api_desc *i;
+       struct api_desc *i;
 
-       i = search(set, name);
-       if (i) {
-               *api = i->api;
-               return 0;
-       }
+       i = lookup(set, name, rec);
+       if (i)
+               return &i->api;
+       errno = ENOENT;
+       return NULL;
+}
 
-       if (set->subset && 0 == afb_apiset_get(set->subset, name, api))
-               return 0;
+/**
+ * Starts the service 'api'.
+ * @param api the api
+ * @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
+ */
+static int start_api(struct afb_apiset *set, struct api_desc *api, int share_session, int onneed)
+{
+       int rc;
 
-       if (set->defapi.itf) {
-               *api = set->defapi;
+       if (api->status == 0)
                return 0;
+       else if (api->status > 0) {
+               errno = api->status;
+               return -1;
        }
 
+       INFO("API %s starting...", api->name);
+       if (api->api.itf->service_start) {
+               api->status = EBUSY;
+               rc = api->api.itf->service_start(api->api.closure, share_session, onneed, set);
+               if (rc < 0) {
+                       api->status = errno ?: ECANCELED;
+                       ERROR("The api %s failed to start (%d)", api->name, rc);
+                       return -1;
+               }
+       } else if (!onneed) {
+               /* already started: it is an error */
+               ERROR("The api %s is not a startable service", api->name);
+               api->status = EINVAL;
+               return -1;
+       }
+       NOTICE("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 0 in case of success or -1 in case of error
+ */
+const struct afb_api *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
+{
+       struct api_desc *i;
+
+       i = lookup(set, name, rec);
+       if (i)
+               return i->status && start_api(set, i, 1, 1) ? NULL : &i->api;
        errno = ENOENT;
-       return -1;
+       return NULL;
 }
 
 /**
@@ -371,7 +392,7 @@ int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api
  */
 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
 {
-       const struct api_desc *a;
+       struct api_desc *a;
 
        a = search(set, name);
        if (!a) {
@@ -380,16 +401,7 @@ int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share
                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(set, a, share_session, onneed);
 }
 
 /**
@@ -402,19 +414,18 @@ int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share
 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
 {
        int rc;
-       const struct api_desc *i, *e;
+       struct api_desc *i, *e;
 
        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;
-               }
+               rc = start_api(set, i, share_session, 1);
+               if (rc < 0)
+                       return rc;
                i++;
        }
-       return 0;
+
+       return set->subset ? afb_apiset_start_all_services(set->subset, share_session) : 0;
 }
 
 /**
@@ -464,9 +475,10 @@ void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int leve
 }
 
 /**
- * Set the verbosity level of the 'api'
+ * Get the verbosity 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 verbosity level or -1 in case of error
  */
 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
 {
@@ -477,12 +489,27 @@ int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
                errno = ENOENT;
                return -1;
        }
+
        if (!i->api.itf->get_verbosity)
-               return 0;
+               return verbosity;
 
        return i->api.itf->get_verbosity(i->api.closure);
 }
 
+/**
+ * Get the description of the API of 'name'
+ * @param set the api set
+ * @param name the api whose description is required
+ * @return the description or NULL
+ */
+struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
+{
+       const struct api_desc *i;
+
+       i = name ? search(set, name) : NULL;
+       return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
+}
+
 /**
  * Get the list of api names
  * @param set the api set
@@ -513,3 +540,27 @@ const char **afb_apiset_get_names(struct afb_apiset *set)
        return names;
 }
 
+/**
+ * Enumerate the api names to a callback.
+ * @param set the api set
+ * @param callback the function to call for each name
+ * @param closure the closure for the callback
+ */
+void afb_apiset_enum(struct afb_apiset *set, int rec, void (*callback)(struct afb_apiset *set, const char *name, void *closure), void *closure)
+{
+       struct afb_apiset *iset;
+       struct api_desc *i, *e;
+
+       iset = set;
+       while (iset) {
+               i = iset->apis;
+               e = &i[iset->count];
+               while (i != e) {
+                       if (lookup(set, i->name, 1) == i)
+                               callback(iset, i->name, closure);
+                       i++;
+               }
+               iset = rec ? iset->subset : NULL;
+       }
+}
+