Enforce starting services on need
[src/app-framework-binder.git] / src / afb-apiset.c
index cf05446..f41c88c 100644 (file)
 /**
  * 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 */
 };
 
+/**
+ * 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_desc *apis;          /**< description of apis */
+       struct afb_apiset *subset;      /**< subset 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 */
+       char name[1];                   /**< name of the apiset */
 };
 
 /**
@@ -59,10 +64,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 */
@@ -91,6 +96,10 @@ static const struct api_desc *search(struct afb_apiset *set, const char *name)
 }
 
 /**
+ * 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,6 +107,11 @@ 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)
 {
        if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
@@ -109,18 +123,21 @@ void afb_apiset_unref(struct afb_apiset *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);
        if (set) {
-               set->subset = afb_apiset_addref(subset);
                set->apis = malloc(INCR * sizeof *set->apis);
                set->count = 0;
                set->timeout = timeout;
                set->refcount = 1;
+               set->subset = NULL;
                if (name)
                        strcpy(set->name, name);
                else
@@ -129,13 +146,22 @@ struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_a
        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
  * @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 +171,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 +181,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;
 }
@@ -165,9 +191,14 @@ struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *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)
+void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
 {
-       struct afb_apiset *tmp = set->subset;
+       struct afb_apiset *tmp;
+       if (subset == set) {
+               /* avoid infinite loop */
+               subset = NULL;
+       }
+       tmp = set->subset;
        set->subset = afb_apiset_addref(subset);
        afb_apiset_unref(tmp);
 }
@@ -179,7 +210,6 @@ 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
  */
@@ -188,13 +218,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);
@@ -223,6 +246,7 @@ 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++;
@@ -235,6 +259,12 @@ error:
        return -1;
 }
 
+/**
+ * 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)
 {
        int i, c;
@@ -257,7 +287,14 @@ 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'
+ * @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
+ */
+int afb_apiset_lookup(struct afb_apiset *set, const char *name, struct afb_api *api)
 {
        const struct api_desc *i;
 
@@ -266,13 +303,100 @@ int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api
                *api = i->api;
                return 0;
        }
-       if (set->subset)
-               return afb_apiset_get(set->subset, name, api);
 
        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 api the structure where to store data about the API of name
+ * @return 0 in case of success or -1 in case of error
+ */
+static struct api_desc *get_api(struct afb_apiset *set, const char *name)
+{
+       struct api_desc *i = search(set, name);
+       return i || !set->subset ? i : get_api(set->subset, name);
+}
+
+/**
+ * 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 api the structure where to store data about the API of name
+ * @return 0 in case of success or -1 in case of error
+ */
+int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
+{
+       const struct api_desc *i;
+
+       i = get_api(set, name);
+       if (!i) {
+               errno = ENOENT;
+               return -1;
+       }
+       *api = i->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)
+{
+       if (api->status == 0)
+               return 0;
+       else if (api->status > 0) {
+               errno = api->status;
+               return -1;
+       }
+
+       if (api->api.itf->service_start) {
+               api->status = EBUSY;
+               if (api->api.itf->service_start(api->api.closure, share_session, onneed, set) >= 0)
+                       api->status = 0;
+               else if (errno)
+                       api->status = errno ?: ECANCELED;
+       } else {
+               if (onneed)
+                       api->status = 0;
+               else {
+                       /* already started: it is an error */
+                       ERROR("The api %s is not a startable service", api->name);
+                       api->status = EINVAL;
+               }
+       }
+       return -!!api->status;
+}
+
+/**
+ * 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 api the structure where to store data about the API of name
+ * @return 0 in case of success or -1 in case of error
+ */
+int afb_apiset_get_started(struct afb_apiset *set, const char *name, struct afb_api *api)
+{
+       struct api_desc *i;
+
+       i = get_api(set, name);
+       if (!i) {
+               errno = ENOENT;
+               return -1;
+       }
+       *api = i->api;
+       return i->status ? start_api(set, i, 1, 1) : 0;
+}
+
 /**
  * Starts a service by its 'api' name.
  * @param set the api set
@@ -285,7 +409,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) {
@@ -294,16 +418,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);
 }
 
 /**
@@ -316,19 +431,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;
 }
 
 /**
@@ -392,7 +506,7 @@ int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
                return -1;
        }
        if (!i->api.itf->get_verbosity)
-               return 0;
+               return verbosity;
 
        return i->api.itf->get_verbosity(i->api.closure);
 }
@@ -427,77 +541,17 @@ const char **afb_apiset_get_names(struct afb_apiset *set)
        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'
+ * Enumerate the api names to a callback.
  * @param set the api set
- * @param signum 0 on normal flow or the signal number that interupted the normal flow
+ * @param callback the function to call for each name
+ * @param closure the closure for the callback
  */
-static void do_call_async(int signum, void *arg)
+void afb_apiset_enum(struct afb_apiset *set, void (*callback)(struct afb_apiset *set, const char *name, void *closure), void *closure)
 {
-       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;
+       int i;
 
-       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);
-       }
+       for (i = 0 ; i < set->count ; i++)
+               callback(set, set->apis[i].name, closure);
 }
 
-#endif
-