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