9ffffdb3b5d22ed632f419c7e7df20bef83e09b4
[src/app-framework-binder.git] / src / afb-apis.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-apis.h"
29 #include "afb-context.h"
30 #include "afb-hook.h"
31 #include <afb/afb-req-itf.h>
32
33 struct api_desc {
34         const char *name;
35         struct afb_api api;
36 };
37
38 static struct api_desc *apis_array = NULL;
39 static int apis_count = 0;
40
41 /**
42  * Checks wether 'name' is a valid API name.
43  * @return 1 if valid, 0 otherwise
44  */
45 int afb_apis_is_valid_api_name(const char *name)
46 {
47         unsigned char c;
48
49         c = (unsigned char)*name;
50         if (c == 0)
51                 /* empty names aren't valid */
52                 return 0;
53
54         do {
55                 if (c < (unsigned char)'\x80') {
56                         switch(c) {
57                         default:
58                                 if (c > ' ')
59                                         break;
60                         case '"':
61                         case '#':
62                         case '%':
63                         case '&':
64                         case '\'':
65                         case '/':
66                         case '?':
67                         case '`':
68                         case '\\':
69                         case '\x7f':
70                                 return 0;
71                         }
72                 }
73                 c = (unsigned char)*++name;
74         } while(c != 0);
75         return 1;
76 }
77
78 /**
79  * Adds the api of 'name' described by 'api'.
80  * @param name the name of the api to add (have to survive, not copied!)
81  * @param api the api
82  * @returns 0 in case of success or -1 in case
83  * of error with errno set:
84  *   - EINVAL if name isn't valid
85  *   - EEXIST if name already registered
86  *   - ENOMEM when out of memory
87  */
88 int afb_apis_add(const char *name, struct afb_api api)
89 {
90         struct api_desc *apis;
91         int i, c;
92
93         /* Checks the api name */
94         if (!afb_apis_is_valid_api_name(name)) {
95                 ERROR("invalid api name forbidden (name is '%s')", name);
96                 errno = EINVAL;
97                 goto error;
98         }
99
100         /* check previously existing plugin */
101         for (i = 0 ; i < apis_count ; i++) {
102                 c = strcasecmp(apis_array[i].name, name);
103                 if (c == 0) {
104                         ERROR("api of name %s already exists", name);
105                         errno = EEXIST;
106                         goto error;
107                 }
108                 if (c > 0)
109                         break;
110         }
111
112         /* allocates enough memory */
113         apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis);
114         if (apis == NULL) {
115                 ERROR("out of memory");
116                 errno = ENOMEM;
117                 goto error;
118         }
119         apis_array = apis;
120
121         /* copy higher part of the array */
122         c = apis_count;
123         while (c > i) {
124                 apis_array[c] = apis_array[c - 1];
125                 c--;
126         }
127
128         /* record the plugin */
129         apis = &apis_array[i];
130         apis->api = api;
131         apis->name = name;
132         apis_count++;
133
134         return 0;
135
136 error:
137         return -1;
138 }
139
140 /**
141  * Dispatch the request 'req' with the 'context' to the
142  * method of 'api' and 'verb'.
143  * @param req the request to dispatch
144  * @param context the context of the request
145  * @param api the api of the verb
146  * @param verb the verb within the api
147  */
148 void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
149 {
150         int i, c, up, lo;
151         const struct api_desc *a;
152
153         /* init hooking the request */
154         req = afb_hook_req_call(req, context, api, verb);
155
156         /* dichotomic search of the api */
157         /* initial slice */
158         lo = 0;
159         up = apis_count;
160         for (;;) {
161                 /* check remaining slice */
162                 if (lo >= up) {
163                         /* empty ?! */
164                         afb_req_fail(req, "fail", "api not found");
165                         break;
166                 }
167                 /* check the mid of the slice */
168                 i = (lo + up) >> 1;
169                 a = &apis_array[i];
170                 c = strcasecmp(a->name, api);
171                 if (c == 0) {
172                         /* api found */
173                         context->api_key = a->api.closure;
174                         a->api.call(a->api.closure, req, context, verb);
175                         break;
176                 }
177                 /* update the slice */
178                 if (c < 0)
179                         lo = i + 1;
180                 else
181                         up = i;
182         }
183 }
184
185 int afb_apis_start_service(const char *api, int share_session, int onneed)
186 {
187         int i;
188
189         for (i = 0 ; i < apis_count ; i++) {
190                 if (!strcasecmp(apis_array[i].name, api))
191                         return apis_array[i].api.service_start(apis_array[i].api.closure, share_session, onneed);
192         }
193         ERROR("can't find service %s", api);
194         return -1;
195 }
196
197 int afb_apis_start_all_services(int share_session)
198 {
199         int i, rc;
200
201         for (i = 0 ; i < apis_count ; i++) {
202                 rc = apis_array[i].api.service_start(apis_array[i].api.closure, share_session, 1);
203                 if (rc < 0)
204                         return rc;
205         }
206         return 0;
207 }
208