afb-api-so: Exclude directories starting with .
[src/app-framework-binder.git] / src / afb-api-so.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdio.h>
21 #include <dlfcn.h>
22 #include <string.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <sys/stat.h>
26
27 #include "afb-api-so.h"
28 #include "afb-api-so-v1.h"
29 #include "afb-api-so-v2.h"
30 #include "afb-api-so-vdyn.h"
31 #include "verbose.h"
32 #include "sig-monitor.h"
33
34 struct safe_dlopen
35 {
36         const char *path;
37         void *handle;
38         int flags;
39 };
40
41 static void safe_dlopen_cb(int sig, void *closure)
42 {
43         struct safe_dlopen *sd = closure;
44         if (!sig)
45                 sd->handle = dlopen(sd->path, sd->flags);
46         else {
47                 ERROR("dlopen of %s raised signal %s", sd->path, strsignal(sig));
48                 sd->handle = NULL;
49         }
50 }
51
52 static void *safe_dlopen(const char *filename, int flags)
53 {
54         struct safe_dlopen sd;
55         sd.path = filename;
56         sd.flags = flags;
57         sd.handle = NULL;
58         sig_monitor(0, safe_dlopen_cb, &sd);
59         return sd.handle;
60 }
61
62 static int load_binding(const char *path, int force, struct afb_apiset *apiset)
63 {
64         int rc;
65         void *handle;
66
67         // This is a loadable library let's check if it's a binding
68         rc = -!!force;
69         handle = safe_dlopen(path, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
70         if (handle == NULL) {
71                 if (force)
72                         ERROR("binding [%s] not loadable: %s", path, dlerror());
73                 else
74                         WARNING("binding [%s] not loadable: %s", path, dlerror());
75                 goto error;
76         }
77
78         /* try the version 2 */
79         rc = afb_api_so_v2_add(path, handle, apiset);
80         if (rc < 0) {
81                 /* error when loading a valid v2 binding */
82                 goto error2;
83         }
84         if (rc)
85                 return 0; /* yes version 2 */
86
87         /* try the version dyn */
88         rc = afb_api_so_vdyn_add(path, handle, apiset);
89         if (rc < 0) {
90                 /* error when loading a valid dyn binding */
91                 goto error2;
92         }
93         if (rc)
94                 return 0; /* yes version dyn */
95
96         /* try the version 1 */
97         rc = afb_api_so_v1_add(path, handle, apiset);
98         if (rc < 0) {
99                 /* error when loading a valid v1 binding */
100                 goto error2;
101         }
102         if (rc)
103                 return 0; /* yes version 1 */
104
105         /* not a valid binding */
106         if (force)
107                 ERROR("binding [%s] is not an AFB binding", path);
108         else
109                 INFO("binding [%s] is not an AFB binding", path);
110
111 error2:
112         dlclose(handle);
113 error:
114         return rc;
115 }
116
117
118 int afb_api_so_add_binding(const char *path, struct afb_apiset *apiset)
119 {
120         return load_binding(path, 1, apiset);
121 }
122
123 static int adddirs(char path[PATH_MAX], size_t end, struct afb_apiset *apiset, int failstops)
124 {
125         DIR *dir;
126         struct dirent *dent;
127         size_t len;
128         int rc = 0;
129
130         /* open the DIR now */
131         dir = opendir(path);
132         if (dir == NULL) {
133                 ERROR("can't scan binding directory %s, %m", path);
134                 return -1;
135         }
136         INFO("Scanning dir=[%s] for bindings", path);
137
138         /* scan each entry */
139         if (end)
140                 path[end++] = '/';
141         for (;;) {
142                 errno = 0;
143                 dent = readdir(dir);
144                 if (dent == NULL) {
145                         if (errno != 0)
146                                 ERROR("read error while scanning directory %.*s: %m", (int)(end - 1), path);
147                         break;
148                 }
149
150                 len = strlen(dent->d_name);
151                 if (len + end >= PATH_MAX) {
152                         ERROR("path too long while scanning bindings for %s", dent->d_name);
153                         continue;
154                 }
155                 if (dent->d_type == DT_DIR) {
156                         /* case of directories */
157                         if (dent->d_name[0] == '.') {
158 /*
159 Exclude from the search of bindings any
160 directory starting with a dot (.) by default.
161
162 It is possible to reactivate the prvious behaviour 
163 by defining the following preprocessor variables
164
165  - AFB_API_SO_ACCEPT_DOT_PREFIXED_DIRS
166
167    When this variable is defined, the directories 
168    starting with a dot are searched except
169    if their name is "." or ".." or ".debug"
170
171  - AFB_API_SO_ACCEPT_DOT_DEBUG_DIRS
172
173    When this variable is defined and the variable
174    AFB_API_SO_ACCEPT_DOT_PREFIXED_DIRS is also defined
175    scans any directory not being "." or "..".
176
177 The previous behaviour was like difining the 2 variables,
178 meaning that only . and .. were excluded from the search.
179
180 This change is intended to definitely solve the issue
181 SPEC-662. Yocto installed the debugging symbols in the
182 subdirectory .debug. For example the binding.so also
183 had a .debug/binding.so file attached. Opening that
184 debug file made dlopen crashing. 
185 See https://sourceware.org/bugzilla/show_bug.cgi?id=22101
186  */
187 #if !defined(AFB_API_SO_ACCEPT_DOT_PREFIXED_DIRS) /* not defined by default */
188                                 continue; /* ignore any directory beginnign with a dot */
189 #else
190                                 if (len == 1)
191                                         continue; /* . */
192                                 if (dent->d_name[1] == '.' && len == 2)
193                                         continue; /* .. */
194 #if !defined(AFB_API_SO_ACCEPT_DOT_DEBUG_DIRS) /* not defined by default */
195                                 if (len == 6
196                                  && dent->d_name[1] == 'd'
197                                  && dent->d_name[2] == 'e'
198                                  && dent->d_name[3] == 'b'
199                                  && dent->d_name[4] == 'u'
200                                  && dent->d_name[5] == 'g')
201                                         continue; /* .debug */
202 #endif
203 #endif
204                         }
205                         memcpy(&path[end], dent->d_name, len+1);
206                         rc = adddirs(path, end+len, apiset, failstops);
207                 } else if (dent->d_type == DT_REG) {
208                         /* case of files */
209                         if (memcmp(&dent->d_name[len - 3], ".so", 4))
210                                 continue;
211                         memcpy(&path[end], dent->d_name, len+1);
212                         rc = load_binding(path, 0, apiset);
213                 }
214                 if (rc < 0 && failstops) {
215                         closedir(dir);
216                         return rc;
217                 }
218         }
219         closedir(dir);
220         return 0;
221 }
222
223 int afb_api_so_add_directory(const char *path, struct afb_apiset *apiset, int failstops)
224 {
225         size_t length;
226         char buffer[PATH_MAX];
227
228         length = strlen(path);
229         if (length >= sizeof(buffer)) {
230                 ERROR("path too long %lu [%.99s...]", (unsigned long)length, path);
231                 return -1;
232         }
233
234         memcpy(buffer, path, length + 1);
235         return adddirs(buffer, length, apiset, failstops);
236 }
237
238 int afb_api_so_add_path(const char *path, struct afb_apiset *apiset, int failstops)
239 {
240         struct stat st;
241         int rc;
242
243         rc = stat(path, &st);
244         if (rc < 0)
245                 ERROR("Invalid binding path [%s]: %m", path);
246         else if (S_ISDIR(st.st_mode))
247                 rc = afb_api_so_add_directory(path, apiset, failstops);
248         else if (strstr(path, ".so"))
249                 rc = load_binding(path, 0, apiset);
250         else
251                 INFO("not a binding [%s], skipped", path);
252         return rc;
253 }
254
255 int afb_api_so_add_pathset(const char *pathset, struct afb_apiset *apiset, int failstops)
256 {
257         static char sep[] = ":";
258         char *ps, *p;
259         int rc;
260
261         ps = strdupa(pathset);
262         for (;;) {
263                 p = strsep(&ps, sep);
264                 if (!p)
265                         return 0;
266                 rc = afb_api_so_add_path(p, apiset, failstops);
267                 if (rc < 0)
268                         return rc;
269         }
270 }
271
272 int afb_api_so_add_pathset_fails(const char *pathset, struct afb_apiset *apiset)
273 {
274         return afb_api_so_add_pathset(pathset, apiset, 1);
275 }
276
277 int afb_api_so_add_pathset_nofails(const char *pathset, struct afb_apiset *apiset)
278 {
279         return afb_api_so_add_pathset(pathset, apiset, 0);
280 }
281