Introduce apiset for grouping apis
[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  * Checks wether 'name' is a valid API name.
95  * @return 1 if valid, 0 otherwise
96  */
97 int afb_apiset_valid_name(const char *name)
98 {
99         unsigned char c;
100
101         c = (unsigned char)*name;
102         if (c == 0)
103                 /* empty names aren't valid */
104                 return 0;
105
106         do {
107                 if (c < (unsigned char)'\x80') {
108                         switch(c) {
109                         default:
110                                 if (c > ' ')
111                                         break;
112                         case '"':
113                         case '#':
114                         case '%':
115                         case '&':
116                         case '\'':
117                         case '/':
118                         case '?':
119                         case '`':
120                         case '\\':
121                         case '\x7f':
122                                 return 0;
123                         }
124                 }
125                 c = (unsigned char)*++name;
126         } while(c != 0);
127         return 1;
128 }
129
130 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
131 {
132         if (set)
133                 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
134         return set;
135 }
136
137 void afb_apiset_unref(struct afb_apiset *set)
138 {
139         if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
140                 afb_apiset_unref(set->subset);
141                 free(set->apis);
142                 free(set);
143         }
144 }
145
146 /**
147  * Create an apiset
148  */
149 struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset)
150 {
151         struct afb_apiset *set;
152
153         set = malloc((name ? strlen(name) : 0) + sizeof *set);
154         if (set) {
155                 set->subset = afb_apiset_addref(subset);
156                 set->apis = malloc(INCR * sizeof *set->apis);
157                 set->count = 0;
158                 set->timeout = timeout;
159                 set->refcount = 1;
160                 if (name)
161                         strcpy(set->name, name);
162                 else
163                         set->name[0] = 0;
164         }
165         return set;
166 }
167
168
169 /**
170  * Get the API timeout of the set
171  * @param set the api set
172  * @return the timeout in seconds
173  */
174 int afb_apiset_get_timeout(struct afb_apiset *set)
175 {
176         return set->timeout;
177 }
178
179 /**
180  * Set the API timeout of the set
181  * @param set the api set
182  * @param to the timeout in seconds
183  */
184 void afb_apiset_set_timeout(struct afb_apiset *set, int to)
185 {
186         set->timeout = to;
187 }
188
189 /**
190  * Get the subset of the set
191  * @param set the api set
192  * @return the subset of set
193  */
194 struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set)
195 {
196         return set->subset;
197 }
198
199 /**
200  * Set the subset of the set
201  * @param set the api set
202  * @param subset the subset to set
203  */
204 void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset)
205 {
206         struct afb_apiset *tmp = set->subset;
207         set->subset = afb_apiset_addref(subset);
208         afb_apiset_unref(tmp);
209 }
210
211 /**
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!)
215  * @param api the api
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
221  */
222 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
223 {
224         struct api_desc *apis;
225         int i, c;
226
227         /* Checks the api name */
228         if (!afb_apiset_valid_name(name)) {
229                 ERROR("invalid api name forbidden (name is '%s')", name);
230                 errno = EINVAL;
231                 goto error;
232         }
233
234         /* check previously existing plugin */
235         for (i = 0 ; i < set->count ; i++) {
236                 c = strcasecmp(set->apis[i].name, name);
237                 if (c == 0) {
238                         ERROR("api of name %s already exists", name);
239                         errno = EEXIST;
240                         goto error;
241                 }
242                 if (c > 0)
243                         break;
244         }
245
246         /* allocates enough memory */
247         c = (set->count + INCR) & ~(INCR - 1);
248         apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
249         if (apis == NULL) {
250                 ERROR("out of memory");
251                 errno = ENOMEM;
252                 goto error;
253         }
254         set->apis = apis;
255
256         /* copy higher part of the array */
257         apis += i;
258         if (i != set->count)
259                 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
260
261         /* record the plugin */
262         apis->api = api;
263         apis->name = name;
264         set->count++;
265
266         NOTICE("API %s added", name);
267
268         return 0;
269
270 error:
271         return -1;
272 }
273
274 int afb_apiset_del(struct afb_apiset *set, const char *name)
275 {
276         int i, c;
277
278         /* search the api */
279         for (i = 0 ; i < set->count ; i++) {
280                 c = strcasecmp(set->apis[i].name, name);
281                 if (c == 0) {
282                         set->count--;
283                         while(i < set->count) {
284                                 set->apis[i] = set->apis[i + 1];
285                                 i++;
286                         }
287                         return 0;
288                 }
289                 if (c > 0)
290                         break;
291         }
292         errno = ENOENT;
293         return -1;
294 }
295
296 int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api)
297 {
298         const struct api_desc *i;
299
300         i = search(set, name);
301         if (i) {
302                 *api = i->api;
303                 return 0;
304         }
305         if (set->subset)
306                 return afb_apiset_get(set->subset, name, api);
307
308         errno = ENOENT;
309         return -1;
310 }
311
312 /**
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
319  *               must be a service
320  * @return a positive number on success
321  */
322 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
323 {
324         const struct api_desc *a;
325
326         a = search(set, name);
327         if (!a) {
328                 ERROR("can't find service %s", name);
329                 errno = ENOENT;
330                 return -1;
331         }
332
333         if (a->api.itf->service_start)
334                 return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
335
336         if (onneed)
337                 return 0;
338
339         /* already started: it is an error */
340         ERROR("The api %s is not a startable service", name);
341         errno = EINVAL;
342         return -1;
343 }
344
345 /**
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
351  */
352 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
353 {
354         int rc;
355         const struct api_desc *i, *e;
356
357         i = set->apis;
358         e = &set->apis[set->count];
359         while (i != e) {
360                 if (i->api.itf->service_start) {
361                         rc = i->api.itf->service_start(i->api.closure, share_session, 1, set);
362                         if (rc < 0)
363                                 return rc;
364                 }
365                 i++;
366         }
367         return 0;
368 }
369
370 /**
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)
374  */
375 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
376 {
377         const struct api_desc *i, *e;
378
379         if (!name) {
380                 i = set->apis;
381                 e = &set->apis[set->count];
382         } else {
383                 i = search(set, name);
384                 e = &i[!!i];
385         }
386         while (i != e) {
387                 if (i->api.itf->update_hooks)
388                         i->api.itf->update_hooks(i->api.closure);
389                 i++;
390         }
391 }
392
393 /**
394  * Set the verbosity level of the 'api'
395  * @param set the api set
396  * @param name the api to set (NULL set all)
397  */
398 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
399 {
400         const struct api_desc *i, *e;
401
402         if (!name) {
403                 i = set->apis;
404                 e = &set->apis[set->count];
405         } else {
406                 i = search(set, name);
407                 e = &i[!!i];
408         }
409         while (i != e) {
410                 if (i->api.itf->set_verbosity)
411                         i->api.itf->set_verbosity(i->api.closure, level);
412                 i++;
413         }
414 }
415
416 /**
417  * Set the verbosity level of the 'api'
418  * @param set the api set
419  * @param name the api to set (NULL set all)
420  */
421 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
422 {
423         const struct api_desc *i;
424
425         i = name ? search(set, name) : NULL;
426         if (!i) {
427                 errno = ENOENT;
428                 return -1;
429         }
430         if (!i->api.itf->get_verbosity)
431                 return 0;
432
433         return i->api.itf->get_verbosity(i->api.closure);
434 }
435
436 /**
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.
440  */
441 const char **afb_apiset_get_names(struct afb_apiset *set)
442 {
443         size_t size;
444         char *dest;
445         const char **names;
446         int i;
447
448         size = set->count * (1 + sizeof(*names)) + sizeof(*names);
449         for (i = 0 ; i < set->count ; i++)
450                 size += strlen(set->apis[i].name);
451
452         names = malloc(size);
453         if (!names)
454                 errno = ENOMEM;
455         else {
456                 dest = (void*)&names[set->count+1];
457                 for (i = 0 ; i < set->count ; i++) {
458                         names[i] = dest;
459                         dest = stpcpy(dest, set->apis[i].name) + 1;
460                 }
461                 names[i] = NULL;
462         }
463         return names;
464 }
465
466
467 #if 0
468
469
470
471 /**
472  * Internal direct dispatch of the request 'xreq'
473  * @param set the api set
474  * @param xreq the request to dispatch
475  */
476 static void do_call_direct(struct afb_xreq *xreq)
477 {
478         const struct api_desc *a;
479
480         /* search the api */
481         a = search(xreq->api);
482         if (!a)
483                 afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
484         else {
485                 xreq->context.api_key = a->api.closure;
486                 a->api.itf->call(a->api.closure, xreq);
487         }
488 }
489
490 /**
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
494  */
495 static void do_call_async(int signum, void *arg)
496 {
497         struct afb_xreq *xreq = arg;
498
499         if (signum != 0)
500                 afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
501         else {
502                 do_call_direct(xreq);
503         }
504         afb_xreq_unref(xreq);
505 }
506
507 /**
508  * Dispatch the request 'xreq' synchronously and directly.
509  * @param set the api set
510  * @param xreq the request to dispatch
511  */
512 void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq)
513 {
514         afb_xreq_begin(xreq);
515         do_call_direct(xreq);
516 }
517
518 /**
519  * Dispatch the request 'xreq' asynchronously.
520  * @param set the api set
521  * @param xreq the request to dispatch
522  */
523 void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq)
524 {
525         int rc;
526
527         afb_xreq_begin(xreq);
528         afb_xreq_addref(xreq);
529         rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq);
530         if (rc < 0) {
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);
535         }
536 }
537
538 #endif
539