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 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
97 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
101 void afb_apiset_unref(struct afb_apiset *set)
103 if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
104 afb_apiset_unref(set->subset);
113 struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset)
115 struct afb_apiset *set;
117 set = malloc((name ? strlen(name) : 0) + sizeof *set);
119 set->subset = afb_apiset_addref(subset);
120 set->apis = malloc(INCR * sizeof *set->apis);
122 set->timeout = timeout;
125 strcpy(set->name, name);
134 * Get the API timeout of the set
135 * @param set the api set
136 * @return the timeout in seconds
138 int afb_apiset_get_timeout(struct afb_apiset *set)
144 * Set the API timeout of the set
145 * @param set the api set
146 * @param to the timeout in seconds
148 void afb_apiset_set_timeout(struct afb_apiset *set, int to)
154 * Get the subset of the set
155 * @param set the api set
156 * @return the subset of set
158 struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set)
164 * Set the subset of the set
165 * @param set the api set
166 * @param subset the subset to set
168 void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset)
170 struct afb_apiset *tmp = set->subset;
171 set->subset = afb_apiset_addref(subset);
172 afb_apiset_unref(tmp);
176 * Adds the api of 'name' described by 'api'.
177 * @param set the api set
178 * @param name the name of the api to add (have to survive, not copied!)
180 * @returns 0 in case of success or -1 in case
181 * of error with errno set:
182 * - EINVAL if name isn't valid
183 * - EEXIST if name already registered
184 * - ENOMEM when out of memory
186 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
188 struct api_desc *apis;
191 /* Checks the api name */
192 if (!afb_api_is_valid_name(name)) {
193 ERROR("invalid api name forbidden (name is '%s')", name);
198 /* check previously existing plugin */
199 for (i = 0 ; i < set->count ; i++) {
200 c = strcasecmp(set->apis[i].name, name);
202 ERROR("api of name %s already exists", name);
210 /* allocates enough memory */
211 c = (set->count + INCR) & ~(INCR - 1);
212 apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
214 ERROR("out of memory");
220 /* copy higher part of the array */
223 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
225 /* record the plugin */
230 NOTICE("API %s added", name);
238 int afb_apiset_del(struct afb_apiset *set, const char *name)
243 for (i = 0 ; i < set->count ; i++) {
244 c = strcasecmp(set->apis[i].name, name);
247 while(i < set->count) {
248 set->apis[i] = set->apis[i + 1];
260 int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
262 const struct api_desc *i;
264 i = search(set, name);
270 return afb_apiset_get(set->subset, name, api);
277 * Starts a service by its 'api' name.
278 * @param set the api set
279 * @param name name of the service to start
280 * @param share_session if true start the servic"e in a shared session
281 * if false start it in its own session
282 * @param onneed if true start the service if possible, if false the api
284 * @return a positive number on success
286 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
288 const struct api_desc *a;
290 a = search(set, name);
292 ERROR("can't find service %s", name);
297 if (a->api.itf->service_start)
298 return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
303 /* already started: it is an error */
304 ERROR("The api %s is not a startable service", name);
310 * Starts all possible services but stops at first error.
311 * @param set the api set
312 * @param share_session if true start the servic"e in a shared session
313 * if false start it in its own session
314 * @return 0 on success or a negative number when an error is found
316 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
319 const struct api_desc *i, *e;
322 e = &set->apis[set->count];
324 if (i->api.itf->service_start) {
325 rc = i->api.itf->service_start(i->api.closure, share_session, 1, set);
335 * Ask to update the hook flags of the 'api'
336 * @param set the api set
337 * @param name the api to update (NULL updates all)
339 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
341 const struct api_desc *i, *e;
345 e = &set->apis[set->count];
347 i = search(set, name);
351 if (i->api.itf->update_hooks)
352 i->api.itf->update_hooks(i->api.closure);
358 * Set the verbosity level of the 'api'
359 * @param set the api set
360 * @param name the api to set (NULL set all)
362 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
364 const struct api_desc *i, *e;
368 e = &set->apis[set->count];
370 i = search(set, name);
374 if (i->api.itf->set_verbosity)
375 i->api.itf->set_verbosity(i->api.closure, level);
381 * Set the verbosity level of the 'api'
382 * @param set the api set
383 * @param name the api to set (NULL set all)
385 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
387 const struct api_desc *i;
389 i = name ? search(set, name) : NULL;
394 if (!i->api.itf->get_verbosity)
397 return i->api.itf->get_verbosity(i->api.closure);
401 * Get the list of api names
402 * @param set the api set
403 * @return a NULL terminated array of api names. Must be freed.
405 const char **afb_apiset_get_names(struct afb_apiset *set)
412 size = set->count * (1 + sizeof(*names)) + sizeof(*names);
413 for (i = 0 ; i < set->count ; i++)
414 size += strlen(set->apis[i].name);
416 names = malloc(size);
420 dest = (void*)&names[set->count+1];
421 for (i = 0 ; i < set->count ; i++) {
423 dest = stpcpy(dest, set->apis[i].name) + 1;
436 * Internal direct dispatch of the request 'xreq'
437 * @param set the api set
438 * @param xreq the request to dispatch
440 static void do_call_direct(struct afb_xreq *xreq)
442 const struct api_desc *a;
445 a = search(xreq->api);
447 afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
449 xreq->context.api_key = a->api.closure;
450 a->api.itf->call(a->api.closure, xreq);
455 * Asynchronous dispatch callback for the request 'xreq'
456 * @param set the api set
457 * @param signum 0 on normal flow or the signal number that interupted the normal flow
459 static void do_call_async(int signum, void *arg)
461 struct afb_xreq *xreq = arg;
464 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
466 do_call_direct(xreq);
468 afb_xreq_unref(xreq);
472 * Dispatch the request 'xreq' synchronously and directly.
473 * @param set the api set
474 * @param xreq the request to dispatch
476 void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq)
478 afb_xreq_begin(xreq);
479 do_call_direct(xreq);
483 * Dispatch the request 'xreq' asynchronously.
484 * @param set the api set
485 * @param xreq the request to dispatch
487 void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq)
491 afb_xreq_begin(xreq);
492 afb_xreq_addref(xreq);
493 rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq);
495 /* TODO: allows or not to proccess it directly as when no threading? (see above) */
496 ERROR("can't process job with threads: %m");
497 afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task");
498 afb_xreq_unref(xreq);