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