Coverage Report

Created: 2025-07-09 06:29

/src/opensips/sr_module_deps.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2014-2021 OpenSIPS Solutions
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * opensips is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19
 */
20
21
#include <stdlib.h>
22
#include <stdarg.h>
23
#include <string.h>
24
25
#include "sr_module_deps.h"
26
27
#include "dprint.h"
28
#include "error.h"
29
#include "mem/mem.h"
30
31
#include "sr_module.h"
32
#include "pt.h"
33
34
/* the list head of unsolved module dependencies: struct sr_module ----> "module_name" */
35
static struct sr_module_dep unsolved_deps;
36
37
#define mod_type_to_string(type) \
38
0
  (type == MOD_TYPE_NULL ? NULL : \
39
0
   type == MOD_TYPE_SQLDB ? "sqldb module" : \
40
0
   type == MOD_TYPE_CACHEDB ? "cachedb module" : \
41
0
   type == MOD_TYPE_AAA ? "aaa module" : \
42
0
   "module")
43
44
45
module_dependency_t *alloc_module_dep(enum module_type mod_type, char *mod_name,
46
                    unsigned int dep_type)
47
0
{
48
0
  return _alloc_module_dep(mod_type, mod_name, dep_type, MOD_TYPE_NULL);
49
0
}
50
51
52
module_dependency_t *_alloc_module_dep(enum module_type mod_type, char *mod_name,
53
                             unsigned int dep_type, ... /* , MOD_TYPE_NULL */)
54
0
{
55
0
  va_list ap;
56
0
  module_dependency_t *md;
57
0
  int ndeps = 1;
58
59
  /* always keep a zeroed entry at the end */
60
0
  md = pkg_malloc(2 * sizeof *md);
61
0
  if (!md) {
62
0
    LM_ERR("oom\n");
63
0
    return NULL;
64
0
  }
65
66
0
  memset(md, 0, 2 * sizeof *md);
67
0
  md->mod_type = mod_type;
68
0
  md->mod_name = mod_name;
69
0
  md->type = dep_type;
70
71
0
  va_start(ap, dep_type);
72
73
0
  for (;;) {
74
0
    mod_type = va_arg(ap, enum module_type);
75
0
    if (mod_type == MOD_TYPE_NULL)
76
0
      break;
77
78
0
    ndeps++;
79
80
0
    md = pkg_realloc(md, (ndeps + 1) * sizeof *md);
81
0
    if (!md) {
82
0
      LM_ERR("oom\n");
83
0
      va_end(ap);
84
0
      return NULL;
85
0
    }
86
0
    memset(&md[ndeps], 0, sizeof *md);
87
88
0
    md[ndeps - 1].mod_type = mod_type;
89
0
    md[ndeps - 1].mod_name = va_arg(ap, char *);
90
0
    md[ndeps - 1].type = va_arg(ap, unsigned int);
91
0
  }
92
93
0
  va_end(ap);
94
0
  return md;
95
0
}
96
97
98
module_dependency_t *get_deps_sqldb_url(const param_export_t *param)
99
0
{
100
0
  char *db_url = *(char **)param->param_pointer;
101
102
0
  if (param->type & USE_FUNC_PARAM)
103
0
    return alloc_module_dep(MOD_TYPE_SQLDB, NULL, DEP_WARN);
104
105
0
  if (!db_url || strlen(db_url) == 0)
106
0
    return NULL;
107
108
0
  return alloc_module_dep(MOD_TYPE_SQLDB, NULL, DEP_WARN);
109
0
}
110
111
112
module_dependency_t *get_deps_cachedb_url(const param_export_t *param)
113
0
{
114
0
  char *cdb_url = *(char **)param->param_pointer;
115
116
0
  if (!cdb_url || strlen(cdb_url) == 0)
117
0
    return NULL;
118
119
0
  return alloc_module_dep(MOD_TYPE_CACHEDB, NULL, DEP_ABORT);
120
0
}
121
122
123
static int add_module_dependency(struct sr_module *mod, const module_dependency_t *dep,
124
                 const char *script_param)
125
0
{
126
0
  struct sr_module_dep *md;
127
0
  int len;
128
129
0
  LM_DBG("adding type %d dependency %s - (%s %s)\n", dep->type,
130
0
      mod->exports->name, mod_type_to_string(dep->mod_type),
131
0
      dep->mod_name);
132
133
0
  len = dep->mod_name ? strlen(dep->mod_name) : 0;
134
135
0
  md = pkg_malloc(sizeof *md + len + 1);
136
0
  if (!md) {
137
0
    LM_CRIT("out of pkg mem\n");
138
0
    return -1;
139
0
  }
140
0
  memset(md, 0, sizeof *md + len + 1);
141
142
0
  md->mod = mod;
143
0
  md->mod_type = dep->mod_type;
144
0
  md->type = dep->type;
145
0
  if (dep->mod_name) {
146
0
    md->dep.s = (char *)(md + 1);
147
0
    md->dep.len = len;
148
0
    memcpy(md->dep.s, dep->mod_name, len);
149
0
  }
150
151
0
  if (script_param)
152
0
    md->script_param = script_param;
153
154
0
  md->next = unsolved_deps.next;
155
0
  unsolved_deps.next = md;
156
157
0
  return 0;
158
0
}
159
160
161
/*
162
 * register all OpenSIPS module dependencies of a single module parameter
163
 */
