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