Coverage Report

Created: 2026-03-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/global_lib.c
Line
Count
Source
1
/*
2
 *  This program is free software; you can redistribute it and/or modify
3
 *  it under the terms of the GNU General Public License as published by
4
 *  the Free Software Foundation; either version 2 of the License, or
5
 *  (at your option) any later version.
6
 *
7
 *  This program is distributed in the hope that it will be useful,
8
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *  GNU General Public License for more details.
11
 *
12
 *  You should have received a copy of the GNU General Public License
13
 *  along with this program; if not, write to the Free Software
14
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/*
18
 * $Id: b6deb3a465c01aa12848d8e6531603a052c23bed $
19
 *
20
 * @file global_lib.c
21
 * @brief Handle global configuration, initialisation and freeing for libraries
22
 *
23
 * @copyright 2022 The FreeRADIUS server project
24
 */
25
#include <freeradius-devel/server/global_lib.h>
26
#include <freeradius-devel/server/main_config.h>
27
#include <freeradius-devel/util/atexit.h>
28
29
/*
30
 *  Terminator for array of global_lib_autoinst_t
31
 */
32
global_lib_autoinst_t const global_lib_terminator = { .name = NULL };
33
34
/*
35
 *  Global list of libraries
36
 */
37
typedef struct {
38
  fr_rb_tree_t    libs;
39
} global_lib_list_t;
40
41
static global_lib_list_t *lib_list;
42
43
/** Structure to track use of libraries.
44
 *
45
 */
46
typedef struct {
47
  fr_rb_node_t      entry;      //!<  Entry in tree of libraries
48
  global_lib_autoinst_t const *autoinit;    //!<  Autoinit structure used to manage this library
49
  uint32_t      instance_count;   //!<  Number of current uses of this library
50
  bool        initialised;    //!<  Has the init callback been run for this library
51
} global_lib_inst_t;
52
53
/** Parse the global config section for a library and call its init function
54
 *
55
 * @param[in] lib to configure and initialise
56
 * @return
57
 *  - 0 on success
58
 *  - -1 on failure
59
 */
60
static int lib_init_call(global_lib_inst_t *lib)
61
0
{
62
0
  CONF_SECTION  *global_cs, *cs;
63
64
  /*
65
   *  Find relevant config section
66
   *  If the section does not exist allocate an empty one
67
   *  so default rules can be evaluated.
68
   */
69
0
  global_cs = cf_section_find(cf_root(main_config->root_cs), "global", NULL);
70
0
  if (!global_cs) global_cs = cf_section_alloc(main_config->root_cs, main_config->root_cs, "global", NULL);
71
72
0
  cs = cf_section_find(global_cs, lib->autoinit->name, NULL);
73
0
  if (!cs) cs = cf_section_alloc(global_cs, global_cs, lib->autoinit->name, NULL);
74
75
0
  if ((cf_section_rules_push(cs, lib->autoinit->config)) < 0 ||
76
0
      (cf_section_parse(lib, lib->autoinit->inst, cs) < 0)) {
77
0
    cf_log_err(cs, "Failed evaluating configuration for %s", lib->autoinit->name);
78
0
    return -1;
79
0
  }
80
81
  /*
82
   *  Call the init callback if defined
83
   */
84
0
  if (lib->autoinit->init && (lib->autoinit->init()) < 0) return -1;
85
86
0
  lib->initialised = true;
87
88
0
  return 0;
89
0
}
90
91
/** Instantiate a list of libraries
92
 *
93
 * @param to_init Array of autoinit structures detailing libraries to initialise
94
 * @return
95
 *  - 0 on success
96
 *  - -1 on failure
97
 */
