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