splitting rest-api in two parts
[src/app-framework-binder.git] / src / afb-plugins.c
1 /*
2  * Copyright (C) 2016 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  * Author José Bollo <jose.bollo@iot.bzh>
5  *
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.
10  *
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.
15  *
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/>.
18  * 
19  * Contain all generic part to handle REST/API
20  * 
21  *  https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'largepost.c']
22  */
23
24 #define _GNU_SOURCE
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <dirent.h>
29 #include <dlfcn.h>
30 #include <unistd.h>
31 #include <limits.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #include "../include/local-def.h"
36
37 #include "afb-plugins.h"
38
39 static const char plugin_register_function[] = "pluginRegister";
40
41 AFB_plugin *afb_plugins_search(AFB_session *session, const char *prefix, size_t length)
42 {
43         int i, n;
44         AFB_plugin **plugins, *p;
45
46         if (!length)
47                 length = strlen(prefix);
48
49         n = session->config->pluginCount;
50         plugins = session->plugins;
51
52         for (i = 0 ; i < n ; i++) {
53                 p = plugins[i];
54                 if (p->prefixlen == length && !strcmp(p->prefix, prefix))
55                         return p;
56         }
57         return NULL;
58 }
59
60 int afb_plugins_add_plugin(AFB_session *session, const char *path)
61 {
62         AFB_plugin *desc, *check, **plugins;
63         AFB_plugin *(*pluginRegisterFct) (void);
64         void *handle;
65         size_t len;
66
67         // This is a loadable library let's check if it's a plugin
68         handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
69         if (handle == NULL) {
70                 fprintf(stderr, "[%s] not loadable, continuing...\n", path);
71                 goto error;
72         }
73
74         /* retrieves the register function */
75         pluginRegisterFct = dlsym(handle, plugin_register_function);
76         if (!pluginRegisterFct) {
77                 fprintf(stderr, "[%s] not an AFB plugin, continuing...\n", path);
78                 goto error2;
79         }
80         if (verbose)
81                 fprintf(stderr, "[%s] is a valid AFB plugin\n", path);
82
83         /* allocates enough memory */
84         plugins = realloc(session->plugins, ((unsigned)session->config->pluginCount + 2) * sizeof(AFB_plugin*));
85         if (plugins == NULL) {
86                 fprintf(stderr, "ERROR: plugin [%s] memory missing. continuing...\n", path);
87                 goto error2;
88         }
89         session->plugins = plugins;
90
91         /* init the plugin */
92         desc = pluginRegisterFct();
93         if (desc == NULL) {
94                 fprintf(stderr, "ERROR: plugin [%s] register function failed. continuing...\n", path);
95                 goto error2;
96         }
97
98         /* check the returned structure */
99         if (desc->type != AFB_PLUGIN_JSON) {
100                 fprintf(stderr, "ERROR: plugin [%s] invalid type %d...\n", path, desc->type);
101                 goto error2;
102         }
103         if (desc->prefix == NULL || *desc->prefix == 0) {
104                 fprintf(stderr, "ERROR: plugin [%s] bad prefix...\n", path);
105                 goto error2;
106         }
107         if (desc->info == NULL || *desc->info == 0) {
108                 fprintf(stderr, "ERROR: plugin [%s] bad description...\n", path);
109                 goto error2;
110         }
111         if (desc->apis == NULL) {
112                 fprintf(stderr, "ERROR: plugin [%s] no APIs...\n", path);
113                 goto error2;
114         }
115
116         /* check previously existing plugin */
117         len = strlen(desc->prefix);
118         check = afb_plugins_search(session, desc->prefix, len);
119         if (check != NULL) {
120                 fprintf(stderr, "ERROR: plugin [%s] prefix %s duplicated...\n", path, desc->prefix);
121                 goto error2;
122         }
123
124         /* Prebuild plugin jtype to boost API response */
125         desc->jtype = json_object_new_string(desc->prefix);
126         desc->prefixlen = len;
127
128         /* record the plugin */
129         session->plugins[session->config->pluginCount] = desc;
130         session->plugins[++session->config->pluginCount] = NULL;
131
132         if (verbose)
133                 fprintf(stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", session->config->pluginCount, desc->prefix, desc->info);
134
135         return 0;
136
137 error2:
138         dlclose(handle);
139 error:
140         return -1;
141 }
142
143 static int adddirs(AFB_session * session, char path[PATH_MAX], size_t end)
144 {
145         int rc;
146         DIR *dir;
147         struct dirent ent, *result;
148         size_t len;
149
150         /* open the DIR now */
151         dir = opendir(path);
152         if (dir == NULL) {
153                 fprintf(stderr, "ERROR in scanning plugin directory %s, %m\n", path);
154                 return -1;
155         }
156         if (verbose)
157                 fprintf(stderr, "Scanning dir=[%s] for plugins\n", path);
158
159         /* scan each entry */
160         if (end)
161                 path[end++] = '/';
162         for (;;) {
163                 readdir_r(dir, &ent, &result);
164                 if (result == NULL)
165                         break;
166
167                 len = strlen(ent.d_name);
168                 if (len + end >= PATH_MAX) {
169                         fprintf(stderr, "path too long for %s\n", ent.d_name);
170                         continue;
171                 }
172                 memcpy(&path[end], ent.d_name, len+1);
173                 if (ent.d_type == DT_DIR) {
174                         /* case of directories */
175                         if (ent.d_name[0] == '.') {
176                                 if (len == 1)
177                                         continue;
178                                 if (ent.d_name[1] == '.' && len == 2)
179                                         continue;
180                         }
181                         rc = adddirs(session, path, end+len);;
182                 } else if (ent.d_type == DT_REG) {
183                         /* case of files */
184                         if (!strstr(ent.d_name, ".so"))
185                                 continue;
186                         rc = afb_plugins_add_plugin(session, path);
187                 }
188         }
189         closedir(dir);
190         return 0;
191 }
192
193 int afb_plugins_add_directory(AFB_session * session, const char *path)
194 {
195         size_t length;
196         char buffer[PATH_MAX];
197
198         length = strlen(path);
199         if (length >= sizeof(buffer)) {
200                 fprintf(stderr, "path too long %lu [%.99s...]\n", (unsigned long)length, path);
201                 return -1;
202         }
203
204         memcpy(buffer, path, length + 1);
205         return adddirs(session, buffer, length);
206 }
207
208 int afb_plugins_add_path(AFB_session * session, const char *path)
209 {
210         struct stat st;
211         int rc;
212
213         rc = stat(path, &st);
214         if (rc < 0)
215                 fprintf(stderr, "Invalid plugin path [%s]: %m\n", path);
216         else if (S_ISDIR(st.st_mode))
217                 rc = afb_plugins_add_directory(session, path);
218         else
219                 rc = afb_plugins_add_plugin(session, path);
220         return rc;
221 }
222
223 int afb_plugins_add_pathset(AFB_session * session, const char *pathset)
224 {
225         static char sep[] = ":";
226         char *ps, *p;
227         int rc;
228
229         ps = strdupa(pathset);
230         for (;;) {
231                 p = strsep(&ps, sep);
232                 if (!p)
233                         return 0;
234                 rc = afb_plugins_add_path(session, p);
235         };
236 }
237
238 void initPlugins(AFB_session * session)
239 {
240         int rc = afb_plugins_add_pathset(session, session->config->ldpaths);
241 }
242