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-context.h"
33 #include <afb/afb-req-itf.h>
36 * Internal description of an api
39 const char *name; /**< name of the api */
40 struct afb_api api; /**< handler of the api */
43 static struct api_desc *apis_array = NULL;
44 static int apis_count = 0;
45 static int apis_timeout = 15;
49 * @param to the timeout in seconds
51 void afb_apis_set_timeout(int to)
57 * Checks wether 'name' is a valid API name.
58 * @return 1 if valid, 0 otherwise
60 int afb_apis_is_valid_api_name(const char *name)
64 c = (unsigned char)*name;
66 /* empty names aren't valid */
70 if (c < (unsigned char)'\x80') {
88 c = (unsigned char)*++name;
94 * Adds the api of 'name' described by 'api'.
95 * @param name the name of the api to add (have to survive, not copied!)
97 * @returns 0 in case of success or -1 in case
98 * of error with errno set:
99 * - EINVAL if name isn't valid
100 * - EEXIST if name already registered
101 * - ENOMEM when out of memory
103 int afb_apis_add(const char *name, struct afb_api api)
105 struct api_desc *apis;
108 /* Checks the api name */
109 if (!afb_apis_is_valid_api_name(name)) {
110 ERROR("invalid api name forbidden (name is '%s')", name);
115 /* check previously existing plugin */
116 for (i = 0 ; i < apis_count ; i++) {
117 c = strcasecmp(apis_array[i].name, name);
119 ERROR("api of name %s already exists", name);
127 /* allocates enough memory */
128 apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis);
130 ERROR("out of memory");
136 /* copy higher part of the array */
139 apis_array[c] = apis_array[c - 1];
143 /* record the plugin */
144 apis = &apis_array[i];
149 NOTICE("API %s added", name);
159 * @param api the api of the verb
160 * @return the descriptor if found or NULL otherwise
162 static const struct api_desc *search(const char *api)
165 const struct api_desc *a;
167 /* dichotomic search of the api */
172 /* check remaining slice */
177 /* check the mid of the slice */
180 c = strcasecmp(a->name, api);
185 /* update the slice */
194 * Starts a service by its 'api' name.
195 * @param api name of the service to start
196 * @param share_session if true start the servic"e in a shared session
197 * if false start it in its own session
198 * @param onneed if true start the service if possible, if false the api
200 * @return a positive number on success
202 int afb_apis_start_service(const char *api, int share_session, int onneed)
206 for (i = 0 ; i < apis_count ; i++) {
207 if (!strcasecmp(apis_array[i].name, api))
208 return apis_array[i].api.itf->service_start(apis_array[i].api.closure, share_session, onneed);
210 ERROR("can't find service %s", api);
216 * Starts all possible services but stops at first error.
217 * @param share_session if true start the servic"e in a shared session
218 * if false start it in its own session
219 * @return 0 on success or a negative number when an error is found
221 int afb_apis_start_all_services(int share_session)
225 for (i = 0 ; i < apis_count ; i++) {
226 rc = apis_array[i].api.itf->service_start(apis_array[i].api.closure, share_session, 1);
234 * Internal direct dispatch of the request 'xreq'
235 * @param xreq the request to dispatch
237 static void do_call_direct(struct afb_xreq *xreq)
239 const struct api_desc *a;
242 a = search(xreq->api);
244 afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
246 xreq->context.api_key = a->api.closure;
247 a->api.itf->call(a->api.closure, xreq);
252 * Asynchronous dispatch callback for the request 'xreq'
253 * @param signum 0 on normal flow or the signal number that interupted the normal flow
255 static void do_call_async(int signum, void *arg)
257 struct afb_xreq *xreq = arg;
260 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
262 do_call_direct(xreq);
264 afb_xreq_unref(xreq);
268 * Dispatch the request 'xreq' synchronously and directly.
269 * @param xreq the request to dispatch
271 void afb_apis_call_direct(struct afb_xreq *xreq)
273 afb_xreq_begin(xreq);
274 do_call_direct(xreq);
278 * Dispatch the request 'xreq' asynchronously.
279 * @param xreq the request to dispatch
281 void afb_apis_call(struct afb_xreq *xreq)
285 afb_xreq_begin(xreq);
286 afb_xreq_addref(xreq);
287 rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq);
289 /* TODO: allows or not to proccess it directly as when no threading? (see above) */
290 ERROR("can't process job with threads: %m");
291 afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task");
292 afb_xreq_unref(xreq);
297 * Ask to update the hook flags of the 'api'
298 * @param api the api to update (NULL updates all)
300 void afb_apis_update_hooks(const char *api)
302 const struct api_desc *i, *e;
306 e = &apis_array[apis_count];
312 if (i->api.itf->update_hooks)
313 i->api.itf->update_hooks(i->api.closure);
319 * Set the verbosity level of the 'api'
320 * @param api the api to set (NULL set all)
322 void afb_apis_set_verbosity(const char *api, int level)
324 const struct api_desc *i, *e;
328 e = &apis_array[apis_count];
334 if (i->api.itf->set_verbosity)
335 i->api.itf->set_verbosity(i->api.closure, level);
341 * Set the verbosity level of the 'api'
342 * @param api the api to set (NULL set all)
344 int afb_apis_get_verbosity(const char *api)
346 const struct api_desc *i;
348 i = api ? search(api) : NULL;
353 if (!i->api.itf->get_verbosity)
356 return i->api.itf->get_verbosity(i->api.closure);
360 * Get the list of api names
361 * @return a NULL terminated array of api names. Must be freed.
363 const char **afb_apis_get_names()
370 size = apis_count * (1 + sizeof(*names)) + sizeof(*names);
371 for (i = 0 ; i < apis_count ; i++)
372 size += strlen(apis_array[i].name);
374 names = malloc(size);
378 dest = (void*)&names[apis_count+1];
379 for (i = 0 ; i < apis_count ; i++) {
381 dest = stpcpy(dest, apis_array[i].name) + 1;