Update copyright dates
[src/app-framework-binder.git] / src / afb-apiset.c
index af5a865..b4a57ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016, 2017, 2018 "IoT.bzh"
+ * Copyright (C) 2015-2020 "IoT.bzh"
  * Author José Bollo <jose.bollo@iot.bzh>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
 #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-context.h"
 #include "afb-xreq.h"
+#include "jobs.h"
 
-#define INCR 8         /* CAUTION: must be a power of 2 */
+#define INCR           8       /* CAUTION: must be a power of 2 */
+#define NOT_STARTED    -1
 
 struct afb_apiset;
 struct api_desc;
@@ -58,7 +60,10 @@ struct api_desc
 {
        struct api_desc *next;
        const char *name;               /**< name of the api */
-       int status;                     /**< initialisation status */
+       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;
@@ -73,7 +78,7 @@ struct api_alias
 {
        struct api_alias *next;
        struct api_desc *api;
-       char name[1];
+       char name[];
 };
 
 /**
@@ -83,7 +88,7 @@ struct api_class
 {
        struct api_class *next;
        struct api_array providers;
-       char name[1];
+       char name[];
 };
 
 /**
@@ -92,7 +97,7 @@ struct api_class
 struct api_depend
 {
        struct afb_apiset *set;
-       char name[1];
+       char name[];
 };
 
 /**
@@ -110,7 +115,7 @@ struct afb_apiset
        } onlack;                       /** not found handler */
        int timeout;                    /**< the timeout in second for the apiset */
        int refcount;                   /**< reference count for freeing resources */
-       char name[1];                   /**< name of the apiset */
+       char name[];                    /**< name of the apiset */
 };
 
 /**
@@ -215,7 +220,7 @@ static struct api_class *class_search(const char *name, int create)
        if (!create)
                return NULL;
 
-       c = calloc(1, strlen(name) + sizeof *c);
+       c = calloc(1, strlen(name) + 1 + sizeof *c);
        if (!c)
                errno = ENOMEM;
        else {
@@ -341,7 +346,7 @@ struct afb_apiset *afb_apiset_create(const char *name, int timeout)
 {
        struct afb_apiset *set;
 
-       set = calloc(1, (name ? strlen(name) : 0) + sizeof *set);
+       set = calloc(1, (name ? strlen(name) : 0) + 1 + sizeof *set);
        if (set) {
                set->timeout = timeout;
                set->refcount = 1;
@@ -427,17 +432,23 @@ struct afb_apiset *afb_apiset_subset_get(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_subset_set(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;
-       if (subset == set) {
-               /* avoid infinite loop */
-               subset = NULL;
-       }
+
+       /* 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*))
@@ -483,7 +494,7 @@ int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item
        if (!desc)
                goto oom;
 
-       desc->status = -1;
+       desc->status = NOT_STARTED;
        desc->api = api;
        desc->name = name;
 
@@ -495,7 +506,8 @@ int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item
        desc->next = all_apis;
        all_apis = desc;
 
-       INFO("API %s added", name);
+       if (afb_api_is_public(name))
+               INFO("API %s added", name);
 
        return 0;
 
@@ -538,7 +550,7 @@ int afb_apiset_add_alias(struct afb_apiset *set, const char *name, const char *a
        }
 
        /* allocates and init the struct */
-       ali = malloc(sizeof *ali + strlen(alias));
+       ali = malloc(sizeof *ali + strlen(alias) + 1);
        if (ali == NULL) {
                ERROR("out of memory");
                errno = ENOMEM;
@@ -693,21 +705,27 @@ const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char
        return NULL;
 }
 
-static int start_api(struct api_desc *api, int share_session, int onneed);
+static int start_api(struct api_desc *api);
 
 /**
  * Start the apis of the 'array'
- * The attribute 'share_session' is sent to the start function.
  */
