2 * Copyright (C) 2016 "IoT.bzh"
3 * Author "Fulup Ar Foll"
4 * Author José Bollo <jose.bollo@iot.bzh>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * Contain all generic part to handle REST/API
21 * https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'largepost.c']
33 #include <sys/types.h>
36 #include "../include/local-def.h"
38 #include "afb-req-itf.h"
42 AFB_plugin *plugin; /* descriptor */
45 void *handle; /* context of dlopen */
48 static struct api_desc *apis_array = NULL;
49 static int apis_count = 0;
51 static const char plugin_register_function[] = "pluginRegister";
58 void afb_apis_free_context(int apiidx, void *context)
62 assert(0 <= apiidx && apiidx < apis_count);
63 cb = apis_array[apiidx].plugin->freeCtxCB;
70 const struct AFB_restapi *afb_apis_get(int apiidx, int verbidx)
72 assert(0 <= apiidx && apiidx < apis_count);
73 return &apis_array[apiidx].plugin->apis[verbidx];
76 int afb_apis_get_verbidx(int apiidx, const char *name)
78 const struct AFB_restapi *apis;
81 assert(0 <= apiidx && apiidx < apis_count);
82 apis = apis_array[apiidx].plugin->apis;
83 for (idx = 0 ; apis[idx].name ; idx++)
84 if (!strcasecmp(apis[idx].name, name))
89 int afb_apis_get_apiidx(const char *prefix, size_t length)
92 const struct api_desc *a;
95 length = strlen(prefix);
97 for (i = 0 ; i < apis_count ; i++) {
99 if (a->prefixlen == length && !strcasecmp(a->prefix, prefix))
105 int afb_apis_add_plugin(const char *path)
107 struct api_desc *apis;
109 AFB_plugin *(*pluginRegisterFct) (void);
113 // This is a loadable library let's check if it's a plugin
114 handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
115 if (handle == NULL) {
116 fprintf(stderr, "[%s] not loadable, continuing...\n", path);
120 /* retrieves the register function */
121 pluginRegisterFct = dlsym(handle, plugin_register_function);
122 if (!pluginRegisterFct) {
123 fprintf(stderr, "[%s] not an AFB plugin, continuing...\n", path);
127 fprintf(stderr, "[%s] is a valid AFB plugin\n", path);
129 /* allocates enough memory */
130 apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis);
132 fprintf(stderr, "ERROR: plugin [%s] memory missing. continuing...\n", path);
137 /* init the plugin */
138 plugin = pluginRegisterFct();
139 if (plugin == NULL) {
140 fprintf(stderr, "ERROR: plugin [%s] register function failed. continuing...\n", path);
144 /* check the returned structure */
145 if (plugin->type != AFB_PLUGIN_JSON) {
146 fprintf(stderr, "ERROR: plugin [%s] invalid type %d...\n", path, plugin->type);
149 if (plugin->prefix == NULL || *plugin->prefix == 0) {
150 fprintf(stderr, "ERROR: plugin [%s] bad prefix...\n", path);
153 if (plugin->info == NULL || *plugin->info == 0) {
154 fprintf(stderr, "ERROR: plugin [%s] bad description...\n", path);
157 if (plugin->apis == NULL) {
158 fprintf(stderr, "ERROR: plugin [%s] no APIs...\n", path);
162 /* check previously existing plugin */
163 len = strlen(plugin->prefix);
164 if (afb_apis_get_apiidx(plugin->prefix, len) >= 0) {
165 fprintf(stderr, "ERROR: plugin [%s] prefix %s duplicated...\n", path, plugin->prefix);
169 /* record the plugin */
171 fprintf(stderr, "Loading plugin[%lu] prefix=[%s] info=%s\n", (unsigned long)apis_count, plugin->prefix, plugin->info);
172 apis = &apis_array[apis_count];
173 apis->plugin = plugin;
174 apis->prefixlen = len;
175 apis->prefix = plugin->prefix;
176 apis->handle = handle;
187 static int adddirs(char path[PATH_MAX], size_t end)
191 struct dirent ent, *result;
194 /* open the DIR now */
197 fprintf(stderr, "ERROR in scanning plugin directory %s, %m\n", path);
201 fprintf(stderr, "Scanning dir=[%s] for plugins\n", path);
203 /* scan each entry */
207 readdir_r(dir, &ent, &result);
211 len = strlen(ent.d_name);
212 if (len + end >= PATH_MAX) {
213 fprintf(stderr, "path too long for %s\n", ent.d_name);
216 memcpy(&path[end], ent.d_name, len+1);
217 if (ent.d_type == DT_DIR) {
218 /* case of directories */
219 if (ent.d_name[0] == '.') {
222 if (ent.d_name[1] == '.' && len == 2)
225 rc = adddirs(path, end+len);;
226 } else if (ent.d_type == DT_REG) {
228 if (!strstr(ent.d_name, ".so"))
230 rc = afb_apis_add_plugin(path);
237 int afb_apis_add_directory(const char *path)
240 char buffer[PATH_MAX];
242 length = strlen(path);
243 if (length >= sizeof(buffer)) {
244 fprintf(stderr, "path too long %lu [%.99s...]\n", (unsigned long)length, path);
248 memcpy(buffer, path, length + 1);
249 return adddirs(buffer, length);
252 int afb_apis_add_path(const char *path)
257 rc = stat(path, &st);
259 fprintf(stderr, "Invalid plugin path [%s]: %m\n", path);
260 else if (S_ISDIR(st.st_mode))
261 rc = afb_apis_add_directory(path);
263 rc = afb_apis_add_plugin(path);
267 int afb_apis_add_pathset(const char *pathset)
269 static char sep[] = ":";
273 ps = strdupa(pathset);
275 p = strsep(&ps, sep);
278 rc = afb_apis_add_path(p);
283 // Check of apiurl is declare in this plugin and call it
284 extern __thread sigjmp_buf *error_handler;
285 static int callPluginApi(AFB_request * request)
287 sigjmp_buf jmpbuf, *older;
289 // save context before calling the API
290 status = setjmp(jmpbuf);
295 // Trigger a timer to protect from unacceptable long time execution
296 if (request->config->apiTimeout > 0)
297 alarm((unsigned)request->config->apiTimeout);
299 older = error_handler;
300 error_handler = &jmpbuf;
301 doCallPluginApi(request, apiidx, verbidx, context);
302 error_handler = older;
304 // cancel timeout and plugin signal handle before next call
310 static void handle(struct afb_req req, const struct api_desc *api, const struct AFB_restapi *verb)
314 request.uuid = request.url = "fake";
315 request.prefix = api->prefix;
316 request.method = verb->name;
317 request.context = NULL;
318 request.restfull = 0;
320 request.config = NULL;
323 switch(verb->session) {
324 case AFB_SESSION_CREATE:
325 case AFB_SESSION_RENEW:
328 case AFB_SESSION_CLOSE:
329 case AFB_SESSION_CHECK:
332 case AFB_SESSION_NONE:
336 verb->callback(&request, NULL);
338 if (verb->session == AFB_SESSION_CLOSE)
342 int afb_apis_handle(struct afb_req req, const char *api, size_t lenapi, const char *verb, size_t lenverb)
345 const struct api_desc *a;
346 const struct AFB_restapi *v;
348 //fprintf(stderr,"afb_apis_handle prefix:%.*s verb:%.*s\n",(int)lenapi,api,(int)lenverb,verb);
350 for (i = 0 ; i < apis_count ; i++, a++) {
351 if (a->prefixlen == lenapi && !strncasecmp(a->prefix, api, lenapi)) {
352 //fprintf(stderr,"afb_apis_handle found prefix:%.*s -> %s\n",(int)lenapi,api,a->prefix);
354 for (j = 0 ; v->name ; j++, v++) {
355 if (!strncasecmp(v->name, verb, lenverb) && !v->name[lenverb]) {
356 //fprintf(stderr,"afb_apis_handle found prefix:%.*s verb:%.*s -> %s/%s\n",(int)lenapi,api,(int)lenverb,verb,a->prefix,v->name);