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