164
int add_modparam_dependencies(struct sr_module *mod, const param_export_t *param)
165
0
{
166
0
  struct sr_module_dep *it, *tmp;
167
0
  module_dependency_t *md;
168
0
  const modparam_dependency_t *mpd;
169
0
  struct module_dependency *(*get_deps_f)(const param_export_t *param) = NULL;
170
171
0
  if (!mod->exports->deps)
172
0
    return 0;
173
174
  /* lookup this parameter's dependency fetching function */
175
0
  for (mpd = mod->exports->deps->mpd; mpd->script_param; mpd++) {
176
0
    if (strcmp(mpd->script_param, param->name) == 0)
177
0
      get_deps_f = mpd->get_deps_f;
178
0
  }
179
180
  /* 98% of OpenSIPS's modparams will stop here */
181
0
  if (!get_deps_f)
182
0
    return 0;
183
184
  /* clear previous entries in case this parameter is set multiple times */
185
0
  for (it = &unsolved_deps; it && it->next; it = it->next) {
186
0
    if (strcmp(it->next->mod->exports->name, mod->exports->name) == 0 &&
187
0
      (it->next->script_param &&
188
0
       strcmp(it->next->script_param, param->name) == 0)) {
189
190
0
      tmp = it->next;
191
0
      it->next = it->next->next;
192
0
      pkg_free(tmp);
193
0
    }
194
0
  }
195
196
0
  md = get_deps_f(param);
197
0
  if (!md)
198
0
    return 0;
199
200
0
  LM_DBG("adding modparam dependencies:\n");
201
0
  for (; md->mod_type != MOD_TYPE_NULL; md++) {
202
0
    LM_DBG("dependency found: %s ---> ( %s %s )\n", mod->exports->name,
203
0
        mod_type_to_string(md->mod_type), md->mod_name);
204
205
0
    if (add_module_dependency(mod, md, param->name) != 0) {
206
0
      LM_ERR("failed to add dep!\n");
207
0
      return E_BUG;
208
0
    }
209
0
  }
210
211
0
  return 0;
212
0
}
213
214
215
/*
216
 * register all OpenSIPS module dependencies of a single module
217
 */
218
int add_module_dependencies(struct sr_module *mod)
219
0
{
220
0
  const module_dependency_t *md;
221
222
0
  for (md = mod->exports->deps->md; md->mod_type != MOD_TYPE_NULL; md++) {
223
0
    if (add_module_dependency(mod, md, NULL) != 0) {
224
0
      LM_ERR("failed to add mod dep\n");
225
0
      return -1;
226
0
    }
227
0
  }
228
229
0
  return 0;
230
0
}
231
232
static struct sr_module_dep *make_dep(struct sr_module_dep *md,
233
    unsigned int dep_type, unsigned int df, struct sr_module *mod_a,
234
    struct sr_module *mod_b)
