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