Improve isolation of bindings
[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 "verbose.h"
31 #include "sig-monitor.h"
32
33 struct safe_dlopen
34 {
35         const char *path;
36         void *handle;
37         int flags;
38 };
39
40 static void safe_dlopen_cb(int sig, void *closure)
41 {
42         struct safe_dlopen *sd = closure;
43         if (!sig)
44                 sd->handle = dlopen(sd->path, sd->flags);
45         else {
46                 ERROR("dlopen of %s raised signal %s", sd->path, strsignal(sig));
47                 sd->handle = NULL;
48         }
49 }
50
51 static void *safe_dlopen(const char *filename, int flags)
52 {
53         struct safe_dlopen sd;
54         sd.path = filename;
55         sd.flags = flags;
56         sd.handle = NULL;
57         sig_monitor(0, safe_dlopen_cb, &sd);
58         return sd.handle;
59 }
60
61 static int load_binding(const char *path, int force, struct afb_apiset *apiset)
62 {
63         int rc;
64         void *handle;
65
66         // This is a loadable library let's check if it's a binding
67         rc = -!!force;
68         handle = safe_dlopen(path, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
69         if (handle == NULL) {
70                 if (force)
71                         ERROR("binding [%s] not loadable: %s", path, dlerror());
72                 else
73                         INFO("binding [%s] not loadable: %s", path, dlerror());
74                 goto error;
75         }
76
77         /* try the version 2 */
78         rc = afb_api_so_v2_add(path, handle, apiset);
79         if (rc < 0) {
80                 /* error when loading a valid v2 binding */
81                 goto error2;
82         }
83         if (rc)
84                 return 0; /* yes version 2 */
85
86         /* try the version 1 */
87         rc = afb_api_so_v1_add(path, handle, apiset);
88         if (rc < 0) {
89                 /* error when loading a valid v1 binding */
90                 goto error2;
91         }
92         if (rc)
93                 return 0; /* yes version 1 */
94
95         /* not a valid binding */
96         if (force)
97                 ERROR("binding [%s] is not an AFB binding", path);
98         else
99                 INFO("binding [%s] is not an AFB binding", path);
100
101 error2:
102         dlclose(handle);
103 error:
104         return rc;
105 }
106
107
108 int afb_api_so_add_binding(const char *path, struct afb_apiset *apiset)
109 {
110         return load_binding(path, 1, apiset);
111 }
112
113 static int adddirs(char path[PATH_MAX], size_t end, struct afb_apiset *apiset, int failstops)
114 {
115         DIR *dir;
116         struct dirent *dent;
117         size_t len;
118         int rc = 0;
119
120         /* open the DIR now */
121         dir = opendir(path);
122         if (dir == NULL) {
123                 ERROR("can't scan binding directory %s, %m", path);
124                 return -1;
125         }
126         INFO("Scanning dir=[%s] for bindings", path);
127
128         /* scan each entry */
129         if (end)
130                 path[end++] = '/';
131         for (;;) {
132                 errno = 0;
133                 dent = readdir(dir);
134                 if (dent == NULL) {
135                         if (errno != 0)
136                                 ERROR("read error while scanning directory %.*s: %m", (int)(end - 1), path);
137                         break;
138                 }
139
140                 len = strlen(dent->d_name);
141                 if (len + end >= PATH_MAX) {
142                         ERROR("path too long while scanning bindings for %s", dent->d_name);
143                         continue;
144                 }
145                 if (dent->d_type == DT_DIR) {
146                         /* case of directories */
147                         if (dent->d_name[0] == '.') {
148                                 if (len == 1)
149                                         continue; /* . */
150                                 if (dent->d_name[1] == '.' && len == 2)
151                                         continue; /* .. */
152                         }
153                         memcpy(&path[end], dent->d_name, len+1);
154                         rc = adddirs(path, end+len, apiset, failstops);
155                 } else if (dent->d_type == DT_REG) {
156                         /* case of files */
157                         if (memcmp(&dent->d_name[len - 3], ".so", 4))
158                                 continue;
159                         memcpy(&path[end], dent->d_name, len+1);
160                         rc = load_binding(path, 0, apiset);
161                 }
162                 if (rc < 0 && failstops) {
163                         closedir(dir);
164                         return rc;
165                 }
166         }
167         closedir(dir);
168         return 0;
169 }
170
171 int afb_api_so_add_directory(const char *path, struct afb_apiset *apiset, int failstops)
172 {
173         size_t length;
174         char buffer[PATH_MAX];
175
176         length = strlen(path);
177         if (length >= sizeof(buffer)) {
178                 ERROR("path too long %lu [%.99s...]", (unsigned long)length, path);
179                 return -1;
180         }
181
182         memcpy(buffer, path, length + 1);
183         return adddirs(buffer, length, apiset, failstops);
184 }
185
186 int afb_api_so_add_path(const char *path, struct afb_apiset *apiset, int failstops)
187 {
188         struct stat st;
189         int rc;
190
191         rc = stat(path, &st);
192         if (rc < 0)
193                 ERROR("Invalid binding path [%s]: %m", path);
194         else if (S_ISDIR(st.st_mode))
195                 rc = afb_api_so_add_directory(path, apiset, failstops);
196         else if (strstr(path, ".so"))
197                 rc = load_binding(path, 0, apiset);
198         else
199                 INFO("not a binding [%s], skipped", path);
200         return rc;
201 }
202
203 int afb_api_so_add_pathset(const char *pathset, struct afb_apiset *apiset, int failstops)
204 {
205         static char sep[] = ":";
206         char *ps, *p;
207         int rc;
208
209         ps = strdupa(pathset);
210         for (;;) {
211                 p = strsep(&ps, sep);
212                 if (!p)
213                         return 0;
214                 rc = afb_api_so_add_path(p, apiset, failstops);
215                 if (rc < 0)
216                         return rc;
217         }
218 }
219
220 int afb_api_so_add_pathset_fails(const char *pathset, struct afb_apiset *apiset)
221 {
222         return afb_api_so_add_pathset(pathset, apiset, 1);
223 }
224
225 int afb_api_so_add_pathset_nofails(const char *pathset, struct afb_apiset *apiset)
226 {
227         return afb_api_so_add_pathset(pathset, apiset, 0);
228 }
229