235
0
{
236
0
  struct sr_module_dep **dip_a, **dip_b;
237
238
0
  switch (df) {
239
0
  case DEP_REVERSE_MINIT:
240
0
    dip_a = &mod_a->sr_deps_init;
241
0
    dip_b = &mod_b->sr_deps_init;
242
0
    break;
243
0
  case DEP_REVERSE_CINIT:
244
0
    dip_a = &mod_a->sr_deps_cinit;
245
0
    dip_b = &mod_b->sr_deps_cinit;
246
0
    break;
247
0
  case DEP_REVERSE_DESTROY:
248
0
    dip_a = &mod_a->sr_deps_destroy;
249
0
    dip_b = &mod_b->sr_deps_destroy;
250
0
    break;
251
0
  default:
252
0
    LM_ERR("BUG, unhandled dep_type: %d\n", df);
253
0
    abort();
254
0
  }
255
256
0
  if (md == NULL) {
257
0
    md = pkg_malloc(sizeof *md);
258
0
    if (!md) {
259
0
      LM_ERR("no more pkg\n");
260
0
      return NULL;
261
0
    }
262
0
    memset(md, 0, sizeof *md);
263
0
  }
264
265
0
  if (dep_type & df) {
266
0
    md->mod = mod_a;
267
0
    md->next = *dip_b;
268
0
    *dip_b = md;
269
0
  } else {
270
0
    md->mod = mod_b;
271
0
    md->next = *dip_a;
272
0
    *dip_a = md;
273
0
  }
274
0
  return md;
275
0
}
276
277
int solve_module_dependencies(struct sr_module *modules)
278
0
{
279
0
  struct sr_module_dep *md, *it;
280
0
  struct sr_module *this, *mod;
281
0
  enum module_type mod_type;
282
0
  unsigned int dep_type;
283
0
  int dep_solved;
284
285
  /*
286
   * now that we've loaded all shared libraries,
287
   * we can attempt to solve each dependency
288
   */
289
0
  for (it = unsolved_deps.next; it; ) {
290
0
    md = it;
291
0
    it = it->next;
292
293
0
    LM_DBG("solving dependency %s -> %s %.*s\n", md->mod->exports->name,
294
0
         mod_type_to_string(md->mod_type), md->dep.len, md->dep.s);
295
296
0
    this = md->mod;
297
0
    dep_type = md->type;
298
0
    int byname = !!md->dep.s;
299
0
    mod_type = md->mod_type;
300
301
    /*
302
     * for generic dependencies (e.g. dialog depends on MOD_TYPE_SQLDB),
303
     * first load all modules of given type
304
     *
305
     * re-purpose this @md structure by linking it into a module's
306
     * list of dependencies (will be used at init time)
307
     *
308
     * md->mod used to point to (highlighted with []):
309
     *    [sr_module A] ---> "mod_name"
310
     *
311
     * now, the dependency is solved. md->mod will point to:
312
     *    sr_module A  ---> [sr_module B]
313
     */
314
315
0
    for (dep_solved = 0, mod = modules; mod; mod = mod->next) {
316
0
      if (!byname) {
317
0
        if (mod == this || mod->exports->type != mod_type)
318
0
          continue;
319
0
      } else {
320
0
        if (strcmp(mod->exports->name, md->dep.s) != 0)
321
0
          continue;
322
0
        if (mod->exports->type != mod_type)
323
0
          LM_BUG("[%.*s %d] -> [%s %d]\n", md->dep.len, md->dep.s,
324
0
            mod_type, mod->exports->name,
325
0
            mod->exports->type);
326
0
      }
327
328
0
      if (!make_dep(md, dep_type, DEP_REVERSE_MINIT, this, mod))
329
0
        return -1;
330
0
      md = NULL;
331
332
0
      if (!make_dep(NULL, dep_type, DEP_REVERSE_DESTROY, mod, this))
333
0
        return -1;
334
335
0
      if (!make_dep(NULL, dep_type, DEP_REVERSE_CINIT, this, mod))
336
0
        return -1;
337
338
0
      dep_solved++;
339
0
      if (byname)
340
0
        break;
341
0
    }
342
343
    /* reverse-init dependencies are always solved! */
344
0
    if (dep_solved || dep_type & DEP_REVERSE_MINIT)
345
0
      continue;
346
347
    /* treat unmet dependencies using the intended behaviour */
348
0
    if (dep_type & DEP_SILENT) {
349
0
      LM_DBG("module %s soft-depends on "
350
0
                 "%s%s%s%.*s%s%s, and %s loaded -- continuing\n",
351
0
          md->mod->exports->name,
352
0
          md->dep.len == 0 ?
353
0
            ((md->mod_type == MOD_TYPE_SQLDB ||
354
0
              md->mod_type == MOD_TYPE_AAA) ? "an " :
355
0
            md->mod_type == MOD_TYPE_CACHEDB ? "a " : "") : "",
356
0
          mod_type_to_string(md->mod_type),
357
0
          md->dep.len == 0 ? "" : " ",
358
0
          md->dep.len, md->dep.s,
359
0
          md->script_param ? " due to modparam " : "",
360
0
          md->script_param ? md->script_param : "",
361
0
          md->dep.len == 0 ? "none was" : "it was not");
362
0
    } else if (dep_type & (DEP_WARN|DEP_ABORT)) {
363
0
      LM_WARN("module %s depends on %s%s%s%.*s%s%s, but %s loaded!\n",
364
0
          md->mod->exports->name,
365
0
          md->dep.len == 0 ?
366
0
            ((md->mod_type == MOD_TYPE_SQLDB ||
367
0
              md->mod_type == MOD_TYPE_AAA) ? "an " :
368
0
            md->mod_type == MOD_TYPE_CACHEDB ? "a " : "") : "",
369
0
          mod_type_to_string(md->mod_type),
370
0
          md->dep.len == 0 ? "" : " ",
371
0
          md->dep.len, md->dep.s,
372
0
          md->script_param ? " due to modparam " : "",
373
0
          md->script_param ? md->script_param : "",
374
0
          md->dep.len == 0 ? "none was" : "it was not");
375
0
    }
376
377
0
    pkg_free(md);
378
0
    if (dep_type & DEP_ABORT)
379
0
      return -1;
380
0
  }
381
382
0
  return 0;
383
0
}
384
385
386
/* After all modules are loaded & destroyed, free the dep structures */
387
void free_module_dependencies(struct sr_module *modules)
388
0
{
389
0
  struct sr_module_dep *aux;
390
0
  struct sr_module *mod;
391
392
0
  for (mod = modules; mod; mod = mod->next) {
393
0
    while (mod->sr_deps_init) {
394
0
      aux = mod->sr_deps_init;
395
0
      mod->sr_deps_init = aux->next;
396
0
      pkg_free(aux);
397
0
    }
398
399
0
    while (mod->sr_deps_destroy) {
400
0
      aux = mod->sr_deps_destroy;
401
0
      mod->sr_deps_destroy = aux->next;
402
0
      pkg_free(aux);
403
0
    }
404
0
  }
405
0
}