]> git.proxmox.com Git - mirror_frr.git/blame - lib/module.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / module.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: ISC
30771d65
DL
2/*
3 * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc.
30771d65
DL
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"
09781197 17#include "lib/version.h"
52fad8f6 18#include "printfrr.h"
30771d65 19
bf8d3d6a
DL
20DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name");
21DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments");
52fad8f6 22DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error");
30771d65
DL
23
24static struct frrmod_info frrmod_default_info = {
25 .name = "libfrr",
26 .version = FRR_VERSION,
27 .description = "libfrr core module",
28};
29union _frrmod_runtime_u frrmod_default = {
996c9314
LB
30 .r =
31 {
32 .info = &frrmod_default_info,
33 .finished_loading = 1,
34 },
30771d65
DL
35};
36
80413c20 37XREF_SETUP();
8e427c29 38
30771d65
DL
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
48struct frrmod_runtime *frrmod_list = &frrmod_default.r;
49static struct frrmod_runtime **frrmod_last = &frrmod_default.r.next;
50static const char *execname = NULL;
51
52void frrmod_init(struct frrmod_runtime *modinfo)
53{
08c2d52a 54 modinfo->finished_loading = true;
30771d65
DL
55 *frrmod_last = modinfo;
56 frrmod_last = &modinfo->next;
57
58 execname = modinfo->info->name;
59}
60
52fad8f6
PZ
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 */
74struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
75 void (*pFerrlog)(const void *, const char *),
76 const void *pErrlogCookie)
30771d65
DL
77{
78 void *handle = NULL;
ff44f570 79 char name[PATH_MAX], fullpath[PATH_MAX * 2], *args;
30771d65
DL
80 struct frrmod_runtime *rtinfo, **rtinfop;
81 const struct frrmod_info *info;
82
52fad8f6
PZ
83#define FRRMOD_LOAD_N_ERRSTR 10
84 char *aErr[FRRMOD_LOAD_N_ERRSTR];
85 unsigned int iErr = 0;
86
87 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
30771d65
DL
119 snprintf(name, sizeof(name), "%s", spec);
120 args = strchr(name, ':');
121 if (args)
122 *args++ = '\0';
123
124 if (!strchr(name, '/')) {
2e1cc436 125 if (execname) {
d62a17ae 126 snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so", dir,
127 execname, name);
30771d65 128 handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
52fad8f6
PZ
129 if (!handle)
130 ERR_RECORD("loader error: dlopen(%s): %s",
131 fullpath, dlerror());
30771d65
DL
132 }
133 if (!handle) {
d62a17ae 134 snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir,
135 name);
30771d65 136 handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
52fad8f6
PZ
137 if (!handle)
138 ERR_RECORD("loader error: dlopen(%s): %s",
139 fullpath, dlerror());
30771d65
DL
140 }
141 }
142 if (!handle) {
143 snprintf(fullpath, sizeof(fullpath), "%s", name);
144 handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
52fad8f6
PZ
145 if (!handle)
146 ERR_RECORD("loader error: dlopen(%s): %s", fullpath,
147 dlerror());
30771d65
DL
148 }
149 if (!handle) {
52fad8f6
PZ
150 ERR_REPORT;
151 ERR_FREE;
30771d65
DL
152 return NULL;
153 }
154
52fad8f6
PZ
155 /* previous dlopen() errors are no longer relevant */
156 ERR_FREE;
157
30771d65
DL
158 rtinfop = dlsym(handle, "frr_module");
159 if (!rtinfop) {
160 dlclose(handle);
52fad8f6
PZ
161 ERR_RECORD("\"%s\" is not an FRR module: %s", name, dlerror());
162 ERR_REPORT;
163 ERR_FREE;
30771d65
DL
164 return NULL;
165 }
166 rtinfo = *rtinfop;
167 rtinfo->load_name = XSTRDUP(MTYPE_MODULE_LOADNAME, name);
168 rtinfo->dl_handle = handle;
169 if (args)
170 rtinfo->load_args = XSTRDUP(MTYPE_MODULE_LOADARGS, args);
171 info = rtinfo->info;
172
173 if (rtinfo->finished_loading) {
174 dlclose(handle);
52fad8f6 175 ERR_RECORD("module \"%s\" already loaded", name);
30771d65
DL
176 goto out_fail;
177 }
178
179 if (info->init && info->init()) {
180 dlclose(handle);
52fad8f6 181 ERR_RECORD("module \"%s\" initialisation failed", name);
30771d65
DL
182 goto out_fail;
183 }
184
08c2d52a 185 rtinfo->finished_loading = true;
30771d65
DL
186
187 *frrmod_last = rtinfo;
188 frrmod_last = &rtinfo->next;
52fad8f6 189 ERR_FREE;
30771d65
DL
190 return rtinfo;
191
192out_fail:
0a22ddfb 193 XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args);
30771d65 194 XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name);
52fad8f6
PZ
195 ERR_REPORT;
196 ERR_FREE;
30771d65
DL
197 return NULL;
198}
199
200#if 0
201void frrmod_unload(struct frrmod_runtime *module)
202{
203}
204#endif