Fedora 30 packaging fix issu
[src/app-framework-binder.git] / src / afb-api-so.c
1 /*
2  * Copyright (C) 2016, 2017, 2018 "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-v2.h"
29 #include "afb-api-so-v3.h"
30 #include "verbose.h"
31 #include "sig-monitor.h"
32
33 #if defined(WITH_LEGACY_BINDING_V1)
34 #   include "afb-api-so-v1.h"
35 #endif
36 #if defined(WITH_LEGACY_BINDING_VDYN)
37 #   include "afb-api-so-vdyn.h"
38 #endif
39
40 struct safe_dlopen
41 {
42         const char *path;
43         void *handle;
44         int flags;
45 };
46
47 static void safe_dlopen_cb(int sig, void *closure)
48 {
49         struct safe_dlopen *sd = closure;
50         if (!sig)
51                 sd->handle = dlopen(sd->path, sd->flags);
52         else {
53                 ERROR("dlopen of %s raised signal %s", sd->path, strsignal(sig));
54                 sd->handle = NULL;
55         }
56 }
57
58 static void *safe_dlopen(const char *filename, int flags)
59 {
60         struct safe_dlopen sd;
61         sd.path = filename;
62         sd.flags = flags;
63         sd.handle = NULL;
64         sig_monitor(0, safe_dlopen_cb, &sd);
65         return sd.handle;
66 }
67
68 static int load_binding(const char *path, int force, struct afb_apiset *declare_set, struct afb_apiset * call_set)
69 {
70         int obsolete = 0;
71         int rc;
72         void *handle;
73
74         // This is a loadable library let's check if it's a binding
75         rc = -!!force;
76         handle = safe_dlopen(path, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
77         if (handle == NULL) {
78                 if (force)
79                         ERROR("binding [%s] not loadable: %s", path, dlerror());
80                 else
81                         WARNING("binding [%s] not loadable: %s", path, dlerror());
82                 goto error;
83         }
84
85         /* try the version 3 */
86         rc = afb_api_so_v3_add(path, handle, declare_set, call_set);
87         if (rc < 0) {
88                 /* error when loading a valid v3 binding */
89                 goto error2;
90         }
91         if (rc)
92                 return 0; /* yes version 3 */
93
94         /* try the version 2 */
95         rc = afb_api_so_v2_add(path, handle, declare_set, call_set);
96         if (rc < 0) {
97                 /* error when loading a valid v2 binding */
98                 goto error2;
99         }
100         if (rc)
101                 return 0; /* yes version 2 */
102
103 #if defined(WITH_LEGACY_BINDING_VDYN)
104         /* try the version dyn */
105         rc = afb_api_so_vdyn_add(path, handle, declare_set, call_set);
106         if (rc < 0) {
107                 /* error when loading a valid dyn binding */
108                 goto error2;
109         }
110         if (rc)
111                 return 0; /* yes version dyn */
112 #else
113         if (dlsym(handle, "afbBindingVdyn")) {
114                 WARNING("binding [%s]: version DYN not supported", path);
115                 obsolete = 1;
116         }
117 #endif
118
119 #if defined(WITH_LEGACY_BINDING_V1)
120         /* try the version 1 */
121         rc = afb_api_so_v1_add(path, handle, declare_set, call_set);
122         if (rc < 0) {
123                 /* error when loading a valid v1 binding */
124                 goto error2;
125         }
126         if (rc)
127                 return 0; /* yes version 1 */
128 #else
129         if (dlsym(handle, "afbBindingV1Register")) {
130                 WARNING("binding [%s]: version 1 not supported", path);
131                 obsolete = 1;
132         }
133 #endif
134
135         /* not a valid binding */
136         _VERBOSE_(force ? Log_Level_Error : Log_Level_Info, "binding [%s] %s",
137                         path, obsolete ? "is obsolete" : "isn't an AFB binding");
138
139 error2:
140         dlclose(handle);
141 error:
142         return rc;
143 }
144
145
146 int afb_api_so_add_binding(const char *path, struct afb_apiset *declare_set, struct afb_apiset * call_set)
147 {
148         return load_binding(path, 1, declare_set, call_set);
149 }
150
151 static int adddirs(char path[PATH_MAX], size_t end, struct afb_apiset *declare_set, struct afb_apiset * call_set, int failstops)
152 {
153         DIR *dir;
154         struct dirent *dent;
155         struct stat st;
156         size_t len;
157         int rc = 0;
158
159         /* open the DIR now */
160         dir = opendir(path);
161         if (dir == NULL) {
162                 ERROR("can't scan binding directory %s, %m", path);
163                 return -1;
164         }
165         INFO("Scanning dir=[%s] for bindings", path);
166
167         /* scan each entry */
168         if (end)
169                 path[end++] = '/';
170         for (;;) {
171                 errno = 0;
172                 dent = readdir(dir);
173                 if (dent == NULL) {
174                         if (errno != 0)
175                                 ERROR("read error while scanning directory %.*s: %m", (int)(end - 1), path);
176                         break;
177                 }
178
179                 /* get the name and inspect dereferenced link instead of the directory entry */
180                 len = strlen(dent->d_name);
181                 if (len + end >= PATH_MAX) {
182                         ERROR("path too long while scanning bindings for %.*s%s", (int)end, path, dent->d_name);
183                         continue;
184                 }
185                 memcpy(&path[end], dent->d_name, len+1);
186                 rc = stat(path, &st);
187                 if (rc < 0) {
188                         ERROR("getting status of %s failed: %m", path);
189                         continue;
190                 }
191                 else if (S_ISDIR(st.st_mode)) {
192                         /* case of directories */
193                         if (dent->d_name[0] == '.') {
194 /*
195 Exclude from the search of bindings any
196 directory starting with a dot (.) by default.
197
198 It is possible to reactivate the prvious behaviour
199 by defining the following preprocessor variables
200
201  - AFB_API_SO_ACCEPT_DOT_PREFIXED_DIRS
202
203    When this variable is defined, the directories
204    starting with a dot are searched except
205    if their name is "." or ".." or ".debug"
206
207  - AFB_API_SO_ACCEPT_DOT_DEBUG_DIRS
208
209    When this variable is defined and the variable
210    AFB_API_SO_ACCEPT_DOT_PREFIXED_DIRS is also defined
211    scans any directory not being "." or "..".
212
213 The previous behaviour was like difining the 2 variables,
214 meaning that only . and .. were excluded from the search.
215
216 This change is intended to definitely solve the issue
217 SPEC-662. Yocto installed the debugging symbols in the
218 subdirectory .debug. For example the binding.so also
219 had a .debug/binding.so file attached. Opening that
220 debug file made dlopen crashing.
221 See https://sourceware.org/bugzilla/show_bug.cgi?id=22101
222  */
223 #if !defined(AFB_API_SO_ACCEPT_DOT_PREFIXED_DIRS) /* not defined by default */
224                                 continue; /* ignore any directory beginning with a dot */
225 #else
226                                 if (len == 1)
227                                         continue; /* . */
228                                 if (dent->d_name[1] == '.' && len == 2)
229                                         continue; /* .. */
230 #if !defined(AFB_API_SO_ACCEPT_DOT_DEBUG_DIRS) /* not defined by default */
231                                 if (len == 6
232                                  && dent->d_name[1] == 'd'
233                                  && dent->d_name[2] == 'e'
234                                  && dent->d_name[3] == 'b'
235                                  && dent->d_name[4] == 'u'
236                                  && dent->d_name[5] == 'g')
237                                         continue; /* .debug */
238 #endif
239 #endif
240                         }
241                         rc = adddirs(path, end+len, declare_set, call_set, failstops);
242                 } else if (S_ISREG(st.st_mode)) {
243                         /* case of files */
244                         if (memcmp(&dent->d_name[len - 3], ".so", 4))
245                                 continue;
246                         rc = load_binding(path, 0, declare_set, call_set);
247                 }
248                 if (rc < 0 && failstops) {
249                         closedir(dir);
250                         return rc;
251                 }
252         }
253         closedir(dir);
254         return 0;
255 }
256
257 int afb_api_so_add_directory(const char *path, struct afb_apiset *declare_set, struct afb_apiset * call_set, int failstops)
258 {
259         size_t length;
260         char buffer[PATH_MAX];
261
262         length = strlen(path);
263         if (length >= sizeof(buffer)) {
264                 ERROR("path too long %lu [%.99s...]", (unsigned long)length, path);
265                 return -1;
266         }
267
268         memcpy(buffer, path, length + 1);
269         return adddirs(buffer, length, declare_set, call_set, failstops);
270 }
271
272 int afb_api_so_add_path(const char *path, struct afb_apiset *declare_set, struct afb_apiset * call_set, int failstops)
273 {
274         struct stat st;
275         int rc;
276
277         rc = stat(path, &st);
278         if (rc < 0)
279                 ERROR("Invalid binding path [%s]: %m", path);
280         else if (S_ISDIR(st.st_mode))
281                 rc = afb_api_so_add_directory(path, declare_set, call_set, failstops);
282         else if (strstr(path, ".so"))
283                 rc = load_binding(path, 0, declare_set, call_set);
284         else
285                 INFO("not a binding [%s], skipped", path);
286         return rc;
287 }
288
289 int afb_api_so_add_pathset(const char *pathset, struct afb_apiset *declare_set, struct afb_apiset * call_set, int failstops)
290 {
291         static char sep[] = ":";
292         char *ps, *p;
293         int rc;
294
295         ps = strdupa(pathset);
296         for (;;) {
297                 p = strsep(&ps, sep);
298                 if (!p)
299                         return 0;
300                 rc = afb_api_so_add_path(p, declare_set, call_set, failstops);
301                 if (rc < 0)
302                         return rc;
303         }
304 }
305
306 int afb_api_so_add_pathset_fails(const char *pathset, struct afb_apiset *declare_set, struct afb_apiset * call_set)
307 {
308         return afb_api_so_add_pathset(pathset, declare_set, call_set, 1);
309 }
310
311 int afb_api_so_add_pathset_nofails(const char *pathset, struct afb_apiset *declare_set, struct afb_apiset * call_set)
312 {
313         return afb_api_so_add_pathset(pathset, declare_set, call_set, 0);
314 }
315