-static int start_array_apis(struct api_array *array, int share_session)
+static int start_array_apis(struct api_array *array)
 {
        int i, rc = 0, rc2;
 
-       i = array->count;
-       while (i) {
-               rc2 = start_api(array->apis[--i], share_session, 1);
-               if (rc2 < 0) {
-                       rc = 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;
@@ -715,24 +733,22 @@ static int start_array_apis(struct api_array *array, int share_session)
 
 /**
  * Start the class 'cla' (start the apis that provide it).
- * The attribute 'share_session' is sent to the start function.
  */
-static int start_class(struct api_class *cla, int share_session)
+static int start_class(struct api_class *cla)
 {
-       return start_array_apis(&cla->providers, share_session);
+       return start_array_apis(&cla->providers);
 }
 
 /**
  * Start the classes of the 'array'
- * The attribute 'share_session' is sent to the start function.
  */
-static int start_array_classes(struct api_array *array, int share_session)
+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], share_session);
+               rc2 = start_class(array->classes[--i]);
                if (rc2 < 0) {
                        rc = rc2;
                }
@@ -742,67 +758,73 @@ static int start_array_classes(struct api_array *array, int share_session)
 
 /**
  * Start the depends of the 'array'
- * The attribute 'share_session' is sent to the start function.
  */
-static int start_array_depends(struct api_array *array, int share_session)
+static int start_array_depends(struct api_array *array)
 {
        struct api_desc *api;
        int i, rc = 0, rc2;
 
-       i = array->count;
-       while (i) {
-               i--;
+       i = 0;
+       while (i < array->count) {
                api = searchrec(array->depends[i]->set, array->depends[i]->name);
-               if (!api)
+               if (!api) {
                        rc = -1;
-               else {
-                       rc2 = start_api(api, share_session, 1);
-                       if (rc2 < 0) {
-                               rc = rc2;
+                       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
- * @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
  */
-static int start_api(struct api_desc *api, int share_session, int onneed)
+static int start_api(struct api_desc *api)
 {
        int rc;
 
-       if (api->status == 0)
+       if (api->status != NOT_STARTED) {
+               if (api->status > 0) {
+                       errno = api->status;
+                       return -1;
+               }
                return 0;
-       else if (api->status > 0) {
-               errno = api->status;
-               return -1;
        }
 
-       INFO("API %s starting...", api->name);
-       rc = start_array_classes(&api->require.classes, share_session);
-       rc = start_array_depends(&api->require.apis, share_session);
-       if (api->api.itf->service_start) {
-               api->status = EBUSY;
-               rc = api->api.itf->service_start(api->api.closure, share_session, onneed);
-               if (rc < 0) {
-                       api->status = errno ?: ECANCELED;
-                       ERROR("The api %s failed to start (%d)", api->name, rc);
-                       return -1;
+       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);
                }
-       } else if (!onneed) {
-               /* already started: it is an error */
-               ERROR("The api %s is not a startable service", api->name);
-               api->status = EINVAL;
+       }
+       if (rc < 0) {
+               api->status = errno ?: ECANCELED;
                return -1;
        }
-       NOTICE("API %s started", api->name);
+       INFO("API %s started", api->name);
        api->status = 0;
        return 0;
 }
@@ -812,30 +834,31 @@ static int start_api(struct api_desc *api, int share_session, int onneed)
  * @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
+ * @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 *i;
+       struct api_desc *desc;
 
-       i = lookup(set, name, rec);
-       if (i)
-               return i->status && start_api(i, 1, 1) ? NULL : &i->api;
-       errno = ENOENT;
-       return NULL;
+       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)
 {
        struct api_desc *a;
 
@@ -846,34 +869,44 @@ int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share
                return -1;
        }
 
-       return start_api(a, share_session, onneed);
+       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;
+       struct afb_apiset *rootset;
+       int rc, ret;
        int i;
 
+       rootset = set;
+       ret = 0;
        while (set) {
                i = 0;
                while (i < set->apis.count) {
-                       rc = start_api(set->apis.apis[i], share_session, 1);
-                       if (rc < 0)
-                               return rc;
-                       i++;
+                       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++;
+                       }
                }
                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
@@ -897,6 +930,7 @@ void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
                        d->api.itf->update_hooks(d->api.closure);
        }
 }
+#endif
 
 /**
  * Set the logmask of the 'api' to 'mask'
@@ -943,20 +977,25 @@ int afb_apiset_get_logmask(struct afb_apiset *set, const char *name)
        return i->api.itf->get_logmask(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)
+void afb_apiset_describe(struct afb_apiset *set, const char *name, void (*describecb)(void *, struct json_object *), void *closure)
 {
        const struct api_desc *i;
-
-       i = name ? searchrec(set, name) : NULL;
-       return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
+       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 {
@@ -1081,7 +1120,7 @@ int afb_apiset_require(struct afb_apiset *set, const char *name, const char *req
        if (!a)
                errno = ENOENT;
        else {
-               d = malloc(strlen(required) + sizeof *d);
+               d = malloc(strlen(required) + 1 + sizeof *d);
                if (!d)
                        errno = ENOMEM;
                else {
@@ -1119,11 +1158,10 @@ int afb_apiset_provide_class(struct afb_apiset *set, const char *apiname, const
 
 /**
  * Start any API that provides the class of name 'classname'
- * The attribute 'share_session' is sent to the start function.
  */
-int afb_apiset_class_start(const char *classname, int share_session)
+int afb_apiset_class_start(const char *classname)
 {
        struct api_class *cla = class_search(classname, 0);
-       return cla ? start_class(cla, share_session) : (errno = ENOENT, -1);
+       return cla ? start_class(cla) : (errno = ENOENT, -1);
 }