Line data Source code
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 12 : DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name");
21 12 : DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments");
22 12 : DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error");
23 :
24 : static struct frrmod_info frrmod_default_info = {
25 : .name = "libfrr",
26 : .version = FRR_VERSION,
27 : .description = "libfrr core module",
28 : };
29 : union _frrmod_runtime_u frrmod_default = {
30 : .r =
31 : {
32 : .info = &frrmod_default_info,
33 : .finished_loading = 1,
34 : },
35 : };
36 :
37 4 : 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 4 : void frrmod_init(struct frrmod_runtime *modinfo)
53 : {
54 4 : modinfo->finished_loading = true;
55 4 : *frrmod_last = modinfo;
56 4 : frrmod_last = &modinfo->next;
57 :
58 4 : execname = modinfo->info->name;
59 4 : }
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 0 : struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
75 : void (*pFerrlog)(const void *, const char *),
76 : const void *pErrlogCookie)
77 : {
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 : #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 : #define ERR_RECORD(...) \
90 : do { \
91 : if (pFerrlog && (iErr < FRRMOD_LOAD_N_ERRSTR)) { \
92 : aErr[iErr++] = asprintfrr(MTYPE_MODULE_LOAD_ERR, \
93 : __VA_ARGS__); \
94 : } \
95 : } while (0)
96 :
97 : #define ERR_REPORT \
98 : do { \
99 : if (pFerrlog) { \
100 : unsigned int i; \
101 : \
102 : for (i = 0; i < iErr; ++i) { \
103 : (*pFerrlog)(pErrlogCookie, aErr[i]); \
104 : } \
105 : } \
106 : } while (0)
107 :
108 : #define ERR_FREE \
109 : do { \
110 : unsigned int i; \
111 : \
112 : for (i = 0; i < iErr; ++i) { \
113 : XFREE(MTYPE_MODULE_LOAD_ERR, aErr[i]); \
114 : aErr[i] = 0; \
115 : } \
116 : iErr = 0; \
117 : } 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 : 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 : fullpath, dlerror());
132 : }
133 : if (!handle) {
134 0 : snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir,
135 : name);
136 0 : handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
137 0 : if (!handle)
138 0 : ERR_RECORD("loader error: dlopen(%s): %s",
139 : fullpath, dlerror());
140 : }
141 : }
142 : 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 : dlerror());
148 : }
149 0 : if (!handle) {
150 0 : ERR_REPORT;
151 0 : ERR_FREE;
152 : return NULL;
153 : }
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 : return NULL;
165 : }
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 : }
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 : }
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 : }
199 :
200 : #if 0
201 : void frrmod_unload(struct frrmod_runtime *module)
202 : {
203 : }
204 : #endif
|