afb-apiset: manage deletion of apis
[src/app-framework-binder.git] / src / afb-apiset.c
1 /*
2  * Copyright (C) 2016, 2017, 2018 "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
33 #define INCR 8          /* CAUTION: must be a power of 2 */
34
35 /**
36  * Internal description of an api
37  */
38 struct api_desc
39 {
40         int status;
41         const char *name;       /**< name of the api */
42         struct afb_api api;     /**< handler of the api */
43 };
44
45 /**
46  * Data structure for apiset
47  */
48 struct afb_apiset
49 {
50         struct api_desc *apis;          /**< description of apis */
51         struct afb_apiset *subset;      /**< subset if any */
52         int count;                      /**< count of apis in the set */
53         int timeout;                    /**< the timeout in second for the apiset */
54         int refcount;                   /**< reference count for freeing resources */
55         char name[1];                   /**< name of the apiset */
56 };
57
58 /**
59  * Search the api of 'name'.
60  * @param set the api set
61  * @param name the api name to search
62  * @return the descriptor if found or NULL otherwise
63  */
64 static struct api_desc *search(struct afb_apiset *set, const char *name)
65 {
66         int i, c, up, lo;
67         struct api_desc *a;
68
69         /* dichotomic search of the api */
70         /* initial slice */
71         lo = 0;
72         up = set->count;
73         for (;;) {
74                 /* check remaining slice */
75                 if (lo >= up) {
76                         /* not found */
77                         return NULL;
78                 }
79                 /* check the mid of the slice */
80                 i = (lo + up) >> 1;
81                 a = &set->apis[i];
82                 c = strcasecmp(a->name, name);
83                 if (c == 0) {
84                         /* found */
85                         return a;
86                 }
87                 /* update the slice */
88                 if (c < 0)
89                         lo = i + 1;
90                 else
91                         up = i;
92         }
93 }
94
95 /**
96  * Increases the count of references to the apiset and return its address
97  * @param set the set whose reference count is to be increased
98  * @return the given apiset
99  */
100 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
101 {
102         if (set)
103                 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
104         return set;
105 }
106
107 /**
108  * Decreases the count of references to the apiset and frees its
109  * resources when no more references exists.
110  * @param set the set to unrefrence
111  */
112 void afb_apiset_unref(struct afb_apiset *set)
113 {
114         if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
115                 afb_apiset_unref(set->subset);
116                 free(set->apis);
117                 free(set);
118         }
119 }
120
121 /**
122  * Create an apiset
123  * @param name the name of the apiset
124  * @param timeout the default timeout in seconds for the apiset
125  * @return the created apiset or NULL in case of error
126  */
127 struct afb_apiset *afb_apiset_create(const char *name, int timeout)
128 {
129         struct afb_apiset *set;
130
131         set = malloc((name ? strlen(name) : 0) + sizeof *set);
132         if (set) {
133                 set->apis = malloc(INCR * sizeof *set->apis);
134                 set->count = 0;
135                 set->timeout = timeout;
136                 set->refcount = 1;
137                 set->subset = NULL;
138                 if (name)
139                         strcpy(set->name, name);
140                 else
141                         set->name[0] = 0;
142         }
143         return set;
144 }
145
146 /**
147  * the name of the apiset
148  * @param set the api set
149  * @return the name of the set
150  */
151 const char *afb_apiset_name(struct afb_apiset *set)
152 {
153         return set->name;
154 }
155
156 /**
157  * Get the API timeout of the set
158  * @param set the api set
159  * @return the timeout in seconds
160  */
161 int afb_apiset_timeout_get(struct afb_apiset *set)
162 {
163         return set->timeout;
164 }
165
166 /**
167  * Set the API timeout of the set
168  * @param set the api set
169  * @param to the timeout in seconds
170  */
171 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
172 {
173         set->timeout = to;
174 }
175
176 /**
177  * Get the subset of the set
178  * @param set the api set
179  * @return the subset of set
180  */
181 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
182 {
183         return set->subset;
184 }
185
186 /**
187  * Set the subset of the set
188  * @param set the api set
189  * @param subset the subset to set
190  */
191 void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
192 {
193         struct afb_apiset *tmp;
194         if (subset == set) {
195                 /* avoid infinite loop */
196                 subset = NULL;
197         }
198         tmp = set->subset;
199         set->subset = afb_apiset_addref(subset);
200         afb_apiset_unref(tmp);
201 }
202
203 /**
204  * Adds the api of 'name' described by 'api'.
205  * @param set the api set
206  * @param name the name of the api to add (have to survive, not copied!)
207  * @param api the api
208  * @returns 0 in case of success or -1 in case
209  * of error with errno set:
210  *   - EEXIST if name already registered
211  *   - ENOMEM when out of memory
212  */
213 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
214 {
215         struct api_desc *apis;
216         int i, c;
217
218         /* check previously existing plugin */
219         for (i = 0 ; i < set->count ; i++) {
220                 c = strcasecmp(set->apis[i].name, name);
221                 if (c == 0) {
222                         ERROR("api of name %s already exists", name);
223                         errno = EEXIST;
224                         goto error;
225                 }
226                 if (c > 0)
227                         break;
228         }
229
230         /* allocates enough memory */
231         c = (set->count + INCR) & ~(INCR - 1);
232         apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
233         if (apis == NULL) {
234                 ERROR("out of memory");
235                 errno = ENOMEM;
236                 goto error;
237         }
238         set->apis = apis;
239
240         /* copy higher part of the array */
241         apis += i;
242         if (i != set->count)
243                 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
244
245         /* record the plugin */
246         apis->status = -1;
247         apis->api = api;
248         apis->name = name;
249         set->count++;
250
251         INFO("API %s added", name);
252
253         return 0;
254
255 error:
256         return -1;
257 }
258
259 /**
260  * Delete from the 'set' the api of 'name'.
261  * @param set the set to be changed
262  * @param name the name of the API to remove
263  * @return 0 in case of success or -1 in case where the API doesn't exist.
264  */
265 int afb_apiset_del(struct afb_apiset *set, const char *name)
266 {
267         struct api_desc *i, *e;
268         int c;
269
270         /* search the api */
271         i = set->apis;
272         e = i + set->count;
273         while (i != e) {
274                 c = strcasecmp(i->name, name);
275                 if (c == 0) {
276                         if (i->api.itf->unref)
277                                 i->api.itf->unref(i->api.closure);
278                         set->count--;
279                         e--;
280                         while (i != e) {
281                                 i[0] = i[1];
282                                 i++;
283                         }
284                         return 0;
285                 }
286                 if (c > 0)
287                         break;
288                 i++;
289         }
290         errno = ENOENT;
291         return -1;
292 }
293
294 /**
295  * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
296  * @param set the set of API
297  * @param name the name of the API to get
298  * @param rec if not zero look also recursively in subsets
299  * @return the api pointer in case of success or NULL in case of error
300  */
301 static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
302 {
303         struct api_desc *i = search(set, name);
304         return i || !rec || !set->subset ? i : lookup(set->subset, name, rec);
305 }
306
307 /**
308  * Get from the 'set' the API of 'name' in 'api'
309  * @param set the set of API
310  * @param name the name of the API to get
311  * @param rec if not zero look also recursively in subsets
312  * @return the api pointer in case of success or NULL in case of error
313  */
314 const struct afb_api *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
315 {
316         struct api_desc *i;
317
318         i = lookup(set, name, rec);
319         if (i)
320                 return &i->api;
321         errno = ENOENT;
322         return NULL;
323 }
324
325 /**
326  * Starts the service 'api'.
327  * @param api the api
328  * @param share_session if true start the servic"e in a shared session
329  *                      if false start it in its own session
330  * @param onneed if true start the service if possible, if false the api
331  *               must be a service
332  * @return a positive number on success
333  */
334 static int start_api(struct afb_apiset *set, struct api_desc *api, int share_session, int onneed)
335 {
336         int rc;
337
338         if (api->status == 0)
339                 return 0;
340         else if (api->status > 0) {
341                 errno = api->status;
342                 return -1;
343         }
344
345         INFO("API %s starting...", api->name);
346         if (api->api.itf->service_start) {
347                 api->status = EBUSY;
348                 rc = api->api.itf->service_start(api->api.closure, share_session, onneed, set);
349                 if (rc < 0) {
350                         api->status = errno ?: ECANCELED;
351                         ERROR("The api %s failed to start (%d)", api->name, rc);
352                         return -1;
353                 }
354         } else if (!onneed) {
355                 /* already started: it is an error */
356                 ERROR("The api %s is not a startable service", api->name);
357                 api->status = EINVAL;
358                 return -1;
359         }
360         NOTICE("API %s started", api->name);
361         api->status = 0;
362         return 0;
363 }
364
365 /**
366  * Get from the 'set' the API of 'name' in 'api'
367  * @param set the set of API
368  * @param name the name of the API to get
369  * @param rec if not zero look also recursively in subsets
370  * @return 0 in case of success or -1 in case of error
371  */
372 const struct afb_api *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
373 {
374         struct api_desc *i;
375
376         i = lookup(set, name, rec);
377         if (i)
378                 return i->status && start_api(set, i, 1, 1) ? NULL : &i->api;
379         errno = ENOENT;
380         return NULL;
381 }
382
383 /**
384  * Starts a service by its 'api' name.
385  * @param set the api set
386  * @param name name of the service to start
387  * @param share_session if true start the servic"e in a shared session
388  *                      if false start it in its own session
389  * @param onneed if true start the service if possible, if false the api
390  *               must be a service
391  * @return a positive number on success
392  */
393 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
394 {
395         struct api_desc *a;
396
397         a = search(set, name);
398         if (!a) {
399                 ERROR("can't find service %s", name);
400                 errno = ENOENT;
401                 return -1;
402         }
403
404         return start_api(set, a, share_session, onneed);
405 }
406
407 /**
408  * Starts all possible services but stops at first error.
409  * @param set the api set
410  * @param share_session if true start the servic"e in a shared session
411  *                      if false start it in its own session
412  * @return 0 on success or a negative number when an error is found
413  */
414 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
415 {
416         int rc;
417         struct api_desc *i, *e;
418
419         i = set->apis;
420         e = &set->apis[set->count];
421         while (i != e) {
422                 rc = start_api(set, i, share_session, 1);
423                 if (rc < 0)
424                         return rc;
425                 i++;
426         }
427
428         return set->subset ? afb_apiset_start_all_services(set->subset, share_session) : 0;
429 }
430
431 /**
432  * Ask to update the hook flags of the 'api'
433  * @param set the api set
434  * @param name the api to update (NULL updates all)
435  */
436 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
437 {
438         const struct api_desc *i, *e;
439
440         if (!name) {
441                 i = set->apis;
442                 e = &set->apis[set->count];
443         } else {
444                 i = search(set, name);
445                 e = &i[!!i];
446         }
447         while (i != e) {
448                 if (i->api.itf->update_hooks)
449                         i->api.itf->update_hooks(i->api.closure);
450                 i++;
451         }
452 }
453
454 /**
455  * Set the verbosity level of the 'api'
456  * @param set the api set
457  * @param name the api to set (NULL set all)
458  */
459 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
460 {
461         const struct api_desc *i, *e;
462
463         if (!name) {
464                 i = set->apis;
465                 e = &set->apis[set->count];
466         } else {
467                 i = search(set, name);
468                 e = &i[!!i];
469         }
470         while (i != e) {
471                 if (i->api.itf->set_verbosity)
472                         i->api.itf->set_verbosity(i->api.closure, level);
473                 i++;
474         }
475 }
476
477 /**
478  * Get the verbosity level of the 'api'
479  * @param set the api set
480  * @param name the api to get
481  * @return the verbosity level or -1 in case of error
482  */
483 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
484 {
485         const struct api_desc *i;
486
487         i = name ? search(set, name) : NULL;
488         if (!i) {
489                 errno = ENOENT;
490                 return -1;
491         }
492
493         if (!i->api.itf->get_verbosity)
494                 return verbosity;
495
496         return i->api.itf->get_verbosity(i->api.closure);
497 }
498
499 /**
500  * Get the description of the API of 'name'
501  * @param set the api set
502  * @param name the api whose description is required
503  * @return the description or NULL
504  */
505 struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
506 {
507         const struct api_desc *i;
508
509         i = name ? search(set, name) : NULL;
510         return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
511 }
512
513 /**
514  * Get the list of api names
515  * @param set the api set
516  * @return a NULL terminated array of api names. Must be freed.
517  */
518 const char **afb_apiset_get_names(struct afb_apiset *set)
519 {
520         size_t size;
521         char *dest;
522         const char **names;
523         int i;
524
525         size = set->count * (1 + sizeof(*names)) + sizeof(*names);
526         for (i = 0 ; i < set->count ; i++)
527                 size += strlen(set->apis[i].name);
528
529         names = malloc(size);
530         if (!names)
531                 errno = ENOMEM;
532         else {
533                 dest = (void*)&names[set->count+1];
534                 for (i = 0 ; i < set->count ; i++) {
535                         names[i] = dest;
536                         dest = stpcpy(dest, set->apis[i].name) + 1;
537                 }
538                 names[i] = NULL;
539         }
540         return names;
541 }
542
543 /**
544  * Enumerate the api names to a callback.
545  * @param set the api set
546  * @param callback the function to call for each name
547  * @param closure the closure for the callback
548  */
549 void afb_apiset_enum(struct afb_apiset *set, int rec, void (*callback)(struct afb_apiset *set, const char *name, void *closure), void *closure)
550 {
551         struct afb_apiset *iset;
552         struct api_desc *i, *e;
553
554         iset = set;
555         while (iset) {
556                 i = iset->apis;
557                 e = &i[iset->count];
558                 while (i != e) {
559                         if (lookup(set, i->name, 1) == i)
560                                 callback(iset, i->name, closure);
561                         i++;
562                 }
563                 iset = rec ? iset->subset : NULL;
564         }
565 }
566