Coverage Report

Created: 2026-02-21 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/module.c
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