]>
Commit | Line | Data |
---|---|---|
30771d65 DL |
1 | /* |
2 | * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. | |
3 | * | |
bbfeedb5 DL |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
30771d65 | 7 | * |
bbfeedb5 DL |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
30771d65 DL |
15 | */ |
16 | ||
17 | #include "config.h" | |
18 | ||
19 | #include <stdlib.h> | |
20 | #include <stdio.h> | |
21 | #include <string.h> | |
22 | #include <unistd.h> | |
23 | #include <limits.h> | |
24 | #include <dlfcn.h> | |
25 | ||
26 | #include "module.h" | |
27 | #include "memory.h" | |
09781197 | 28 | #include "lib/version.h" |
52fad8f6 | 29 | #include "printfrr.h" |
30771d65 | 30 | |
bf8d3d6a DL |
31 | DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name"); |
32 | DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments"); | |
52fad8f6 | 33 | DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error"); |
30771d65 DL |
34 | |
35 | static struct frrmod_info frrmod_default_info = { | |
36 | .name = "libfrr", | |
37 | .version = FRR_VERSION, | |
38 | .description = "libfrr core module", | |
39 | }; | |
40 | union _frrmod_runtime_u frrmod_default = { | |
996c9314 LB |
41 | .r = |
42 | { | |
43 | .info = &frrmod_default_info, | |
44 | .finished_loading = 1, | |
45 | }, | |
30771d65 DL |
46 | }; |
47 | ||
80413c20 | 48 | XREF_SETUP(); |
8e427c29 | 49 | |
30771d65 DL |
50 | // if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE) |
51 | // union _frrmod_runtime_u _frrmod_this_module | |
52 | // __attribute__((weak, alias("frrmod_default"))); | |
53 | // elif defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) | |
54 | #pragma weak _frrmod_this_module = frrmod_default | |
55 | // else | |
56 | // error need weak symbol support | |
57 | // endif | |
58 | ||
59 | struct frrmod_runtime *frrmod_list = &frrmod_default.r; | |
60 | static struct frrmod_runtime **frrmod_last = &frrmod_default.r.next; | |
61 | static const char *execname = NULL; | |
62 | ||
63 | void frrmod_init(struct frrmod_runtime *modinfo) | |
64 | { | |
08c2d52a | 65 | modinfo->finished_loading = true; |
30771d65 DL |
66 | *frrmod_last = modinfo; |
67 | frrmod_last = &modinfo->next; | |
68 | ||
69 | execname = modinfo->info->name; | |
70 | } | |
71 | ||
52fad8f6 PZ |
72 | /* |
73 | * If caller wants error strings, it should define non-NULL pFerrlog | |
74 | * which will be called with 0-terminated error messages. These | |
75 | * messages will NOT contain newlines, and the (*pFerrlog)() function | |
76 | * could be called multiple times for a single call to frrmod_load(). | |
77 | * | |
78 | * The (*pFerrlog)() function may copy these strings if needed, but | |
79 | * should expect them to be freed by frrmod_load() before frrmod_load() | |
80 | * returns. | |
81 | * | |
82 | * frrmod_load() is coded such that (*pFerrlog)() will be called only | |
83 | * in the case where frrmod_load() returns an error. | |
84 | */ | |
85 | struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, | |
86 | void (*pFerrlog)(const void *, const char *), | |
87 | const void *pErrlogCookie) | |
30771d65 DL |
88 | { |
89 | void *handle = NULL; | |
ff44f570 | 90 | char name[PATH_MAX], fullpath[PATH_MAX * 2], *args; |
30771d65 DL |
91 | struct frrmod_runtime *rtinfo, **rtinfop; |
92 | const struct frrmod_info *info; | |
93 | ||
52fad8f6 PZ |
94 | #define FRRMOD_LOAD_N_ERRSTR 10 |
95 | char *aErr[FRRMOD_LOAD_N_ERRSTR]; | |
96 | unsigned int iErr = 0; | |
97 | ||
98 | memset(aErr, 0, sizeof(aErr)); | |
99 | ||
100 | #define ERR_RECORD(...) \ | |
101 | do { \ | |
102 | if (pFerrlog && (iErr < FRRMOD_LOAD_N_ERRSTR)) { \ | |
103 | aErr[iErr++] = asprintfrr(MTYPE_MODULE_LOAD_ERR, \ | |
104 | __VA_ARGS__); \ | |
105 | } \ | |
106 | } while (0) | |
107 | ||
108 | #define ERR_REPORT \ | |
109 | do { \ | |
110 | if (pFerrlog) { \ | |
111 | unsigned int i; \ | |
112 | \ | |
113 | for (i = 0; i < iErr; ++i) { \ | |
114 | (*pFerrlog)(pErrlogCookie, aErr[i]); \ | |
115 | } \ | |
116 | } \ | |
117 | } while (0) | |
118 | ||
119 | #define ERR_FREE \ | |
120 | do { \ | |
121 | unsigned int i; \ | |
122 | \ | |
123 | for (i = 0; i < iErr; ++i) { \ | |
124 | XFREE(MTYPE_MODULE_LOAD_ERR, aErr[i]); \ | |
125 | aErr[i] = 0; \ | |
126 | } \ | |
127 | iErr = 0; \ | |
128 | } while (0) | |
129 | ||
30771d65 DL |
130 | snprintf(name, sizeof(name), "%s", spec); |
131 | args = strchr(name, ':'); | |
132 | if (args) | |
133 | *args++ = '\0'; | |
134 | ||
135 | if (!strchr(name, '/')) { | |
2e1cc436 | 136 | if (execname) { |
d62a17ae | 137 | snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so", dir, |
138 | execname, name); | |
30771d65 | 139 | handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); |
52fad8f6 PZ |
140 | if (!handle) |
141 | ERR_RECORD("loader error: dlopen(%s): %s", | |
142 | fullpath, dlerror()); | |
30771d65 DL |
143 | } |
144 | if (!handle) { | |
d62a17ae | 145 | snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir, |
146 | name); | |
30771d65 | 147 | handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); |
52fad8f6 PZ |
148 | if (!handle) |
149 | ERR_RECORD("loader error: dlopen(%s): %s", | |
150 | fullpath, dlerror()); | |
30771d65 DL |
151 | } |
152 | } | |
153 | if (!handle) { | |
154 | snprintf(fullpath, sizeof(fullpath), "%s", name); | |
155 | handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); | |
52fad8f6 PZ |
156 | if (!handle) |
157 | ERR_RECORD("loader error: dlopen(%s): %s", fullpath, | |
158 | dlerror()); | |
30771d65 DL |
159 | } |
160 | if (!handle) { | |
52fad8f6 PZ |
161 | ERR_REPORT; |
162 | ERR_FREE; | |
30771d65 DL |
163 | return NULL; |
164 | } | |
165 | ||
52fad8f6 PZ |
166 | /* previous dlopen() errors are no longer relevant */ |
167 | ERR_FREE; | |
168 | ||
30771d65 DL |
169 | rtinfop = dlsym(handle, "frr_module"); |
170 | if (!rtinfop) { | |
171 | dlclose(handle); | |
52fad8f6 PZ |
172 | ERR_RECORD("\"%s\" is not an FRR module: %s", name, dlerror()); |
173 | ERR_REPORT; | |
174 | ERR_FREE; | |
30771d65 DL |
175 | return NULL; |
176 | } | |
177 | rtinfo = *rtinfop; | |
178 | rtinfo->load_name = XSTRDUP(MTYPE_MODULE_LOADNAME, name); | |
179 | rtinfo->dl_handle = handle; | |
180 | if (args) | |
181 | rtinfo->load_args = XSTRDUP(MTYPE_MODULE_LOADARGS, args); | |
182 | info = rtinfo->info; | |
183 | ||
184 | if (rtinfo->finished_loading) { | |
185 | dlclose(handle); | |
52fad8f6 | 186 | ERR_RECORD("module \"%s\" already loaded", name); |
30771d65 DL |
187 | goto out_fail; |
188 | } | |
189 | ||
190 | if (info->init && info->init()) { | |
191 | dlclose(handle); | |
52fad8f6 | 192 | ERR_RECORD("module \"%s\" initialisation failed", name); |
30771d65 DL |
193 | goto out_fail; |
194 | } | |
195 | ||
08c2d52a | 196 | rtinfo->finished_loading = true; |
30771d65 DL |
197 | |
198 | *frrmod_last = rtinfo; | |
199 | frrmod_last = &rtinfo->next; | |
52fad8f6 | 200 | ERR_FREE; |
30771d65 DL |
201 | return rtinfo; |
202 | ||
203 | out_fail: | |
0a22ddfb | 204 | XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args); |
30771d65 | 205 | XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name); |
52fad8f6 PZ |
206 | ERR_REPORT; |
207 | ERR_FREE; | |
30771d65 DL |
208 | return NULL; |
209 | } | |
210 | ||
211 | #if 0 | |
212 | void frrmod_unload(struct frrmod_runtime *module) | |
213 | { | |
214 | } | |
215 | #endif |