98
static int lib_auto_instantiate(global_lib_autoinst_t * const *to_init)
99
0
{
100
0
  global_lib_autoinst_t * const *p;
101
102
0
  for (p = to_init; *p != &global_lib_terminator; p++) {
103
0
    global_lib_inst_t *lib = NULL;
104
105
0
    lib = fr_rb_find(&lib_list->libs, &(global_lib_inst_t){ .autoinit = *p });
106
107
    /*
108
     *  If the library is already initialised, just increase the reference count
109
     */
110
0
    if ((lib) && (lib->initialised)) {
111
0
      lib->instance_count++;
112
0
      continue;
113
0
    }
114
115
0
    if (!lib) {
116
0
      MEM(lib = talloc_zero(lib_list, global_lib_inst_t));
117
0
      lib->autoinit = *p;
118
0
      fr_rb_insert(&lib_list->libs, lib);
119
0
    }
120
0
    lib->instance_count++;
121
122
    /*
123
     *  If the main config parsing is not complete we can't initialise the library yet
124
     */
125
0
    if (!main_config->root_cs) continue;
126
127
0
    DEBUG2("Instantiating %s", lib->autoinit->name);
128
0
    if (lib_init_call(lib) < 0) return -1;
129
0
  }
130
131
0
  return 0;
132
0
}
133
134
/** Callback for creation of "lib" symbols
135
 *
136
 */
137
int global_lib_auto_instantiate(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx)
138
0
{
139
0
  if (lib_auto_instantiate((global_lib_autoinst_t **)symbol) < 0) return -1;
140
141
0
  return 0;
142
0
}
143
144
/** Run free callbacks for external libraries no-longer in use
145
 *
146
 * @param[in] to_free Array of autoinit structures detailing libraries to free
147
 */
148
static void lib_autofree(global_lib_autoinst_t * const *to_free)
149
0
{
150
0
  global_lib_autoinst_t * const *p;
151
152
0
  for (p = to_free; *p != &global_lib_terminator; p++) {
153
0
    global_lib_inst_t *lib = NULL;
154
155
0
    lib = fr_rb_find(&lib_list->libs, &(global_lib_inst_t){ .autoinit = *p });
156
157
0
    fr_assert_msg(lib, "Library %s already freed", (*p)->name);
158
159
0
    if (--lib->instance_count > 0) continue;
160
161
    /*
162
     *  Only run the free callback if the library was successfully initialised
163
     */
164
0
    if (lib->initialised && ((*p)->free)) (*p)->free();
165
166
0
    fr_rb_remove(&lib_list->libs, lib);
167
0
    talloc_free(lib);
168
0
  }
169
0
}
170
171
/** Callback for freeing of "lib" symbols
172
 *
173
 */
174
void global_lib_autofree(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx)
175
0
{
176
0
  lib_autofree((global_lib_autoinst_t **)symbol);
177
0
}
178
179
/** Compare two fr_lib_t
180
 *
181
 */
182
static int8_t _lib_cmp(void const *one, void const *two)
183
0
{
184
0
  global_lib_inst_t const *a = one;
185
0
  global_lib_inst_t const *b = two;
186
187
0
  return CMP(a->autoinit, b->autoinit);
188
0
}
189
190
/** Free global list of libraries
191
 *
192
 * Called as an atexit function
193
 */
194
static int _lib_list_free_atexit(UNUSED void *uctx)
195
0
{
196
0
  if (talloc_free(lib_list) < 0) return -1;
197
0
  lib_list = NULL;
198
0
  return 0;
199
0
}
200
201
/** Initialise the global list of external libraries
202
 *
203
 */
204
int global_lib_init(void)
205
{
206
  if (lib_list) return 0;
207
208
  MEM(lib_list = talloc_zero(NULL, global_lib_list_t));
209
  fr_rb_inline_init(&lib_list->libs, global_lib_inst_t, entry, _lib_cmp, NULL);
210
211
  fr_atexit_global(_lib_list_free_atexit, NULL);
212
  return 0;
213
}
214
215
/** Walk the tree of libraries and instantiate any which are pending
216
 *
217
 */
218
int global_lib_instantiate(void)
219
0
{
220
  /*
221
   *  Must be called after main config has been parsed
222
   */
223
0
  fr_assert(main_config->root_cs);
224
225
0
  DEBUG2("#### Instantiating libraries ####");
226
0
  fr_rb_inorder_foreach(&lib_list->libs, global_lib_inst_t, lib) {
227
0
    if (lib->initialised) continue;
228
229
0
    DEBUG2("Instantiating %s", lib->autoinit->name);
230
0
    if (lib_init_call(lib) < 0) return -1;
231
232
0
  }
233
0
  endforeach
234
0
  return 0;
235
0
}