2 * Copyright (C) 2016, 2017 "IoT.bzh"
3 * Author "Fulup Ar Foll"
4 * Author José Bollo <jose.bollo@iot.bzh>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
26 #include "afb-session.h"
29 #include "afb-apiset.h"
30 #include "afb-context.h"
33 #define INCR 8 /* CAUTION: must be a power of 2 */
36 * Internal description of an api
41 const char *name; /**< name of the api */
42 struct afb_api api; /**< handler of the api */
46 * Data structure for apiset
50 struct api_desc *apis; /**< description of apis */
51 struct afb_apiset *subset; /**< subset if any */
52 int count; /**< count of apis in the set */
53 int timeout; /**< the timeout in second for the apiset */
54 int refcount; /**< reference count for freeing resources */
55 char name[1]; /**< name of the apiset */
59 * Search the api of 'name'.
60 * @param set the api set
61 * @param name the api name to search
62 * @return the descriptor if found or NULL otherwise
64 static struct api_desc *search(struct afb_apiset *set, const char *name)
69 /* dichotomic search of the api */
74 /* check remaining slice */
79 /* check the mid of the slice */
82 c = strcasecmp(a->name, name);
87 /* update the slice */
96 * Increases the count of references to the apiset and return its address
97 * @param set the set whose reference count is to be increased
98 * @return the given apiset
100 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
103 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
108 * Decreases the count of references to the apiset and frees its
109 * resources when no more references exists.
110 * @param set the set to unrefrence
112 void afb_apiset_unref(struct afb_apiset *set)
114 if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
115 afb_apiset_unref(set->subset);
123 * @param name the name of the apiset
124 * @param timeout the default timeout in seconds for the apiset
125 * @return the created apiset or NULL in case of error
127 struct afb_apiset *afb_apiset_create(const char *name, int timeout)
129 struct afb_apiset *set;
131 set = malloc((name ? strlen(name) : 0) + sizeof *set);
133 set->apis = malloc(INCR * sizeof *set->apis);
135 set->timeout = timeout;
139 strcpy(set->name, name);
147 * the name of the apiset
148 * @param set the api set
149 * @return the name of the set
151 const char *afb_apiset_name(struct afb_apiset *set)
157 * Get the API timeout of the set
158 * @param set the api set
159 * @return the timeout in seconds
161 int afb_apiset_timeout_get(struct afb_apiset *set)
167 * Set the API timeout of the set
168 * @param set the api set
169 * @param to the timeout in seconds
171 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
177 * Get the subset of the set
178 * @param set the api set
179 * @return the subset of set
181 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
187 * Set the subset of the set
188 * @param set the api set
189 * @param subset the subset to set
191 void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
193 struct afb_apiset *tmp;
195 /* avoid infinite loop */
199 set->subset = afb_apiset_addref(subset);
200 afb_apiset_unref(tmp);
204 * Adds the api of 'name' described by 'api'.
205 * @param set the api set
206 * @param name the name of the api to add (have to survive, not copied!)
208 * @returns 0 in case of success or -1 in case
209 * of error with errno set:
210 * - EEXIST if name already registered
211 * - ENOMEM when out of memory
213 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
215 struct api_desc *apis;
218 /* check previously existing plugin */
219 for (i = 0 ; i < set->count ; i++) {
220 c = strcasecmp(set->apis[i].name, name);
222 ERROR("api of name %s already exists", name);
230 /* allocates enough memory */
231 c = (set->count + INCR) & ~(INCR - 1);
232 apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
234 ERROR("out of memory");
240 /* copy higher part of the array */
243 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
245 /* record the plugin */
251 INFO("API %s added", name);
260 * Delete from the 'set' the api of 'name'.
261 * @param set the set to be changed
262 * @param name the name of the API to remove
263 * @return 0 in case of success or -1 in case where the API doesn't exist.
265 int afb_apiset_del(struct afb_apiset *set, const char *name)
270 for (i = 0 ; i < set->count ; i++) {
271 c = strcasecmp(set->apis[i].name, name);
274 while(i < set->count) {
275 set->apis[i] = set->apis[i + 1];
288 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
289 * @param set the set of API
290 * @param name the name of the API to get
291 * @param rec if not zero look also recursively in subsets
292 * @return the api pointer in case of success or NULL in case of error
294 static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
296 struct api_desc *i = search(set, name);
297 return i || !rec || !set->subset ? i : lookup(set->subset, name, rec);
301 * Get from the 'set' the API of 'name' in 'api'
302 * @param set the set of API
303 * @param name the name of the API to get
304 * @param rec if not zero look also recursively in subsets
305 * @return the api pointer in case of success or NULL in case of error
307 const struct afb_api *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
311 i = lookup(set, name, rec);
319 * Starts the service 'api'.
321 * @param share_session if true start the servic"e in a shared session
322 * if false start it in its own session
323 * @param onneed if true start the service if possible, if false the api
325 * @return a positive number on success
327 static int start_api(struct afb_apiset *set, struct api_desc *api, int share_session, int onneed)
331 if (api->status == 0)
333 else if (api->status > 0) {
338 INFO("API %s starting...", api->name);
339 if (api->api.itf->service_start) {
341 rc = api->api.itf->service_start(api->api.closure, share_session, onneed, set);
343 api->status = errno ?: ECANCELED;
344 ERROR("The api %s failed to start (%d)", api->name, rc);
347 } else if (!onneed) {
348 /* already started: it is an error */
349 ERROR("The api %s is not a startable service", api->name);
350 api->status = EINVAL;
353 NOTICE("API %s started", api->name);
359 * Get from the 'set' the API of 'name' in 'api'
360 * @param set the set of API
361 * @param name the name of the API to get
362 * @param rec if not zero look also recursively in subsets
363 * @return 0 in case of success or -1 in case of error
365 const struct afb_api *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
369 i = lookup(set, name, rec);
371 return i->status && start_api(set, i, 1, 1) ? NULL : &i->api;
377 * Starts a service by its 'api' name.
378 * @param set the api set
379 * @param name name of the service to start
380 * @param share_session if true start the servic"e in a shared session
381 * if false start it in its own session
382 * @param onneed if true start the service if possible, if false the api
384 * @return a positive number on success
386 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
390 a = search(set, name);
392 ERROR("can't find service %s", name);
397 return start_api(set, a, share_session, onneed);
401 * Starts all possible services but stops at first error.
402 * @param set the api set
403 * @param share_session if true start the servic"e in a shared session
404 * if false start it in its own session
405 * @return 0 on success or a negative number when an error is found
407 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
410 struct api_desc *i, *e;
413 e = &set->apis[set->count];
415 rc = start_api(set, i, share_session, 1);
421 return set->subset ? afb_apiset_start_all_services(set->subset, share_session) : 0;
425 * Ask to update the hook flags of the 'api'
426 * @param set the api set
427 * @param name the api to update (NULL updates all)
429 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
431 const struct api_desc *i, *e;
435 e = &set->apis[set->count];
437 i = search(set, name);
441 if (i->api.itf->update_hooks)
442 i->api.itf->update_hooks(i->api.closure);
448 * Set the verbosity level of the 'api'
449 * @param set the api set
450 * @param name the api to set (NULL set all)
452 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
454 const struct api_desc *i, *e;
458 e = &set->apis[set->count];
460 i = search(set, name);
464 if (i->api.itf->set_verbosity)
465 i->api.itf->set_verbosity(i->api.closure, level);
471 * Get the verbosity level of the 'api'
472 * @param set the api set
473 * @param name the api to get
474 * @return the verbosity level or -1 in case of error
476 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
478 const struct api_desc *i;
480 i = name ? search(set, name) : NULL;
486 if (!i->api.itf->get_verbosity)
489 return i->api.itf->get_verbosity(i->api.closure);
493 * Get the description of the API of 'name'
494 * @param set the api set
495 * @param name the api whose description is required
496 * @return the description or NULL
498 struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
500 const struct api_desc *i;
502 i = name ? search(set, name) : NULL;
503 return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
507 * Get the list of api names
508 * @param set the api set
509 * @return a NULL terminated array of api names. Must be freed.
511 const char **afb_apiset_get_names(struct afb_apiset *set)
518 size = set->count * (1 + sizeof(*names)) + sizeof(*names);
519 for (i = 0 ; i < set->count ; i++)
520 size += strlen(set->apis[i].name);
522 names = malloc(size);
526 dest = (void*)&names[set->count+1];
527 for (i = 0 ; i < set->count ; i++) {
529 dest = stpcpy(dest, set->apis[i].name) + 1;
537 * Enumerate the api names to a callback.
538 * @param set the api set
539 * @param callback the function to call for each name
540 * @param closure the closure for the callback
542 void afb_apiset_enum(struct afb_apiset *set, int rec, void (*callback)(struct afb_apiset *set, const char *name, void *closure), void *closure)
544 struct afb_apiset *iset;
545 struct api_desc *i, *e;
552 if (lookup(set, i->name, 1) == i)
553 callback(iset, i->name, closure);
556 iset = rec ? iset->subset : NULL;