Add default api to apiset
[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 /**
151  * Get the API timeout of the set
152  * @param set the api set
153  * @return the timeout in seconds
154  */
155 int afb_apiset_timeout_get(struct afb_apiset *set)
156 {
157         return set->timeout;
158 }
159
160 /**
161  * Set the API timeout of the set
162  * @param set the api set
163  * @param to the timeout in seconds
164  */
165 void afb_apiset_timeout_set(struct afb_apiset *set, int to)
166 {
167         set->timeout = to;
168 }
169
170 /**
171  * Get the subset of the set
172  * @param set the api set
173  * @return the subset of set
174  */
175 struct afb_apiset *afb_apiset_subset_get(struct afb_apiset *set)
176 {
177         return set->subset;
178 }
179
180 /**
181  * Set the subset of the set
182  * @param set the api set
183  * @param subset the subset to set
184  */
185 void afb_apiset_subset_set(struct afb_apiset *set, struct afb_apiset *subset)
186 {
187         struct afb_apiset *tmp;
188         if (subset == set) {
189                 /* avoid infinite loop */
190                 subset = NULL;
191         }
192         tmp = set->subset;
193         set->subset = afb_apiset_addref(subset);
194         afb_apiset_unref(tmp);
195 }
196
197 /**
198  * Check if the apiset has a default api
199  * @param set the api set
200  * @return 1 if the set has a default api or 0 otherwise
201  */
202 int afb_apiset_default_api_exist(struct afb_apiset *set)
203 {
204         return !!set->defapi.itf;
205 }
206
207 /**
208  * Get the default api of the api set.
209  * @param set the api set
210  * @param api where to store the default api
211  * @return 0 in case of success or -1 when no default api is set
212  */
213 int afb_apiset_default_api_get(struct afb_apiset *set, struct afb_api *api)
214 {
215         if (set->defapi.itf) {
216                 *api = set->defapi;
217                 return 0;
218         }
219         errno = ENOENT;
220         return -1;
221 }
222
223 /**
224  * Set the default api of the api set
225  * @param set the api set
226  * @param subset the subset to set
227  */
228 void afb_apiset_default_api_set(struct afb_apiset *set, struct afb_api api)
229 {
230         set->defapi = api;
231 }
232
233 /**
234  * Set the default api of the api set
235  * @param set the api set
236  */
237 void afb_apiset_default_api_drop(struct afb_apiset *set)
238 {
239         set->defapi.itf = NULL;
240 }
241
242 /**
243  * Adds the api of 'name' described by 'api'.
244  * @param set the api set
245  * @param name the name of the api to add (have to survive, not copied!)
246  * @param api the api
247  * @returns 0 in case of success or -1 in case
248  * of error with errno set:
249  *   - EINVAL if name isn't valid
250  *   - EEXIST if name already registered
251  *   - ENOMEM when out of memory
252  */
253 int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api)
254 {
255         struct api_desc *apis;
256         int i, c;
257
258         /* Checks the api name */
259         if (!afb_api_is_valid_name(name)) {
260                 ERROR("invalid api name forbidden (name is '%s')", name);
261                 errno = EINVAL;
262                 goto error;
263         }
264
265         /* check previously existing plugin */
266         for (i = 0 ; i < set->count ; i++) {
267                 c = strcasecmp(set->apis[i].name, name);
268                 if (c == 0) {
269                         ERROR("api of name %s already exists", name);
270                         errno = EEXIST;
271                         goto error;
272                 }
273                 if (c > 0)
274                         break;
275         }
276
277         /* allocates enough memory */
278         c = (set->count + INCR) & ~(INCR - 1);
279         apis = realloc(set->apis, ((unsigned)c) * sizeof * apis);
280         if (apis == NULL) {
281                 ERROR("out of memory");
282                 errno = ENOMEM;
283                 goto error;
284         }
285         set->apis = apis;
286
287         /* copy higher part of the array */
288         apis += i;
289         if (i != set->count)
290                 memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis);
291
292         /* record the plugin */
293         apis->api = api;
294         apis->name = name;
295         set->count++;
296
297         NOTICE("API %s added", name);
298
299         return 0;
300
301 error:
302         return -1;
303 }
304
305 /**
306  * Delete from the 'set' the api of 'name'.
307  * @param set the set to be changed
308  * @param name the name of the API to remove
309  * @return 0 in case of success or -1 in case where the API doesn't exist.
310  */
311 int afb_apiset_del(struct afb_apiset *set, const char *name)
312 {
313         int i, c;
314
315         /* search the api */
316         for (i = 0 ; i < set->count ; i++) {
317                 c = strcasecmp(set->apis[i].name, name);
318                 if (c == 0) {
319                         set->count--;
320                         while(i < set->count) {
321                                 set->apis[i] = set->apis[i + 1];
322                                 i++;
323                         }
324                         return 0;
325                 }
326                 if (c > 0)
327                         break;
328         }
329         errno = ENOENT;
330         return -1;
331 }
332
333 /**
334  * Get from the 'set' the API of 'name' in '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 = search(set, name);
345         if (i) {
346                 *api = i->api;
347                 return 0;
348         }
349
350         if (set->subset && 0 == afb_apiset_get(set->subset, name, api))
351                 return 0;
352
353         if (set->defapi.itf) {
354                 *api = set->defapi;
355                 return 0;
356         }
357
358         errno = ENOENT;
359         return -1;
360 }
361
362 /**
363  * Starts a service by its 'api' name.
364  * @param set the api set
365  * @param name name of the service to start
366  * @param share_session if true start the servic"e in a shared session
367  *                      if false start it in its own session
368  * @param onneed if true start the service if possible, if false the api
369  *               must be a service
370  * @return a positive number on success
371  */
372 int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed)
373 {
374         const struct api_desc *a;
375
376         a = search(set, name);
377         if (!a) {
378                 ERROR("can't find service %s", name);
379                 errno = ENOENT;
380                 return -1;
381         }
382
383         if (a->api.itf->service_start)
384                 return a->api.itf->service_start(a->api.closure, share_session, onneed, set);
385
386         if (onneed)
387                 return 0;
388
389         /* already started: it is an error */
390         ERROR("The api %s is not a startable service", name);
391         errno = EINVAL;
392         return -1;
393 }
394
395 /**
396  * Starts all possible services but stops at first error.
397  * @param set the api set
398  * @param share_session if true start the servic"e in a shared session
399  *                      if false start it in its own session
400  * @return 0 on success or a negative number when an error is found
401  */
402 int afb_apiset_start_all_services(struct afb_apiset *set, int share_session)
403 {
404         int rc;
405         const struct api_desc *i, *e;
406
407         i = set->apis;
408         e = &set->apis[set->count];
409         while (i != e) {
410                 if (i->api.itf->service_start) {
411                         rc = i->api.itf->service_start(i->api.closure, share_session, 1, set);
412                         if (rc < 0)
413                                 return rc;
414                 }
415                 i++;
416         }
417         return 0;
418 }
419
420 /**
421  * Ask to update the hook flags of the 'api'
422  * @param set the api set
423  * @param name the api to update (NULL updates all)
424  */
425 void afb_apiset_update_hooks(struct afb_apiset *set, const char *name)
426 {
427         const struct api_desc *i, *e;
428
429         if (!name) {
430                 i = set->apis;
431                 e = &set->apis[set->count];
432         } else {
433                 i = search(set, name);
434                 e = &i[!!i];
435         }
436         while (i != e) {
437                 if (i->api.itf->update_hooks)
438                         i->api.itf->update_hooks(i->api.closure);
439                 i++;
440         }
441 }
442
443 /**
444  * Set the verbosity level of the 'api'
445  * @param set the api set
446  * @param name the api to set (NULL set all)
447  */
448 void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level)
449 {
450         const struct api_desc *i, *e;
451
452         if (!name) {
453                 i = set->apis;
454                 e = &set->apis[set->count];
455         } else {
456                 i = search(set, name);
457                 e = &i[!!i];
458         }
459         while (i != e) {
460                 if (i->api.itf->set_verbosity)
461                         i->api.itf->set_verbosity(i->api.closure, level);
462                 i++;
463         }
464 }
465
466 /**
467  * Set the verbosity level of the 'api'
468  * @param set the api set
469  * @param name the api to set (NULL set all)
470  */
471 int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name)
472 {
473         const struct api_desc *i;
474
475         i = name ? search(set, name) : NULL;
476         if (!i) {
477                 errno = ENOENT;
478                 return -1;
479         }
480         if (!i->api.itf->get_verbosity)
481                 return 0;
482
483         return i->api.itf->get_verbosity(i->api.closure);
484 }
485
486 /**
487  * Get the list of api names
488  * @param set the api set
489  * @return a NULL terminated array of api names. Must be freed.
490  */
491 const char **afb_apiset_get_names(struct afb_apiset *set)
492 {
493         size_t size;
494         char *dest;
495         const char **names;
496         int i;
497
498         size = set->count * (1 + sizeof(*names)) + sizeof(*names);
499         for (i = 0 ; i < set->count ; i++)
500                 size += strlen(set->apis[i].name);
501
502         names = malloc(size);
503         if (!names)
504                 errno = ENOMEM;
505         else {
506                 dest = (void*)&names[set->count+1];
507                 for (i = 0 ; i < set->count ; i++) {
508                         names[i] = dest;
509                         dest = stpcpy(dest, set->apis[i].name) + 1;
510                 }
511                 names[i] = NULL;
512         }
513         return names;
514 }
515