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"
34 #define INCR 8 /* CAUTION: must be a power of 2 */
37 * Internal description of an api
42 const char *name; /**< name of the api */
43 struct afb_api api; /**< handler of the api */
47 * Data structure for apiset
51 struct api_desc *apis; /**< description of apis */
52 struct afb_apiset *subset; /**< subset if any */
53 int count; /**< count of apis in the set */
54 int timeout; /**< the timeout in second for the apiset */
55 int refcount; /**< reference count for freeing resources */
56 char name[1]; /**< name of the apiset */
60 * Search the api of 'name'.
61 * @param set the api set
62 * @param name the api name to search
63 * @return the descriptor if found or NULL otherwise
65 static struct api_desc *search(struct afb_apiset *set, const char *name)
70 /* dichotomic search of the api */
75 /* check remaining slice */
80 /* check the mid of the slice */
83 c = strcasecmp(a->name, name);
88 /* update the slice */
97 * Increases the count of references to the apiset and return its address
98 * @param set the set whose reference count is to be increased
99 * @return the given apiset
101 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
104 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
109 * Decreases the count of references to the apiset and frees its
110 * resources when no more references exists.
111 * @param set the set to unrefrence
113 void afb_apiset_unref(struct afb_apiset *set)
115 if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
116 afb_apiset_unref(set->subset);
124 * @param name the name of the apiset
125 * @param timeout the default timeout in seconds for the apiset
126 * @return the created apiset or NULL in case of error
128 struct afb_apiset *afb_apiset_create(const char *name, int timeout)
130 struct afb_apiset *set;
132 set = malloc((name ? strlen(name) : 0) + sizeof *set);
134 set->apis = malloc(INCR * sizeof *set->apis);
136 set->timeout = timeout;
140 strcpy(set->name, name);
148 * the name of the apiset
149 * @param set the api set
150 * @return the name of the set
152 const char *afb_apiset_name(struct afb_apiset *set)
158 * Get the API timeout of the set
159 * @param set the api set
160 * @return the timeout in seconds
162 int afb_apiset_timeout_get(struct afb_apiset *set)
168 * Set the API timeout of the set
169 * @param set the api set
170 * @param to the timeout in seconds
172 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
178 * Get the subset of the set
179 * @param set the api set
180 * @return the subset of set
182 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
188 * Set the subset of the set
189 * @param set the api set
190 * @param subset the subset to set
192 void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
194 struct afb_apiset *tmp;
196 /* avoid infinite loop */
200 set->subset = afb_apiset_addref(subset);
201 afb_apiset_unref(tmp);
205 * Adds the api of 'name' described by 'api'.
206 * @param set the api set
207 * @param name the name of the api to add (have to survive, not copied!)
209 * @returns 0 in case of success or -1 in case
210 * of error with errno set:
211 * - EEXIST if name already registered
212 * - ENOMEM when out of memory
214 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
216 struct api_desc *apis;
219 /* check previously existing plugin */
220 for (i = 0 ; i < set->count ; i++) {
221 c = strcasecmp(set->apis[i].name, name);
223 ERROR("api of name %s already exists", name);
231 /* allocates enough memory */
232 c = (set->count + INCR) & ~(INCR - 1);
233 apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
235 ERROR("out of memory");
241 /* copy higher part of the array */
244 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
246 /* record the plugin */
252 NOTICE("API %s added", name);
261 * Delete from the 'set' the api of 'name'.
262 * @param set the set to be changed
263 * @param name the name of the API to remove
264 * @return 0 in case of success or -1 in case where the API doesn't exist.
266 int afb_apiset_del(struct afb_apiset *set, const char *name)
271 for (i = 0 ; i < set->count ; i++) {
272 c = strcasecmp(set->apis[i].name, name);
275 while(i < set->count) {
276 set->apis[i] = set->apis[i + 1];
289 * Get from the 'set' the API of 'name' in 'api'
290 * @param set the set of API
291 * @param name the name of the API to get
292 * @param api the structure where to store data about the API of name
293 * @return 0 in case of success or -1 in case of error
295 int afb_apiset_lookup(struct afb_apiset *set, const char *name, struct afb_api *api)
297 const struct api_desc *i;
299 i = search(set, name);
310 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
311 * @param set the set of API
312 * @param name the name of the API to get
313 * @param api the structure where to store data about the API of name
314 * @return 0 in case of success or -1 in case of error
316 static struct api_desc *get_api(struct afb_apiset *set, const char *name)
318 struct api_desc *i = search(set, name);
319 return i || !set->subset ? i : get_api(set->subset, name);
323 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
324 * @param set the set of API
325 * @param name the name of the API to get
326 * @param api the structure where to store data about the API of name
327 * @return 0 in case of success or -1 in case of error
329 int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
331 const struct api_desc *i;
333 i = get_api(set, name);
343 * Starts the service 'api'.
345 * @param share_session if true start the servic"e in a shared session
346 * if false start it in its own session
347 * @param onneed if true start the service if possible, if false the api
349 * @return a positive number on success
351 static int start_api(struct afb_apiset *set, struct api_desc *api, int share_session, int onneed)
355 if (api->status == 0)
357 else if (api->status > 0) {
362 NOTICE("API %s starting...", api->name);
363 if (api->api.itf->service_start) {
365 rc = api->api.itf->service_start(api->api.closure, share_session, onneed, set);
367 api->status = errno ?: ECANCELED;
368 ERROR("The api %s failed to start (%d)", api->name, rc);
371 } else if (!onneed) {
372 /* already started: it is an error */
373 ERROR("The api %s is not a startable service", api->name);
374 api->status = EINVAL;
377 NOTICE("API %s started", api->name);
383 * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
384 * @param set the set of API
385 * @param name the name of the API to get
386 * @param api the structure where to store data about the API of name
387 * @return 0 in case of success or -1 in case of error
389 int afb_apiset_get_started(struct afb_apiset *set, const char *name, struct afb_api *api)
393 i = get_api(set, name);
399 return i->status ? start_api(set, i, 1, 1) : 0;
403 * Starts a service by its 'api' name.
404 * @param set the api set
405 * @param name name of the service to start
406 * @param share_session if true start the servic"e in a shared session
407 * if false start it in its own session
408 * @param onneed if true start the service if possible, if false the api
410 * @return a positive number on success
412 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
416 a = search(set, name);
418 ERROR("can't find service %s", name);
423 return start_api(set, a, share_session, onneed);
427 * Starts all possible services but stops at first error.
428 * @param set the api set
429 * @param share_session if true start the servic"e in a shared session
430 * if false start it in its own session
431 * @return 0 on success or a negative number when an error is found
433 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
436 struct api_desc *i, *e;
439 e = &set->apis[set->count];
441 rc = start_api(set, i, share_session, 1);
447 return set->subset ? afb_apiset_start_all_services(set->subset, share_session) : 0;
451 * Ask to update the hook flags of the 'api'
452 * @param set the api set
453 * @param name the api to update (NULL updates all)
455 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
457 const struct api_desc *i, *e;
461 e = &set->apis[set->count];
463 i = search(set, name);
467 if (i->api.itf->update_hooks)
468 i->api.itf->update_hooks(i->api.closure);
474 * Set the verbosity level of the 'api'
475 * @param set the api set
476 * @param name the api to set (NULL set all)
478 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
480 const struct api_desc *i, *e;
484 e = &set->apis[set->count];
486 i = search(set, name);
490 if (i->api.itf->set_verbosity)
491 i->api.itf->set_verbosity(i->api.closure, level);
497 * Set the verbosity level of the 'api'
498 * @param set the api set
499 * @param name the api to set (NULL set all)
501 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
503 const struct api_desc *i;
505 i = name ? search(set, name) : NULL;
510 if (!i->api.itf->get_verbosity)
513 return i->api.itf->get_verbosity(i->api.closure);
517 * Get the list of api names
518 * @param set the api set
519 * @return a NULL terminated array of api names. Must be freed.
521 const char **afb_apiset_get_names(struct afb_apiset *set)
528 size = set->count * (1 + sizeof(*names)) + sizeof(*names);
529 for (i = 0 ; i < set->count ; i++)
530 size += strlen(set->apis[i].name);
532 names = malloc(size);
536 dest = (void*)&names[set->count+1];
537 for (i = 0 ; i < set->count ; i++) {
539 dest = stpcpy(dest, set->apis[i].name) + 1;
547 * Enumerate the api names to a callback.
548 * @param set the api set
549 * @param callback the function to call for each name
550 * @param closure the closure for the callback
552 void afb_apiset_enum(struct afb_apiset *set, void (*callback)(struct afb_apiset *set, const char *name, void *closure), void *closure)
556 for (i = 0 ; i < set->count ; i++)
557 callback(set, set->apis[i].name, closure);