Line | Count | Source |
1 | | // SPDX-License-Identifier: ISC |
2 | | /* |
3 | | * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. |
4 | | */ |
5 | | |
6 | | #include "config.h" |
7 | | |
8 | | #include <stdlib.h> |
9 | | #include <stdio.h> |
10 | | #include <string.h> |
11 | | #include <unistd.h> |
12 | | #include <limits.h> |
13 | | #include <dlfcn.h> |
14 | | |
15 | | #include "module.h" |
16 | | #include "memory.h" |
17 | | #include "lib/version.h" |
18 | | #include "printfrr.h" |
19 | | |
20 | 8 | DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name"); |
21 | 8 | DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments"); |
22 | 8 | DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error"); |
23 | 8 | |
24 | 8 | static struct frrmod_info frrmod_default_info = { |
25 | 8 | .name = "libfrr", |
26 | 8 | .version = FRR_VERSION, |
27 | 8 | .description = "libfrr core module", |
28 | 8 | }; |
29 | 8 | union _frrmod_runtime_u frrmod_default = { |
30 | 8 | .r = |
31 | 8 | { |
32 | 8 | .info = &frrmod_default_info, |
33 | 8 | .finished_loading = 1, |
34 | 8 | }, |
35 | 8 | }; |
36 | 8 | |
37 | 8 | XREF_SETUP(); |
38 | | |
39 | | // if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE) |
40 | | // union _frrmod_runtime_u _frrmod_this_module |
41 | | // __attribute__((weak, alias("frrmod_default"))); |
42 | | // elif defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) |
43 | | #pragma weak _frrmod_this_module = frrmod_default |
44 | | // else |
45 | | // error need weak symbol support |
46 | | // endif |
47 | | |
48 | | struct frrmod_runtime *frrmod_list = &frrmod_default.r; |
49 | | static struct frrmod_runtime **frrmod_last = &frrmod_default.r.next; |
50 | | static const char *execname = NULL; |
51 | | |
52 | | void frrmod_init(struct frrmod_runtime *modinfo) |
53 | 0 | { |
54 | 0 | modinfo->finished_loading = true; |
55 | 0 | *frrmod_last = modinfo; |
56 | 0 | frrmod_last = &modinfo->next; |
57 | |
|
58 | 0 | execname = modinfo->info->name; |
59 | 0 | } |
60 | | |
61 | | /* |
62 | | * If caller wants error strings, it should define non-NULL pFerrlog |
63 | | * which will be called with 0-terminated error messages. These |
64 | | * messages will NOT contain newlines, and the (*pFerrlog)() function |
65 | | * could be called multiple times for a single call to frrmod_load(). |
66 | | * |
67 | | * The (*pFerrlog)() function may copy these strings if needed, but |
68 | | * should expect them to be freed by frrmod_load() before frrmod_load() |
69 | | * returns. |
70 | | * |
71 | | * frrmod_load() is coded such that (*pFerrlog)() will be called only |
72 | | * in the case where frrmod_load() returns an error. |
73 | | */ |
74 | | struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, |
75 | | void (*pFerrlog)(const void *, const char *), |
76 | | const void *pErrlogCookie) |
77 | 0 | { |
78 | 0 | void *handle = NULL; |
79 | 0 | char name[PATH_MAX], fullpath[PATH_MAX * 2], *args; |
80 | 0 | struct frrmod_runtime *rtinfo, **rtinfop; |
81 | 0 | const struct frrmod_info *info; |
82 | |
|
83 | 0 | #define FRRMOD_LOAD_N_ERRSTR 10 |
84 | 0 | char *aErr[FRRMOD_LOAD_N_ERRSTR]; |
85 | 0 | unsigned int iErr = 0; |
86 | |
|
87 | 0 | memset(aErr, 0, sizeof(aErr)); |
88 | |
|
89 | 0 | #define ERR_RECORD(...) \ |
90 | 0 | do { \ |
91 | 0 | if (pFerrlog && (iErr < FRRMOD_LOAD_N_ERRSTR)) { \ |
92 | 0 | aErr[iErr++] = asprintfrr(MTYPE_MODULE_LOAD_ERR, \ |
93 | 0 | __VA_ARGS__); \ |
94 | 0 | } \ |
95 | 0 | } while (0) |
96 | |
|
97 | 0 | #define ERR_REPORT \ |
98 | 0 | do { \ |
99 | 0 | if (pFerrlog) { \ |
100 | 0 | unsigned int i; \ |
101 | 0 | \ |
102 | 0 | for (i = 0; i < iErr; ++i) { \ |
103 | 0 | (*pFerrlog)(pErrlogCookie, aErr[i]); \ |
104 | 0 | } \ |
105 | 0 | } \ |
106 | 0 | } while (0) |
107 | |
|
108 | 0 | #define ERR_FREE \ |
109 | 0 | do { \ |
110 | 0 | unsigned int i; \ |
111 | 0 | \ |
112 | 0 | for (i = 0; i < iErr; ++i) { \ |
113 | 0 | XFREE(MTYPE_MODULE_LOAD_ERR, aErr[i]); \ |
114 | 0 | aErr[i] = 0; \ |
115 | 0 | } \ |
116 | 0 | iErr = 0; \ |
117 | 0 | } while (0) |
118 | |
|
119 | 0 | snprintf(name, sizeof(name), "%s", spec); |
120 | 0 | args = strchr(name, ':'); |
121 | 0 | if (args) |
122 | 0 | *args++ = '\0'; |
123 | |
|
124 | 0 | if (!strchr(name, '/')) { |
125 | 0 | if (execname) { |
126 | 0 | snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so", dir, |
127 | 0 | execname, name); |
128 | 0 | handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); |
129 | 0 | if (!handle) |
130 | 0 | ERR_RECORD("loader error: dlopen(%s): %s", |
131 | 0 | fullpath, dlerror()); |
132 | 0 | } |
133 | 0 | if (!handle) { |
134 | 0 | snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir, |
135 | 0 | name); |
136 | 0 | handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); |
137 | 0 | if (!handle) |
138 | 0 | ERR_RECORD("loader error: dlopen(%s): %s", |
139 | 0 | fullpath, dlerror()); |
140 | 0 | } |
141 | 0 | } |
142 | 0 | if (!handle) { |
143 | 0 | snprintf(fullpath, sizeof(fullpath), "%s", name); |
144 | 0 | handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL); |
145 | 0 | if (!handle) |
146 | 0 | ERR_RECORD("loader error: dlopen(%s): %s", fullpath, |
147 | 0 | dlerror()); |
148 | 0 | } |
149 | 0 | if (!handle) { |
150 | 0 | ERR_REPORT; |
151 | 0 | ERR_FREE; |
152 | 0 | return NULL; |
153 | 0 | } |
154 | | |
155 | | /* previous dlopen() errors are no longer relevant */ |
156 | 0 | ERR_FREE; |
157 | |
|
158 | 0 | rtinfop = dlsym(handle, "frr_module"); |
159 | 0 | if (!rtinfop) { |
160 | 0 | dlclose(handle); |
161 | 0 | ERR_RECORD("\"%s\" is not an FRR module: %s", name, dlerror()); |
162 | 0 | ERR_REPORT; |
163 | 0 | ERR_FREE; |
164 | 0 | return NULL; |
165 | 0 | } |
166 | 0 | rtinfo = *rtinfop; |
167 | 0 | rtinfo->load_name = XSTRDUP(MTYPE_MODULE_LOADNAME, name); |
168 | 0 | rtinfo->dl_handle = handle; |
169 | 0 | if (args) |
170 | 0 | rtinfo->load_args = XSTRDUP(MTYPE_MODULE_LOADARGS, args); |
171 | 0 | info = rtinfo->info; |
172 | |
|
173 | 0 | if (rtinfo->finished_loading) { |
174 | 0 | dlclose(handle); |
175 | 0 | ERR_RECORD("module \"%s\" already loaded", name); |
176 | 0 | goto out_fail; |
177 | 0 | } |
178 | | |
179 | 0 | if (info->init && info->init()) { |
180 | 0 | dlclose(handle); |
181 | 0 | ERR_RECORD("module \"%s\" initialisation failed", name); |
182 | 0 | goto out_fail; |
183 | 0 | } |
184 | | |
185 | 0 | rtinfo->finished_loading = true; |
186 | |
|
187 | 0 | *frrmod_last = rtinfo; |
188 | 0 | frrmod_last = &rtinfo->next; |
189 | 0 | ERR_FREE; |
190 | 0 | return rtinfo; |
191 | | |
192 | 0 | out_fail: |
193 | 0 | XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args); |
194 | 0 | XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name); |
195 | 0 | ERR_REPORT; |
196 | 0 | ERR_FREE; |
197 | | return NULL; |
198 | 0 | } |
199 | | |
200 | | #if 0 |
201 | | void frrmod_unload(struct frrmod_runtime *module) |
202 | | { |
203 | | } |
204 | | #endif |