Use of 'afb_api_is_valid_name'
[src/app-framework-binder.git] / src / afb-apiset.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  * Author José Bollo <jose.bollo@iot.bzh>
5  *
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
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include "afb-session.h"
27 #include "verbose.h"
28 #include "afb-api.h"
29 #include "afb-apiset.h"
30 #include "afb-context.h"
31 #include "afb-xreq.h"
32 #include "jobs.h"
33
34 #include <afb/afb-req-itf.h>
35
36 #define INCR 8          /* CAUTION: must be a power of 2 */
37
38 /**
39  * Internal description of an api
40  */
41 struct api_desc {
42         const char *name;       /**< name of the api */
43         struct afb_api api;     /**< handler of the api */
44 };
45
46 struct afb_apiset
47 {
48         struct afb_apiset *subset;
49         struct api_desc *apis;
50         int count;
51         int timeout;
52         int refcount;
53         char name[1];
54 };
55
56 /**
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
61  */
62 static const struct api_desc *search(struct afb_apiset *set, const char *name)
63 {
64         int i, c, up, lo;
65         const struct api_desc *a;
66
67         /* dichotomic search of the api */
68         /* initial slice */
69         lo = 0;
70         up = set->count;
71         for (;;) {
72                 /* check remaining slice */
73                 if (lo >= up) {
74                         /* not found */
75                         return NULL;
76                 }
77                 /* check the mid of the slice */
78                 i = (lo + up) >> 1;
79                 a = &set->apis[i];
80                 c = strcasecmp(a->name, name);
81                 if (c == 0) {
82                         /* found */
83                         return a;
84                 }
85                 /* update the slice */
86                 if (c < 0)
87                         lo = i + 1;
88                 else
89                         up = i;
90         }
91 }
92
93 /**
94 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
95 {
96         if (set)
97                 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
98         return set;
99 }
100
101 void afb_apiset_unref(struct afb_apiset *set)
102 {
103         if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
104                 afb_apiset_unref(set->subset);
105                 free(set->apis);
106                 free(set);
107         }
108 }
109
110 /**
111  * Create an apiset
112  */
113 struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset)
114 {
115         struct afb_apiset *set;
116
117         set = malloc((name ? strlen(name) : 0) + sizeof *set);
118         if (set) {
119                 set->subset = afb_apiset_addref(subset);
120                 set->apis = malloc(INCR * sizeof *set->apis);
121                 set->count = 0;
122                 set->timeout = timeout;
123                 set->refcount = 1;
124                 if (name)
125                         strcpy(set->name, name);
126                 else
127                         set->name[0] = 0;
128         }
129         return set;
130 }
131
132
133 /**
134  * Get the API timeout of the set
135  * @param set the api set
136  * @return the timeout in seconds
137  */
138 int afb_apiset_get_timeout(struct afb_apiset *set)
139 {
140         return set->timeout;
141 }
142
143 /**
144  * Set the API timeout of the set
145  * @param set the api set
146  * @param to the timeout in seconds
147  */
148 void afb_apiset_set_timeout(struct afb_apiset *set, int to)
149 {
150         set->timeout = to;
151 }
152
153 /**
154  * Get the subset of the set
155  * @param set the api set
156  * @return the subset of set
157  */
158 struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set)
159 {
160         return set->subset;
161 }
162
163 /**
164  * Set the subset of the set
165  * @param set the api set
166  * @param subset the subset to set
167  */
168 void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset)
169 {
170         struct afb_apiset *tmp = set->subset;
171         set->subset = afb_apiset_addref(subset);
172         afb_apiset_unref(tmp);
173 }
174
175 /**
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!)
179  * @param api the api
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
185  */
186 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
187 {
188         struct api_desc *apis;
189         int i, c;
190
191         /* Checks the api name */
192         if (!afb_api_is_valid_name(name)) {
193                 ERROR("invalid api name forbidden (name is '%s')", name);
194                 errno = EINVAL;
195                 goto error;
196         }
197
198         /* check previously existing plugin */
199         for (i = 0 ; i < set->count ; i++) {
200                 c = strcasecmp(set->apis[i].name, name);
201                 if (c == 0) {
202                         ERROR("api of name %s already exists", name);
203                         errno = EEXIST;
204                         goto error;
205                 }
206                 if (c > 0)
207                         break;
208         }
209
210         /* allocates enough memory */
211         c = (set->count + INCR) & ~(INCR - 1);
212         apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
213         if (apis == NULL) {
214                 ERROR("out of memory");
215                 errno = ENOMEM;
216                 goto error;
217         }
218         set->apis = apis;
219
220         /* copy higher part of the array */
221         apis += i;
222         if (i != set->count)
223                 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
224
225         /* record the plugin */
226         apis->api = api;
227         apis->name = name;
228         set->count++;
229
230         NOTICE("API %s added", name);
231
232         return 0;
233
234 error:
235         return -1;
236 }
237
238 int afb_apiset_del(struct afb_apiset *set, const char *name)
239 {
240         int i, c;
241
242         /* search the api */
243         for (i = 0 ; i < set->count ; i++) {
244                 c = strcasecmp(set->apis[i].name, name);
245                 if (c == 0) {
246                         set->count--;
247                         while(i < set->count) {
248                                 set->apis[i] = set->apis[i + 1];
249                                 i++;
250                         }
251                         return 0;
252                 }
253                 if (c > 0)
254                         break;
255         }
256         errno = ENOENT;
257         return -1;
258 }
259
260 int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
261 {
262         const struct api_desc *i;
263
264         i = search(set, name);
265         if (i) {
266                 *api = i->api;
267                 return 0;
268         }
269         if (set->subset)
270                 return afb_apiset_get(set->subset, name, api);
271
272         errno = ENOENT;
273         return -1;
274 }
275
276 /**
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
283  *               must be a service
284  * @return a positive number on success
285  */
286 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
287 {
288         const struct api_desc *a;
289
290         a = search(set, name);
291         if (!a) {
292                 ERROR("can't find service %s", name);
293                 errno = ENOENT;
294                 return -1;
295         }
296
297         if (a->api.itf->service_start)
298                 return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
299
300         if (onneed)
301                 return 0;
302
303         /* already started: it is an error */
304         ERROR("The api %s is not a startable service", name);
305         errno = EINVAL;
306         return -1;
307 }
308
309 /**
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
315  */
316 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
317 {
318         int rc;
319         const struct api_desc *i, *e;
320
321         i = set->apis;
322         e = &set->apis[set->count];
323         while (i != e) {
324                 if (i->api.itf->service_start) {
325                         rc = i->api.itf->service_start(i->api.closure, share_session, 1, set);
326                         if (rc < 0)
327                                 return rc;
328                 }
329                 i++;
330         }
331         return 0;
332 }
333
334 /**
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)
338  */
339 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
340 {
341         const struct api_desc *i, *e;
342
343         if (!name) {
344                 i = set->apis;
345                 e = &set->apis[set->count];
346         } else {
347                 i = search(set, name);
348                 e = &i[!!i];
349         }
350         while (i != e) {
351                 if (i->api.itf->update_hooks)
352                         i->api.itf->update_hooks(i->api.closure);
353                 i++;
354         }
355 }
356
357 /**
358  * Set the verbosity level of the 'api'
359  * @param set the api set
360  * @param name the api to set (NULL set all)
361  */
362 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
363 {
364         const struct api_desc *i, *e;
365
366         if (!name) {
367                 i = set->apis;
368                 e = &set->apis[set->count];
369         } else {
370                 i = search(set, name);
371                 e = &i[!!i];
372         }
373         while (i != e) {
374                 if (i->api.itf->set_verbosity)
375                         i->api.itf->set_verbosity(i->api.closure, level);
376                 i++;
377         }
378 }
379
380 /**
381  * Set the verbosity level of the 'api'
382  * @param set the api set
383  * @param name the api to set (NULL set all)
384  */
385 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
386 {
387         const struct api_desc *i;
388
389         i = name ? search(set, name) : NULL;
390         if (!i) {
391                 errno = ENOENT;
392                 return -1;
393         }
394         if (!i->api.itf->get_verbosity)
395                 return 0;
396
397         return i->api.itf->get_verbosity(i->api.closure);
398 }
399
400 /**
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.
404  */
405 const char **afb_apiset_get_names(struct afb_apiset *set)
406 {
407         size_t size;
408         char *dest;
409         const char **names;
410         int i;
411
412         size = set->count * (1 + sizeof(*names)) + sizeof(*names);
413         for (i = 0 ; i < set->count ; i++)
414                 size += strlen(set->apis[i].name);
415
416         names = malloc(size);
417         if (!names)
418                 errno = ENOMEM;
419         else {
420                 dest = (void*)&names[set->count+1];
421                 for (i = 0 ; i < set->count ; i++) {
422                         names[i] = dest;
423                         dest = stpcpy(dest, set->apis[i].name) + 1;
424                 }
425                 names[i] = NULL;
426         }
427         return names;
428 }
429
430
431 #if 0
432
433
434
435 /**
436  * Internal direct dispatch of the request 'xreq'
437  * @param set the api set
438  * @param xreq the request to dispatch
439  */
440 static void do_call_direct(struct afb_xreq *xreq)
441 {
442         const struct api_desc *a;
443
444         /* search the api */
445         a = search(xreq->api);
446         if (!a)
447                 afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
448         else {
449                 xreq->context.api_key = a->api.closure;
450                 a->api.itf->call(a->api.closure, xreq);
451         }
452 }
453
454 /**
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
458  */
459 static void do_call_async(int signum, void *arg)
460 {
461         struct afb_xreq *xreq = arg;
462
463         if (signum != 0)
464                 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
465         else {
466                 do_call_direct(xreq);
467         }
468         afb_xreq_unref(xreq);
469 }
470
471 /**
472  * Dispatch the request 'xreq' synchronously and directly.
473  * @param set the api set
474  * @param xreq the request to dispatch
475  */
476 void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq)
477 {
478         afb_xreq_begin(xreq);
479         do_call_direct(xreq);
480 }
481
482 /**
483  * Dispatch the request 'xreq' asynchronously.
484  * @param set the api set
485  * @param xreq the request to dispatch
486  */
487 void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq)
488 {
489         int rc;
490
491         afb_xreq_begin(xreq);
492         afb_xreq_addref(xreq);
493         rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq);
494         if (rc < 0) {
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);
499         }
500 }
501
502 #endif
503