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