BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file load_module.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Synopsis:
32 * load_module(string name)
33 */
34  
35 #include <limits.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <dlfcn.h>
40  
41 #include <misc/balloc.h>
42 #include <misc/concat_strings.h>
43 #include <misc/strdup.h>
44 #include <misc/offset.h>
45 #include <misc/debug.h>
46 #include <structure/LinkedList0.h>
47  
48 #include <ncd/module_common.h>
49  
50 #include <generated/blog_channel_ncd_load_module.h>
51  
52 struct global {
53 LinkedList0 modules_list;
54 };
55  
56 struct module {
57 char *name;
58 void *lib_handle;
59 int ncdmodule_loaded;
60 LinkedList0Node modules_list_node;
61 };
62  
63 static struct module * find_module (const char *name, struct global *g)
64 {
65 for (LinkedList0Node *ln = LinkedList0_GetFirst(&g->modules_list); ln; ln = LinkedList0Node_Next(ln)) {
66 struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node);
67 if (!strcmp(mod->name, name)) {
68 return mod;
69 }
70 }
71 return NULL;
72 }
73  
74 static struct module * module_init (const char *name, NCDModuleInst *i)
75 {
76 struct global *g = ModuleGlobal(i);
77 ASSERT(!find_module(name, g))
78  
79 struct module *mod = BAlloc(sizeof(*mod));
80 if (!mod) {
81 ModuleLog(i, BLOG_ERROR, "BAlloc failed");
82 goto fail0;
83 }
84  
85 mod->name = b_strdup(name);
86 if (!mod->name) {
87 ModuleLog(i, BLOG_ERROR, "b_strdup failed");
88 goto fail1;
89 }
90  
91 mod->lib_handle = NULL;
92 mod->ncdmodule_loaded = 0;
93 LinkedList0_Prepend(&g->modules_list, &mod->modules_list_node);
94  
95 return mod;
96  
97 fail1:
98 BFree(mod);
99 fail0:
100 return NULL;
101 }
102  
103 static void module_free (struct module *mod, struct global *g)
104 {
105 LinkedList0_Remove(&g->modules_list, &mod->modules_list_node);
106 if (mod->lib_handle) {
107 if (dlclose(mod->lib_handle) != 0) {
108 BLog(BLOG_ERROR, "dlclose failed");
109 }
110 }
111 BFree(mod->name);
112 BFree(mod);
113 }
114  
115 static char * x_read_link (const char *path)
116 {
117 size_t size = 32;
118 char *buf = BAlloc(size + 1);
119 if (!buf) {
120 goto fail0;
121 }
122  
123 ssize_t link_size;
124 while (1) {
125 link_size = readlink(path, buf, size);
126 if (link_size < 0) {
127 goto fail1;
128 }
129 if (link_size >= 0 && link_size < size) {
130 break;
131 }
132 if (size > SIZE_MAX / 2 || 2 * size > SIZE_MAX - 1) {
133 goto fail1;
134 }
135 size *= 2;
136 char *new_buf = BRealloc(buf, size + 1);
137 if (!new_buf) {
138 goto fail1;
139 }
140 buf = new_buf;
141 }
142  
143 buf[link_size] = '\0';
144 return buf;
145  
146 fail1:
147 BFree(buf);
148 fail0:
149 return NULL;
150 }
151  
152 static char * find_module_library (NCDModuleInst *i, const char *module_name)
153 {
154 char *ret = NULL;
155  
156 char *self = x_read_link("/proc/self/exe");
157 if (!self) {
158 ModuleLog(i, BLOG_ERROR, "failed to read /proc/self/exe");
159 goto fail0;
160 }
161  
162 char *slash = strrchr(self, '/');
163 if (!slash) {
164 ModuleLog(i, BLOG_ERROR, "contents of /proc/self/exe do not have a slash");
165 goto fail1;
166 }
167 *slash = '\0';
168  
169 const char *paths[] = {"../lib/badvpn-ncd", "../mcvpn", NULL};
170  
171 size_t j;
172 for (j = 0; paths[j]; j++) {
173 char *module_path = concat_strings(6, self, "/", paths[j], "/libncdmodule_", module_name, ".so");
174 if (!module_path) {
175 ModuleLog(i, BLOG_ERROR, "concat_strings failed");
176 goto fail1;
177 }
178  
179 if (access(module_path, F_OK) == 0) {
180 ret = module_path;
181 break;
182 }
183  
184 BFree(module_path);
185 }
186  
187 if (!paths[j]) {
188 ModuleLog(i, BLOG_ERROR, "failed to find module");
189 }
190  
191 fail1:
192 BFree(self);
193 fail0:
194 return ret;
195 }
196  
197 static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params)
198 {
199 struct global *g = BAlloc(sizeof(*g));
200 if (!g) {
201 BLog(BLOG_ERROR, "BAlloc failed");
202 return 0;
203 }
204  
205 group->group_state = g;
206 LinkedList0_Init(&g->modules_list);
207  
208 return 1;
209 }
210  
211 static void func_globalfree (struct NCDInterpModuleGroup *group)
212 {
213 struct global *g = group->group_state;
214  
215 LinkedList0Node *ln;
216 while ((ln = LinkedList0_GetFirst(&g->modules_list))) {
217 struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node);
218 module_free(mod, g);
219 }
220  
221 BFree(g);
222 }
223  
224 static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
225 {
226 // check arguments
227 NCDValRef name_arg;
228 if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
229 ModuleLog(i, BLOG_ERROR, "wrong arity");
230 goto fail0;
231 }
232 if (!NCDVal_IsStringNoNulls(name_arg)) {
233 ModuleLog(i, BLOG_ERROR, "wrong type");
234 goto fail0;
235 }
236  
237 NCDValNullTermString name_nts;
238 if (!NCDVal_StringNullTerminate(name_arg, &name_nts)) {
239 ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed");
240 goto fail0;
241 }
242  
243 struct module *mod = find_module(name_nts.data, ModuleGlobal(i));
244 ASSERT(!mod || mod->lib_handle)
245  
246 if (!mod) {
247 mod = module_init(name_nts.data, i);
248 if (!mod) {
249 ModuleLog(i, BLOG_ERROR, "module_init failed");
250 goto fail1;
251 }
252  
253 // find module library
254 char *module_path = find_module_library(i, name_nts.data);
255 if (!module_path) {
256 module_free(mod, ModuleGlobal(i));
257 goto fail1;
258 }
259  
260 // load it as a dynamic library
261 mod->lib_handle = dlopen(module_path, RTLD_NOW);
262 BFree(module_path);
263 if (!mod->lib_handle) {
264 ModuleLog(i, BLOG_ERROR, "dlopen failed");
265 module_free(mod, ModuleGlobal(i));
266 goto fail1;
267 }
268 }
269  
270 if (!mod->ncdmodule_loaded) {
271 // build name of NCDModuleGroup structure symbol
272 char *group_symbol = concat_strings(2, "ncdmodule_", name_nts.data);
273 if (!group_symbol) {
274 ModuleLog(i, BLOG_ERROR, "concat_strings failed");
275 goto fail1;
276 }
277  
278 // resolve NCDModuleGroup structure symbol
279 void *group = dlsym(mod->lib_handle, group_symbol);
280 BFree(group_symbol);
281 if (!group) {
282 ModuleLog(i, BLOG_ERROR, "dlsym failed");
283 goto fail1;
284 }
285  
286 // load module group
287 if (!NCDModuleInst_Backend_InterpLoadGroup(i, (struct NCDModuleGroup *)group)) {
288 ModuleLog(i, BLOG_ERROR, "NCDModuleInst_Backend_InterpLoadGroup failed");
289 goto fail1;
290 }
291  
292 mod->ncdmodule_loaded = 1;
293 }
294  
295 NCDValNullTermString_Free(&name_nts);
296  
297 // signal up
298 NCDModuleInst_Backend_Up(i);
299 return;
300  
301 fail1:
302 NCDValNullTermString_Free(&name_nts);
303 fail0:
304 NCDModuleInst_Backend_DeadError(i);
305 }
306  
307 static struct NCDModule modules[] = {
308 {
309 .type = "load_module",
310 .func_new2 = func_new
311 }, {
312 .type = NULL
313 }
314 };
315  
316 const struct NCDModuleGroup ncdmodule_load_module = {
317 .func_globalinit = func_globalinit,
318 .func_globalfree = func_globalfree,
319 .modules = modules
320 };