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 #include <afb/afb-req-itf.h>
36 #define INCR 8 /* CAUTION: must be a power of 2 */
39 * Internal description of an api
42 const char *name; /**< name of the api */
43 struct afb_api api; /**< handler of the api */
48 struct afb_apiset *subset;
49 struct api_desc *apis;
57 * Search the api of 'name'.
58 * @param set the api set
59 * @param name the api name to search
60 * @return the descriptor if found or NULL otherwise
62 static const struct api_desc *search(struct afb_apiset *set, const char *name)
65 const struct api_desc *a;
67 /* dichotomic search of the api */
72 /* check remaining slice */
77 /* check the mid of the slice */
80 c = strcasecmp(a->name, name);
85 /* update the slice */
94 * Checks wether 'name' is a valid API name.
95 * @return 1 if valid, 0 otherwise
97 int afb_apiset_valid_name(const char *name)
101 c = (unsigned char)*name;
103 /* empty names aren't valid */
107 if (c < (unsigned char)'\x80') {
125 c = (unsigned char)*++name;
130 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
133 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
137 void afb_apiset_unref(struct afb_apiset *set)
139 if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
140 afb_apiset_unref(set->subset);
149 struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset)
151 struct afb_apiset *set;
153 set = malloc((name ? strlen(name) : 0) + sizeof *set);
155 set->subset = afb_apiset_addref(subset);
156 set->apis = malloc(INCR * sizeof *set->apis);
158 set->timeout = timeout;
161 strcpy(set->name, name);
170 * Get the API timeout of the set
171 * @param set the api set
172 * @return the timeout in seconds
174 int afb_apiset_get_timeout(struct afb_apiset *set)
180 * Set the API timeout of the set
181 * @param set the api set
182 * @param to the timeout in seconds
184 void afb_apiset_set_timeout(struct afb_apiset *set, int to)
190 * Get the subset of the set
191 * @param set the api set
192 * @return the subset of set
194 struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set)
200 * Set the subset of the set
201 * @param set the api set
202 * @param subset the subset to set
204 void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset)
206 struct afb_apiset *tmp = set->subset;
207 set->subset = afb_apiset_addref(subset);
208 afb_apiset_unref(tmp);
212 * Adds the api of 'name' described by 'api'.
213 * @param set the api set
214 * @param name the name of the api to add (have to survive, not copied!)
216 * @returns 0 in case of success or -1 in case
217 * of error with errno set:
218 * - EINVAL if name isn't valid
219 * - EEXIST if name already registered
220 * - ENOMEM when out of memory
222 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
224 struct api_desc *apis;
227 /* Checks the api name */
228 if (!afb_apiset_valid_name(name)) {
229 ERROR("invalid api name forbidden (name is '%s')", name);
234 /* check previously existing plugin */
235 for (i = 0 ; i < set->count ; i++) {
236 c = strcasecmp(set->apis[i].name, name);
238 ERROR("api of name %s already exists", name);
246 /* allocates enough memory */
247 c = (set->count + INCR) & ~(INCR - 1);
248 apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
250 ERROR("out of memory");
256 /* copy higher part of the array */
259 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
261 /* record the plugin */
266 NOTICE("API %s added", name);
274 int afb_apiset_del(struct afb_apiset *set, const char *name)
279 for (i = 0 ; i < set->count ; i++) {
280 c = strcasecmp(set->apis[i].name, name);
283 while(i < set->count) {
284 set->apis[i] = set->apis[i + 1];
296 int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
298 const struct api_desc *i;
300 i = search(set, name);
306 return afb_apiset_get(set->subset, name, api);
313 * Starts a service by its 'api' name.
314 * @param set the api set
315 * @param name name of the service to start
316 * @param share_session if true start the servic"e in a shared session
317 * if false start it in its own session
318 * @param onneed if true start the service if possible, if false the api
320 * @return a positive number on success
322 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
324 const struct api_desc *a;
326 a = search(set, name);
328 ERROR("can't find service %s", name);
333 if (a->api.itf->service_start)
334 return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
339 /* already started: it is an error */
340 ERROR("The api %s is not a startable service", name);
346 * Starts all possible services but stops at first error.
347 * @param set the api set
348 * @param share_session if true start the servic"e in a shared session
349 * if false start it in its own session
350 * @return 0 on success or a negative number when an error is found
352 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
355 const struct api_desc *i, *e;
358 e = &set->apis[set->count];
360 if (i->api.itf->service_start) {
361 rc = i->api.itf->service_start(i->api.closure, share_session, 1, set);
371 * Ask to update the hook flags of the 'api'
372 * @param set the api set
373 * @param name the api to update (NULL updates all)
375 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
377 const struct api_desc *i, *e;
381 e = &set->apis[set->count];
383 i = search(set, name);
387 if (i->api.itf->update_hooks)
388 i->api.itf->update_hooks(i->api.closure);
394 * Set the verbosity level of the 'api'
395 * @param set the api set
396 * @param name the api to set (NULL set all)
398 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
400 const struct api_desc *i, *e;
404 e = &set->apis[set->count];
406 i = search(set, name);
410 if (i->api.itf->set_verbosity)
411 i->api.itf->set_verbosity(i->api.closure, level);
417 * Set the verbosity level of the 'api'
418 * @param set the api set
419 * @param name the api to set (NULL set all)
421 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
423 const struct api_desc *i;
425 i = name ? search(set, name) : NULL;
430 if (!i->api.itf->get_verbosity)
433 return i->api.itf->get_verbosity(i->api.closure);
437 * Get the list of api names
438 * @param set the api set
439 * @return a NULL terminated array of api names. Must be freed.
441 const char **afb_apiset_get_names(struct afb_apiset *set)
448 size = set->count * (1 + sizeof(*names)) + sizeof(*names);
449 for (i = 0 ; i < set->count ; i++)
450 size += strlen(set->apis[i].name);
452 names = malloc(size);
456 dest = (void*)&names[set->count+1];
457 for (i = 0 ; i < set->count ; i++) {
459 dest = stpcpy(dest, set->apis[i].name) + 1;
472 * Internal direct dispatch of the request 'xreq'
473 * @param set the api set
474 * @param xreq the request to dispatch
476 static void do_call_direct(struct afb_xreq *xreq)
478 const struct api_desc *a;
481 a = search(xreq->api);
483 afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
485 xreq->context.api_key = a->api.closure;
486 a->api.itf->call(a->api.closure, xreq);
491 * Asynchronous dispatch callback for the request 'xreq'
492 * @param set the api set
493 * @param signum 0 on normal flow or the signal number that interupted the normal flow
495 static void do_call_async(int signum, void *arg)
497 struct afb_xreq *xreq = arg;
500 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
502 do_call_direct(xreq);
504 afb_xreq_unref(xreq);
508 * Dispatch the request 'xreq' synchronously and directly.
509 * @param set the api set
510 * @param xreq the request to dispatch
512 void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq)
514 afb_xreq_begin(xreq);
515 do_call_direct(xreq);
519 * Dispatch the request 'xreq' asynchronously.
520 * @param set the api set
521 * @param xreq the request to dispatch
523 void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq)
527 afb_xreq_begin(xreq);
528 afb_xreq_addref(xreq);
529 rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq);
531 /* TODO: allows or not to proccess it directly as when no threading? (see above) */
532 ERROR("can't process job with threads: %m");
533 afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task");
534 afb_xreq_unref(xreq);