api-v3: First draft
[src/app-framework-binder.git] / src / afb-apiset.c
1 /*
2  * Copyright (C) 2016, 2017, 2018 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include "afb-session.h"
26 #include "verbose.h"
27 #include "afb-api.h"
28 #include "afb-apiset.h"
29 #include "afb-context.h"
30 #include "afb-xreq.h"
31
32 #define INCR 8          /* CAUTION: must be a power of 2 */
33
34 struct afb_apiset;
35 struct api_desc;
36 struct api_class;
37 struct api_alias;
38 struct api_depend;
39
40 /**
41  * array of items
42  */
43 struct api_array {
44         int count;                      /* count of items */
45         union {
46                 void **anys;
47                 struct api_desc **apis;
48                 struct api_class **classes;
49                 struct api_alias **aliases;
50                 struct api_depend **depends;
51         };
52 };
53
54 /**
55  * Internal description of an api
56  */
57 struct api_desc
58 {
59         struct api_desc *next;
60         const char *name;               /**< name of the api */
61         int status;                     /**< initialisation status */
62         struct afb_api_item api;        /**< handler of the api */
63         struct {
64                 struct api_array classes;
65                 struct api_array apis;
66         } require;
67 };
68
69 /**
70  * internal description of aliases
71  */
72 struct api_alias
73 {
74         struct api_alias *next;
75         struct api_desc *api;
76         char name[1];
77 };
78
79 /**
80  *
81  */
82 struct api_class
83 {
84         struct api_class *next;
85         struct api_array providers;
86         char name[1];
87 };
88
89 /**
90  *
91  */
92 struct api_depend
93 {
94         struct afb_apiset *set;
95         char name[1];
96 };
97
98 /**
99  * Data structure for apiset
100  */
101 struct afb_apiset
102 {
103         struct api_array apis;          /**< the apis */
104         struct api_alias *aliases;      /**< the aliases */
105         struct afb_apiset *subset;      /**< subset if any */
106         struct {
107                 int (*callback)(void*, struct afb_apiset*, const char*); /* not found handler */
108                 void *closure;
109                 void (*cleanup)(void*);
110         } onlack;                       /** not found handler */
111         int timeout;                    /**< the timeout in second for the apiset */
112         int refcount;                   /**< reference count for freeing resources */
113         char name[1];                   /**< name of the apiset */
114 };
115
116 /**
117  * global apis
118  */
119 static struct api_desc *all_apis;
120
121 /**
122  * global classes
123  */
124 static struct api_class *all_classes;
125
126 /**
127  * Ensure enough room in 'array' for 'count' items
128  */
129 static int api_array_ensure_count(struct api_array *array, int count)
130 {
131         int c;
132         void **anys;
133
134         c = (count + INCR - 1) & ~(INCR - 1);
135         anys = realloc(array->anys, c * sizeof *anys);
136         if (!anys) {
137                 errno = ENOMEM;
138                 return -1;
139         }
140
141         array->count = count;
142         array->anys = anys;
143         return 0;
144 }
145
146 /**
147  * Insert in 'array' the item 'any' at the 'index'
148  */
149 static int api_array_insert(struct api_array *array, void *any, int index)
150 {
151         int n = array->count;
152
153         if (api_array_ensure_count(array, n + 1) < 0)
154                 return -1;
155
156         while (index < n) {
157                 array->anys[n] = array->anys[n - 1];
158                 n--;
159         }
160
161         array->anys[index] = any;
162         return 0;
163 }
164
165 /**
166  * Add the item 'any' to the 'array'
167  */
168 static int api_array_add(struct api_array *array, void *any)
169 {
170         int i, n = array->count;
171
172         for (i = 0 ; i < n ; i++) {
173                 if (array->anys[i] == any)
174                         return 0;
175         }
176
177         if (api_array_ensure_count(array, n + 1) < 0)
178                 return -1;
179
180         array->anys[n] = any;
181         return 0;
182 }
183
184 /**
185  * Delete the 'api' from the 'array'
186  * Returns 1 if delete or 0 if not found
187  */
188 static int api_array_del(struct api_array *array, void *any)
189 {
190         int i = array->count;
191         while (i) {
192                 if (array->anys[--i] == any) {
193                         array->anys[i] = array->anys[--array->count];
194                         return 1;
195                 }
196         }
197         return 0;
198 }
199
200 /**
201  * Search the class of 'name' and return it.
202  * In case where the class of 'namle' isn't found, it returns
203  * NULL when 'create' is null or a fresh created instance if 'create' isn't
204  * zero (but NULL on allocation failure).
205  */
206 static struct api_class *class_search(const char *name, int create)
207 {
208         struct api_class *c;
209
210         for (c= all_classes ; c ; c = c->next) {
211                 if (!strcasecmp(name, c->name))
212                         return c;
213         }
214
215         if (!create)
216                 return NULL;
217
218         c = calloc(1, strlen(name) + sizeof *c);
219         if (!c)
220                 errno = ENOMEM;
221         else {
222                 strcpy(c->name, name);
223                 c->next = all_classes;
224                 all_classes = c;
225         }
226         return c;
227 }
228
229 /**
230  * Search the api of 'name'.
231  * @param set the api set
232  * @param name the api name to search
233  * @return the descriptor if found or NULL otherwise
234  */
235 static struct api_desc *search(struct afb_apiset *set, const char *name)
236 {
237         int i, c, up, lo;
238         struct api_desc *a;
239         struct api_alias *aliases;
240
241         /* dichotomic search of the api */
242         /* initial slice */
243         lo = 0;
244         up = set->apis.count;
245         while (lo < up) {
246                 /* check the mid of the slice */
247                 i = (lo + up) >> 1;
248                 a = set->apis.apis[i];
249                 c = strcasecmp(a->name, name);
250                 if (c == 0) {
251                         /* found */
252                         return a;
253                 }
254                 /* update the slice */
255                 if (c < 0)
256                         lo = i + 1;
257                 else
258                         up = i;
259         }
260
261         /* linear search of aliases */
262         aliases = set->aliases;
263         for(;;) {
264                 if (!aliases)
265                         break;
266                 c = strcasecmp(aliases->name, name);
267                 if (!c)
268                         return aliases->api;
269                 if (c > 0)
270                         break;
271                 aliases = aliases->next;
272         }
273         return NULL;
274 }
275
276 /**
277  * Search the api of 'name' in the apiset and in its subsets.
278  * @param set the api set
279  * @param name the api name to search
280  * @return the descriptor if found or NULL otherwise
281  */
282 static struct api_desc *searchrec(struct afb_apiset *set, const char *name)
283 {
284         struct api_desc *result;
285
286         do {
287                 result = search(set, name);
288         } while (result == NULL && (set = set->subset) != NULL);
289
290         return result;
291 }
292
293 /**
294  * Increases the count of references to the apiset and return its address
295  * @param set the set whose reference count is to be increased
296  * @return the given apiset
297  */
298 struct afb_apiset *afb_apiset_addref(struct afb_apiset *set)
299 {
300         if (set)
301                 __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED);
302         return set;
303 }
304
305 /**
306  * Decreases the count of references to the apiset and frees its
307  * resources when no more references exists.
308  * @param set the set to unrefrence
309  */
310 void afb_apiset_unref(struct afb_apiset *set)
311 {
312         struct api_alias *a;
313         struct api_desc *d;
314
315         if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) {
316                 afb_apiset_unref(set->subset);
317                 if (set->onlack.cleanup)
318                         set->onlack.cleanup(set->onlack.closure);
319                 while((a = set->aliases)) {
320                         set->aliases = a->next;
321                         free(a);
322                 }
323                 while (set->apis.count) {
324                         d = set->apis.apis[--set->apis.count];
325                         if (d->api.itf->unref)
326                                 d->api.itf->unref(d->api.closure);
327                         free(d);
328                 }
329                 free(set->apis.apis);
330                 free(set);
331         }
332 }
333
334 /**
335  * Create an apiset
336  * @param name the name of the apiset
337  * @param timeout the default timeout in seconds for the apiset
338  * @return the created apiset or NULL in case of error
339  */
340 struct afb_apiset *afb_apiset_create(const char *name, int timeout)
341 {
342         struct afb_apiset *set;
343
344         set = calloc(1, (name ? strlen(name) : 0) + sizeof *set);
345         if (set) {
346                 set->timeout = timeout;
347                 set->refcount = 1;
348                 if (name)
349                         strcpy(set->name, name);
350         }
351         return set;
352 }
353
354 /**
355  * Create an apiset being the last subset of 'set'
356  * @param set     the set to extend with the created subset (can be NULL)
357  * @param name    the name of the created apiset (can be NULL)
358  * @param timeout the default timeout in seconds for the created apiset
359  * @return the created apiset or NULL in case of error
360  */
361 struct afb_apiset *afb_apiset_create_subset_last(struct afb_apiset *set, const char *name, int timeout)
362 {
363         if (set)
364                 while (set->subset)
365                         set = set->subset;
366         return afb_apiset_create_subset_first(set, name, timeout);
367 }
368
369 /**
370  * Create an apiset being the first subset of 'set'
371  * @param set     the set to extend with the created subset (can be NULL)
372  * @param name    the name of the created apiset (can be NULL)
373  * @param timeout the default timeout in seconds for the created apiset
374  * @return the created apiset or NULL in case of error
375  */
376 struct afb_apiset *afb_apiset_create_subset_first(struct afb_apiset *set, const char *name, int timeout)
377 {
378         struct afb_apiset *result = afb_apiset_create(name, timeout);
379         if (result && set) {
380                 result->subset = set->subset;
381                 set->subset = result;
382         }
383         return result;
384 }
385
386 /**
387  * the name of the apiset
388  * @param set the api set
389  * @return the name of the set
390  */
391 const char *afb_apiset_name(struct afb_apiset *set)
392 {
393         return set->name;
394 }
395
396 /**
397  * Get the API timeout of the set
398  * @param set the api set
399  * @return the timeout in seconds
400  */
401 int afb_apiset_timeout_get(struct afb_apiset *set)
402 {
403         return set->timeout;
404 }
405
406 /**
407  * Set the API timeout of the set
408  * @param set the api set
409  * @param to the timeout in seconds
410  */
411 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
412 {
413         set->timeout = to;
414 }
415
416 /**
417  * Get the subset of the set
418  * @param set the api set
419  * @return the subset of set
420  */
421 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
422 {
423         return set->subset;
424 }
425
426 /**
427  * Set the subset of the set
428  * @param set the api set
429  * @param subset the subset to set
430  */
431 void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
432 {
433         struct afb_apiset *tmp;
434         if (subset == set) {
435                 /* avoid infinite loop */
436                 subset = NULL;
437         }
438         tmp = set->subset;
439         set->subset = afb_apiset_addref(subset);
440         afb_apiset_unref(tmp);
441 }
442
443 void afb_apiset_onlack_set(struct afb_apiset *set, int (*callback)(void*, struct afb_apiset*, const char*), void *closure, void (*cleanup)(void*))
444 {
445         if (set->onlack.cleanup)
446                 set->onlack.cleanup(set->onlack.closure);
447         set->onlack.callback = callback;
448         set->onlack.closure = closure;
449         set->onlack.cleanup = cleanup;
450 }
451
452 /**
453  * Adds the api of 'name' described by 'api'.
454  * @param set the api set
455  * @param name the name of the api to add (have to survive, not copied!)
456  * @param api the api
457  * @returns 0 in case of success or -1 in case
458  * of error with errno set:
459  *   - EEXIST if name already registered
460  *   - ENOMEM when out of memory
461  */
462 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api_item api)
463 {
464         struct api_desc *desc;
465         int i, c;
466
467         /* check whether it exists already */
468         if (search(set, name)) {
469                 ERROR("api of name %s already exists", name);
470                 errno = EEXIST;
471                 goto error;
472         }
473
474         /* search insertion place */
475         for (i = 0 ; i < set->apis.count ; i++) {
476                 c = strcasecmp(set->apis.apis[i]->name, name);
477                 if (c > 0)
478                         break;
479         }
480
481         /* allocates memory */
482         desc = calloc(1, sizeof *desc);
483         if (!desc)
484                 goto oom;
485
486         desc->status = -1;
487         desc->api = api;
488         desc->name = name;
489
490         if (api_array_insert(&set->apis, desc, i) < 0) {
491                 free(desc);
492                 goto error;
493         }
494
495         desc->next = all_apis;
496         all_apis = desc;
497
498         INFO("API %s added", name);
499
500         return 0;
501
502 oom:
503         ERROR("out of memory");
504         errno = ENOMEM;
505 error:
506         return -1;
507 }
508
509 /**
510  * Adds a the 'alias' name to the api of 'name'.
511  * @params set the api set
512  * @param name the name of the api to alias
513  * @param alias the aliased name to add to the api of name
514  * @returns 0 in case of success or -1 in case
515  * of error with errno set:
516  *   - ENOENT if the api doesn't exist
517  *   - EEXIST if name (of alias) already registered
518  *   - ENOMEM when out of memory
519  */
520 int afb_apiset_add_alias(struct afb_apiset *set, const char *name, const char *alias)
521 {
522         struct api_desc *api;
523         struct api_alias *ali, **pali;
524
525         /* check alias doesn't already exist */
526         if (search(set, alias)) {
527                 ERROR("api of name %s already exists", alias);
528                 errno = EEXIST;
529                 goto error;
530         }
531
532         /* check aliased api exists */
533         api = search(set, name);
534         if (api == NULL) {
535                 ERROR("api of name %s doesn't exists", name);
536                 errno = ENOENT;
537                 goto error;
538         }
539
540         /* allocates and init the struct */
541         ali = malloc(sizeof *ali + strlen(alias));
542         if (ali == NULL) {
543                 ERROR("out of memory");
544                 errno = ENOMEM;
545                 goto error;
546         }
547         ali->api = api;
548         strcpy(ali->name, alias);
549
550         /* insert the alias in the sorted order */
551         pali = &set->aliases;
552         while(*pali && strcmp((*pali)->name, alias) < 0)
553                 pali = &(*pali)->next;
554         ali->next = *pali;
555         *pali = ali;
556         return 0;
557 error:
558         return -1;
559 }
560
561 int afb_apiset_is_alias(struct afb_apiset *set, const char *name)
562 {
563         struct api_desc *api = searchrec(set, name);
564         return api && strcasecmp(api->name, name);
565 }
566
567 const char *afb_apiset_unalias(struct afb_apiset *set, const char *name)
568 {
569         struct api_desc *api = searchrec(set, name);
570         return api ? api->name : NULL;
571 }
572
573 /**
574  * Delete from the 'set' the api of 'name'.
575  * @param set the set to be changed
576  * @param name the name of the API to remove
577  * @return 0 in case of success or -1 in case where the API doesn't exist.
578  */
579 int afb_apiset_del(struct afb_apiset *set, const char *name)
580 {
581         struct api_class *cla;
582         struct api_alias *ali, **pali;
583         struct api_desc *desc, **pdesc, *odesc;
584         int i, c;
585
586         /* search the alias */
587         pali = &set->aliases;
588         while ((ali = *pali)) {
589                 c = strcasecmp(ali->name, name);
590                 if (!c) {
591                         *pali = ali->next;
592                         free(ali);
593                         return 0;
594                 }
595                 if (c > 0)
596                         break;
597                 pali = &ali->next;
598         }
599
600         /* search the api */
601         for (i = 0 ; i < set->apis.count ; i++) {
602                 desc = set->apis.apis[i];
603                 c = strcasecmp(desc->name, name);
604                 if (c == 0) {
605                         /* remove from classes */
606                         for (cla = all_classes ; cla ; cla = cla->next)
607                                 api_array_del(&cla->providers, desc);
608
609                         /* unlink from the whole set and their requires */
610                         pdesc = &all_apis;
611                         while ((odesc = *pdesc) != desc) {
612                                 pdesc = &odesc->next;
613                         }
614                         *pdesc = odesc = desc->next;
615                         while (odesc) {
616                                 odesc = odesc->next;
617                         }
618
619                         /* remove references from classes */
620                         free(desc->require.classes.classes);
621
622                         /* drop the aliases */
623                         pali = &set->aliases;
624                         while ((ali = *pali)) {
625                                 if (ali->api != desc)
626                                         pali = &ali->next;
627                                 else {
628                                         *pali = ali->next;
629                                         free(ali);
630                                 }
631                         }
632
633                         /* unref the api */
634                         if (desc->api.itf->unref)
635                                 desc->api.itf->unref(desc->api.closure);
636
637                         set->apis.count--;
638                         while(i < set->apis.count) {
639                                 set->apis.apis[i] = set->apis.apis[i + 1];
640                                 i++;
641                         }
642                         free(desc);
643                         return 0;
644                 }
645                 if (c > 0)
646                         break;
647         }
648         errno = ENOENT;
649         return -1;
650 }
651
652 /**
653  * Get from the 'set' the API of 'name' in 'api' with fallback to subset or default api
654  * @param set the set of API
655  * @param name the name of the API to get
656  * @param rec if not zero look also recursively in subsets
657  * @return the api pointer in case of success or NULL in case of error
658  */
659 static struct api_desc *lookup(struct afb_apiset *set, const char *name, int rec)
660 {
661         struct api_desc *result;
662
663         result = search(set, name);
664         while (!result) {
665                 /* lacking the api, try onlack behaviour */
666                 if (set->onlack.callback && set->onlack.callback(set->onlack.closure, set, name) > 0) {
667                         result = search(set, name);
668                         if (result)
669                                 break;
670                 }
671                 if (!rec || !(set = set->subset))
672                         break;
673                 result = search(set, name);
674         }
675         return result;
676 }
677
678 /**
679  * Get from the 'set' the API of 'name' in 'api'
680  * @param set the set of API
681  * @param name the name of the API to get
682  * @param rec if not zero look also recursively in subsets
683  * @return the api pointer in case of success or NULL in case of error
684  */
685 const struct afb_api_item *afb_apiset_lookup(struct afb_apiset *set, const char *name, int rec)
686 {
687         struct api_desc *i;
688
689         i = lookup(set, name, rec);
690         if (i)
691                 return &i->api;
692         errno = ENOENT;
693         return NULL;
694 }
695
696 static int start_api(struct api_desc *api, int share_session, int onneed);
697
698 /**
699  * Start the apis of the 'array'
700  * The attribute 'share_session' is sent to the start function.
701  */
702 static int start_array_apis(struct api_array *array, int share_session)
703 {
704         int i, rc = 0, rc2;
705
706         i = array->count;
707         while (i) {
708                 rc2 = start_api(array->apis[--i], share_session, 1);
709                 if (rc2 < 0) {
710                         rc = rc2;
711                 }
712         }
713         return rc;
714 }
715
716 /**
717  * Start the class 'cla' (start the apis that provide it).
718  * The attribute 'share_session' is sent to the start function.
719  */
720 static int start_class(struct api_class *cla, int share_session)
721 {
722         return start_array_apis(&cla->providers, share_session);
723 }
724
725 /**
726  * Start the classes of the 'array'
727  * The attribute 'share_session' is sent to the start function.
728  */
729 static int start_array_classes(struct api_array *array, int share_session)
730 {
731         int i, rc = 0, rc2;
732
733         i = array->count;
734         while (i) {
735                 rc2 = start_class(array->classes[--i], share_session);
736                 if (rc2 < 0) {
737                         rc = rc2;
738                 }
739         }
740         return rc;
741 }
742
743 /**
744  * Start the depends of the 'array'
745  * The attribute 'share_session' is sent to the start function.
746  */
747 static int start_array_depends(struct api_array *array, int share_session)
748 {
749         struct api_desc *api;
750         int i, rc = 0, rc2;
751
752         i = array->count;
753         while (i) {
754                 i--;
755                 api = searchrec(array->depends[i]->set, array->depends[i]->name);
756                 if (!api)
757                         rc = -1;
758                 else {
759                         rc2 = start_api(api, share_session, 1);
760                         if (rc2 < 0) {
761                                 rc = rc2;
762                         }
763                 }
764         }
765         return rc;
766 }
767
768 /**
769  * Starts the service 'api'.
770  * @param api the api
771  * @param share_session if true start the servic"e in a shared session
772  *                      if false start it in its own session
773  * @param onneed if true start the service if possible, if false the api
774  *               must be a service
775  * @return a positive number on success
776  */
777 static int start_api(struct api_desc *api, int share_session, int onneed)
778 {
779         int rc;
780
781         if (api->status == 0)
782                 return 0;
783         else if (api->status > 0) {
784                 errno = api->status;
785                 return -1;
786         }
787
788         INFO("API %s starting...", api->name);
789         rc = start_array_classes(&api->require.classes, share_session);
790         rc = start_array_depends(&api->require.apis, share_session);
791         if (api->api.itf->service_start) {
792                 api->status = EBUSY;
793                 rc = api->api.itf->service_start(api->api.closure, share_session, onneed);
794                 if (rc < 0) {
795                         api->status = errno ?: ECANCELED;
796                         ERROR("The api %s failed to start (%d)", api->name, rc);
797                         return -1;
798                 }
799         } else if (!onneed) {
800                 /* already started: it is an error */
801                 ERROR("The api %s is not a startable service", api->name);
802                 api->status = EINVAL;
803                 return -1;
804         }
805         NOTICE("API %s started", api->name);
806         api->status = 0;
807         return 0;
808 }
809
810 /**
811  * Get from the 'set' the API of 'name' in 'api'
812  * @param set the set of API
813  * @param name the name of the API to get
814  * @param rec if not zero look also recursively in subsets
815  * @return 0 in case of success or -1 in case of error
816  */
817 const struct afb_api_item *afb_apiset_lookup_started(struct afb_apiset *set, const char *name, int rec)
818 {
819         struct api_desc *i;
820
821         i = lookup(set, name, rec);
822         if (i)
823                 return i->status && start_api(i, 1, 1) ? NULL : &i->api;
824         errno = ENOENT;
825         return NULL;
826 }
827
828 /**
829  * Starts a service by its 'api' name.
830  * @param set the api set
831  * @param name name of the service to start
832  * @param share_session if true start the servic"e in a shared session
833  *                      if false start it in its own session
834  * @param onneed if true start the service if possible, if false the api
835  *               must be a service
836  * @return a positive number on success
837  */
838 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
839 {
840         struct api_desc *a;
841
842         a = searchrec(set, name);
843         if (!a) {
844                 ERROR("can't find service %s", name);
845                 errno = ENOENT;
846                 return -1;
847         }
848
849         return start_api(a, share_session, onneed);
850 }
851
852 /**
853  * Starts all possible services but stops at first error.
854  * @param set the api set
855  * @param share_session if true start the servic"e in a shared session
856  *                      if false start it in its own session
857  * @return 0 on success or a negative number when an error is found
858  */
859 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
860 {
861         int rc;
862         int i;
863
864         while (set) {
865                 i = 0;
866                 while (i < set->apis.count) {
867                         rc = start_api(set->apis.apis[i], share_session, 1);
868                         if (rc < 0)
869                                 return rc;
870                         i++;
871                 }
872                 set = set->subset;
873         }
874         return 0;
875 }
876
877 /**
878  * Ask to update the hook flags of the 'api'
879  * @param set the api set
880  * @param name the api to update (NULL updates all)
881  */
882 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
883 {
884         struct api_desc **i, **e, *d;
885
886         if (!name) {
887                 i = set->apis.apis;
888                 e = &set->apis.apis[set->apis.count];
889                 while (i != e) {
890                         d = *i++;
891                         if (d->api.itf->update_hooks)
892                                 d->api.itf->update_hooks(d->api.closure);
893                 }
894         } else {
895                 d = searchrec(set, name);
896                 if (d && d->api.itf->update_hooks)
897                         d->api.itf->update_hooks(d->api.closure);
898         }
899 }
900
901 /**
902  * Set the logmask of the 'api' to 'mask'
903  * @param set the api set
904  * @param name the api to set (NULL set all)
905  */
906 void afb_apiset_set_logmask(struct afb_apiset *set, const char *name, int mask)
907 {
908         int i;
909         struct api_desc *d;
910
911         if (!name) {
912                 for (i = 0 ; i < set->apis.count ; i++) {
913                         d = set->apis.apis[i];;
914                         if (d->api.itf->set_logmask)
915                                 d->api.itf->set_logmask(d->api.closure, mask);
916                 }
917         } else {
918                 d = searchrec(set, name);
919                 if (d && d->api.itf->set_logmask)
920                         d->api.itf->set_logmask(d->api.closure, mask);
921         }
922 }
923
924 /**
925  * Get the logmask level of the 'api'
926  * @param set the api set
927  * @param name the api to get
928  * @return the logmask level or -1 in case of error
929  */
930 int afb_apiset_get_logmask(struct afb_apiset *set, const char *name)
931 {
932         const struct api_desc *i;
933
934         i = name ? searchrec(set, name) : NULL;
935         if (!i) {
936                 errno = ENOENT;
937                 return -1;
938         }
939
940         if (!i->api.itf->get_logmask)
941                 return logmask;
942
943         return i->api.itf->get_logmask(i->api.closure);
944 }
945
946 /**
947  * Get the description of the API of 'name'
948  * @param set the api set
949  * @param name the api whose description is required
950  * @return the description or NULL
951  */
952 struct json_object *afb_apiset_describe(struct afb_apiset *set, const char *name)
953 {
954         const struct api_desc *i;
955
956         i = name ? searchrec(set, name) : NULL;
957         return i && i->api.itf->describe ? i->api.itf->describe(i->api.closure) : NULL;
958 }
959
960 struct get_names {
961         union  {
962                 struct {
963                         size_t count;
964                         size_t size;
965                 };
966                 struct {
967                         const char **ptr;
968                         char *data;
969                 };
970         };
971         int type;
972 };
973
974 static void get_names_count(void *closure, struct afb_apiset *set, const char *name, int isalias)
975 {
976         struct get_names *gc = closure;
977         if ((1 + isalias) & gc->type) {
978                 gc->size += strlen(name);
979                 gc->count++;
980         }
981 }
982
983 static void get_names_value(void *closure, struct afb_apiset *set, const char *name, int isalias)
984 {
985         struct get_names *gc = closure;
986         if ((1 + isalias) & gc->type) {
987                 *gc->ptr++ = gc->data;
988                 gc->data = stpcpy(gc->data, name) + 1;
989         }
990 }
991
992 #if !defined(APISET_NO_SORT)
993 static int get_names_sortcb(const void *a, const void *b)
994 {
995         return strcasecmp(*(const char **)a, *(const char **)b);
996 }
997 #endif
998
999 /**
1000  * Get the list of api names
1001  * @param set the api set
1002  * @param rec recursive
1003  * @param type expected type: 1 names, 3 names+aliases, 2 aliases
1004  * @return a NULL terminated array of api names. Must be freed.
1005  */
1006 const char **afb_apiset_get_names(struct afb_apiset *set, int rec, int type)
1007 {
1008         struct get_names gc;
1009         size_t size;
1010         const char **names;
1011
1012         gc.count = gc.size = 0;
1013         gc.type = type >= 1 && type <= 3 ? type : 1;
1014         afb_apiset_enum(set, rec, get_names_count, &gc);
1015
1016         size = gc.size + gc.count * (1 + sizeof *names) + sizeof(*names);
1017         names = malloc(size);
1018
1019         if (!names)
1020                 errno = ENOMEM;
1021         else {
1022                 gc.data = (char*)&names[gc.count + 1];
1023                 gc.ptr = names;
1024                 afb_apiset_enum(set, rec, get_names_value, &gc);
1025 #if !defined(APISET_NO_SORT)
1026                 qsort(names, gc.ptr - names, sizeof *names, get_names_sortcb);
1027 #endif
1028                 *gc.ptr = NULL;
1029         }
1030         return names;
1031 }
1032
1033 /**
1034  * Enumerate the api names to a callback.
1035  * @param set the api set
1036  * @param rec should the enumeration be recursive
1037  * @param callback the function to call for each name
1038  * @param closure the closure for the callback
1039  */
1040 void afb_apiset_enum(
1041         struct afb_apiset *set,
1042         int rec,
1043         void (*callback)(void *closure, struct afb_apiset *set, const char *name, int isalias),
1044         void *closure)
1045 {
1046         int i;
1047         struct afb_apiset *iset;
1048         struct api_desc *d;
1049         struct api_alias *a;
1050
1051         iset = set;
1052         while (iset) {
1053                 for (i = 0 ; i < set->apis.count ; i++) {
1054                         d = set->apis.apis[i];;
1055                         if (searchrec(set, d->name) == d)
1056                                 callback(closure, iset, d->name, 0);
1057                 }
1058                 a = iset->aliases;
1059                 while (a) {
1060                         if (searchrec(set, a->name) == a->api)
1061                                 callback(closure, iset, a->name, 1);
1062                         a = a->next;
1063                 }
1064                 iset = rec ? iset->subset : NULL;
1065         }
1066 }
1067
1068 /**
1069  * Declare that the api of 'name' requires the api of name 'required'.
1070  * The api is searched in the apiset 'set' and if 'rec' isn't null also in its subset.
1071  * Returns 0 if the declaration successed or -1 in case of failure
1072  * (ENOMEM: allocation failure, ENOENT: api name not found)
1073  */
1074 int afb_apiset_require(struct afb_apiset *set, const char *name, const char *required)
1075 {
1076         struct api_desc *a;
1077         struct api_depend *d;
1078         int rc = -1;
1079
1080         a = searchrec(set, name);
1081         if (!a)
1082                 errno = ENOENT;
1083         else {
1084                 d = malloc(strlen(required) + sizeof *d);
1085                 if (!d)
1086                         errno = ENOMEM;
1087                 else {
1088                         d->set = set;
1089                         strcpy(d->name, required);
1090                         rc = api_array_add(&a->require.apis, d);
1091                 }
1092         }
1093         return rc;
1094 }
1095
1096 /**
1097  * Declare that the api of name 'apiname' requires the class of name 'classname'.
1098  * Returns 0 if the declaration successed or -1 in case of failure
1099  * (ENOMEM: allocation failure, ENOENT: api name not found)
1100  */
1101 int afb_apiset_require_class(struct afb_apiset *set, const char *apiname, const char *classname)
1102 {
1103         struct api_desc *a = searchrec(set, apiname);
1104         struct api_class *c = class_search(classname, 1);
1105         return a && c ? api_array_add(&a->require.classes, c) : (errno = ENOENT, -1);
1106 }
1107
1108 /**
1109  * Declare that the api of name 'apiname' provides the class of name 'classname'
1110  * Returns 0 if the declaration successed or -1 in case of failure
1111  * (ENOMEM: allocation failure, ENOENT: api name not found)
1112  */
1113 int afb_apiset_provide_class(struct afb_apiset *set, const char *apiname, const char *classname)
1114 {
1115         struct api_desc *a = searchrec(set, apiname);
1116         struct api_class *c = class_search(classname, 1);
1117         return a && c ? api_array_add(&c->providers, a) : (errno = ENOENT, -1);
1118 }
1119
1120 /**
1121  * Start any API that provides the class of name 'classname'
1122  * The attribute 'share_session' is sent to the start function.
1123  */
1124 int afb_apiset_class_start(const char *classname, int share_session)
1125 {
1126         struct api_class *cla = class_search(classname, 0);
1127         return cla ? start_class(cla, share_session) : (errno = ENOENT, -1);
1128 }
1129