#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 */
};
+/**
+ * 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 */
};
/**
* @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 */
}
/**
- * Checks wether 'name' is a valid API name.
- * @return 1 if valid, 0 otherwise
+ * 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
*/
-int afb_apiset_valid_name(const char *name)
-{
- unsigned char c;
-
- c = (unsigned char)*name;
- if (c == 0)
- /* empty names aren't valid */
- return 0;
-
- do {
- if (c < (unsigned char)'\x80') {
- switch(c) {
- default:
- if (c > ' ')
- break;
- case '"':
- case '#':
- case '%':
- case '&':
- case '\'':
- case '/':
- case '?':
- case '`':
- case '\\':
- case '\x7f':
- return 0;
- }
- }
- c = (unsigned char)*++name;
- } while(c != 0);
- return 1;
-}
-
struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
{
if (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)) {
/**
* 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
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;
}
* @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;
}
* @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;
}
* @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);
}
* @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
*/
struct api_desc *apis;
int i, c;
- /* Checks the api 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 < set->count ; i++) {
c = strcasecmp(set->apis[i].name, name);
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;
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;
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 *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 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 *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 NULL;
+}
+
+/**
+ * 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;
- i = search(set, name);
- if (i) {
- *api = i->api;
+ 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;
}
- if (set->subset)
- return afb_apiset_get(set->subset, name, api);
+ 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;
}
/**
*/
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) {
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);
}
/**
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;
}
/**
}
/**
- * 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)
{
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
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 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)(struct afb_apiset *set, const char *name, void *closure), void *closure)
{
- 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);
+ 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;
}
}
-#endif
-