Coverage Report

Created: 2024-02-25 06:34

/src/kamailio/src/core/cfg/cfg_ctx.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2007 iptelorg GmbH
3
 *
4
 * This file is part of Kamailio, a free SIP server.
5
 *
6
 * Kamailio 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
 * Kamailio 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
22
#include <string.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
26
#include "../ut.h"
27
#include "cfg_struct.h"
28
#include "cfg_script.h"
29
#include "cfg_ctx.h"
30
31
32
/* linked list of all the registered cfg contexts */
33
static cfg_ctx_t *cfg_ctx_list = NULL;
34
35
/* creates a new config context that is an interface to the
36
 * cfg variables with write permission
37
 */
38
int cfg_register_ctx(cfg_ctx_t **handle, cfg_on_declare on_declare_cb)
39
0
{
40
0
  cfg_ctx_t *ctx;
41
0
  cfg_group_t *group;
42
0
  str gname;
43
44
  /* allocate memory for the new context
45
   * Better to use shm mem, because 'changed' and 'lock'
46
   * must be in shm mem anyway */
47
0
  ctx = (cfg_ctx_t *)shm_malloc(sizeof(cfg_ctx_t));
48
0
  if(!ctx) {
49
0
    SHM_MEM_ERROR;
50
0
    return -1;
51
0
  }
52
0
  memset(ctx, 0, sizeof(cfg_ctx_t));
53
0
  if(lock_init(&ctx->lock) == 0) {
54
0
    LM_ERR("failed to init lock\n");
55
0
    shm_free(ctx);
56
0
    return -1;
57
0
  }
58
59
  /* add the new ctx to the beginning of the list */
60
0
  ctx->next = cfg_ctx_list;
61
0
  cfg_ctx_list = ctx;
62
63
  /* let the driver know about the already registered groups
64
   * The handle of the context must be set before calling the
65
   * on_declare callbacks. */
66
0
  *handle = ctx;
67
0
  if(on_declare_cb) {
68
0
    ctx->on_declare_cb = on_declare_cb;
69
70
0
    for(group = cfg_group; group; group = group->next) {
71
      /* dynamic groups are not ready, the callback
72
       * will be called later when the group is fixed-up */
73
0
      if(group->dynamic != CFG_GROUP_STATIC)
74
0
        continue;
75
76
0
      gname.s = group->name;
77
0
      gname.len = group->name_len;
78
0
      on_declare_cb(&gname, group->mapping->def);
79
0
    }
80
0
  }
81
82
0
  return 0;
83
0
}
84
85
/* free the memory allocated for the contexts */
86
void cfg_ctx_destroy(void)
87
0
{
88
0
  cfg_ctx_t *ctx, *ctx2;
89
90
0
  for(ctx = cfg_ctx_list; ctx; ctx = ctx2) {
91
0
    ctx2 = ctx->next;
92
0
    shm_free(ctx);
93
0
  }
94
0
  cfg_ctx_list = NULL;
95
0
}
96
97
/* notify the drivers about the new config definition */
98
void cfg_notify_drivers(char *group_name, int group_name_len, cfg_def_t *def)
99
0
{
100
0
  cfg_ctx_t *ctx;
101
0
  str gname;
102
103
0
  gname.s = group_name;
104
0
  gname.len = group_name_len;
105
106
0
  for(ctx = cfg_ctx_list; ctx; ctx = ctx->next)
107
0
    if(ctx->on_declare_cb)
108
0
      ctx->on_declare_cb(&gname, def);
109
0
}
110
111
/* placeholder for a temporary string */
112
static char *temp_string = NULL;
113
114
/* convert the value to the requested type */
115
int convert_val(
116
    unsigned int val_type, void *val, unsigned int var_type, void **new_val)
117
0
{
118
0
  static str s;
119
0
  char *end;
120
0
  int i;
121
0
  static char buf[INT2STR_MAX_LEN];
122
123
  /* we have to convert from val_type to var_type */
124
0
  switch(CFG_INPUT_MASK(var_type)) {
125
0
    case CFG_INPUT_INT:
126
0
      if(val_type == CFG_VAR_INT) {
127
0
        *new_val = val;
128
0
        break;
129
130
0
      } else if(val_type == CFG_VAR_STRING) {
131
0
        if(!val || (((char *)val)[0] == '\0')) {
132
0
          LM_ERR("cannot convert NULL string value to integer\n");
133
0
          return -1;
134
0
        }
135
0
        *new_val = (void *)(long)strtol((char *)val, &end, 10);
136
0
        if(*end != '\0') {
137
0
          LM_ERR("cannot convert string to integer '%s'\n",
138
0
              (char *)val);
139
0
          return -1;
140
0
        }
141
0
        break;
142
143
0
      } else if(val_type == CFG_VAR_STR) {
144
0
        if(!((str *)val)->len || !((str *)val)->s) {
145
0
          LM_ERR("cannot convert NULL str value to integer\n");
146
0
          return -1;
147
0
        }
148
0
        if(str2sint((str *)val, &i)) {
149
0
          LM_ERR("cannot convert string to integer '%.*s'\n",
150
0
              ((str *)val)->len, ((str *)val)->s);
151
0
          return -1;
152
0
        }
153
0
        *new_val = (void *)(long)i;
154
0
        break;
155
0
      }
156
0
      goto error;
157
158
0
    case CFG_INPUT_STRING:
159
0
      if(val_type == CFG_VAR_INT) {
160
0
        buf[snprintf(buf, sizeof(buf) - 1, "%ld", (long)val)] = '\0';
161
0
        *new_val = buf;
162
0
        break;
163
164
0
      } else if(val_type == CFG_VAR_STRING) {
165
0
        *new_val = val;
166
0
        break;
167
168
0
      } else if(val_type == CFG_VAR_STR) {
169
0
        if(!((str *)val)->s) {
170
0
          *new_val = NULL;
171
0
          break;
172
0
        }
173
        /* the value may not be zero-terminated, thus,
174
         * a new variable has to be allocated with larger memory space */
175
0
        if(temp_string)
176
0
          pkg_free(temp_string);
177
0
        temp_string = (char *)pkg_malloc(
178
0
            sizeof(char) * (((str *)val)->len + 1));
179
0
        if(!temp_string) {
180
0
          PKG_MEM_ERROR;
181
0
          return -1;
182
0
        }
183
0
        memcpy(temp_string, ((str *)val)->s, ((str *)val)->len);
184
0
        temp_string[((str *)val)->len] = '\0';
185
0
        *new_val = (void *)temp_string;
186
0
        break;
187
0
      }
188
0
      goto error;
189
190
0
    case CFG_INPUT_STR:
191
0
      if(val_type == CFG_VAR_INT) {
192
0
        s.len = snprintf(buf, sizeof(buf) - 1, "%ld", (long)val);
193
0
        buf[s.len] = '\0';
194
0
        s.s = buf;
195
0
        *new_val = (void *)&s;
196
0
        break;
197
198
0
      } else if(val_type == CFG_VAR_STRING) {
199
0
        s.s = (char *)val;
200
0
        s.len = (s.s) ? strlen(s.s) : 0;
201
0
        *new_val = (void *)&s;
202
0
        break;
203
204
0
      } else if(val_type == CFG_VAR_STR) {
205
0
        *new_val = val;
206
0
        break;
207
0
      }
208
0
      goto error;
209
0
  }
210
211
0
  return 0;
212
213
0
error:
214
0
  LM_ERR("got a value with type %u, but expected %u\n", val_type,
215
0
      CFG_INPUT_MASK(var_type));
216
0
  return -1;
217
0
}
218
219
void convert_val_cleanup(void)
220
0
{
221
0
  if(temp_string) {
222
0
    pkg_free(temp_string);
223
0
    temp_string = NULL;
224
0
  }
225
0
}
226
227
/* returns the size of the variable */
228
static int cfg_var_size(cfg_mapping_t *var)
229
0
{
230
0
  switch(CFG_VAR_TYPE(var)) {
231
232
0
    case CFG_VAR_INT:
233
0
      return sizeof(int);
234
235
0
    case CFG_VAR_STRING:
236
0
      return sizeof(char *);
237
238
0
    case CFG_VAR_STR:
239
0
      return sizeof(str);
240
241
0
    case CFG_VAR_POINTER:
242
0
      return sizeof(void *);
243
244
0
    default:
245
0
      LM_CRIT("unknown type: %u\n", CFG_VAR_TYPE(var));
246
0
      return 0;
247
0
  }
248
0
}
249
250
/* Update the variables of the array within the meta structure
251
 * with the new default value.
252
 * The array is cloned before a change if clone is set to 1.
253
 */
254
static int cfg_update_defaults(cfg_group_meta_t *meta, cfg_group_t *group,
255
    cfg_mapping_t *var, char *new_val, int clone)
256
0
{
257
0
  int i, clone_done = 0;
258
0
  cfg_group_inst_t *array, *ginst;
259
260
0
  if(!(array = meta->array))
261
0
    return 0;
262
0
  for(i = 0; i < meta->num; i++) {
263
0
    ginst = (cfg_group_inst_t *)((char *)array
264
0
                   + (sizeof(cfg_group_inst_t) + group->size
265
0
                         - 1)
266
0
                         * i);
267
268
0
    if(!CFG_VAR_TEST(ginst, var)) {
269
      /* The variable uses the default value, it needs to be rewritten. */
270
0
      if(clone && !clone_done) {
271
        /* The array needs to be cloned before the modification */
272
0
        if(!(array = cfg_clone_array(meta, group)))
273
0
          return -1;
274
0
        ginst = (cfg_group_inst_t *)translate_pointer(
275
0
            (char *)array, (char *)meta->array, (char *)ginst);
276
        /* re-link the array to the meta-data */
277
0
        meta->array = array;
278
0
        clone_done = 1;
279
0
      }
280
0
      if((unsigned long)ginst->vars + var->offset) {
281
0
        memcpy(ginst->vars + var->offset, new_val, cfg_var_size(var));
282
0
      } else {
283
0
        LM_ERR("invalid variable offset\n");
284
0
      }
285
0
    }
286
0
  }
287
0
  return 0;
288
0
}
289
290
/* sets the value of a variable without the need of commit
291
 *
292
 * return value:
293
 *   0: success
294
 *  -1: error
295
 *   1: variable has not been found
296
 */
297
int cfg_set_now(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
298
    str *var_name, void *val, unsigned int val_type)
299
0
{
300
0
  int i;
301
0
  cfg_group_t *group;
302
0
  cfg_mapping_t *var;
303
0
  void *p, *v;
304
0
  cfg_block_t *block = NULL;
305
0
  str s, s2;
306
0
  char *old_string = NULL;
307
0
  void **replaced = NULL;
308
0
  cfg_child_cb_t *child_cb = NULL;
309
0
  cfg_group_inst_t *group_inst = NULL, *new_array = NULL;
310
0
  unsigned char *var_block;
311
312
  /* verify the context even if we do not need it now
313
   * to make sure that a cfg driver has called the function
314
   * (very very weak security) */
315
0
  if(!ctx) {
316
0
    LM_ERR("context is undefined\n");
317
0
    return -1;
318
0
  }
319
320
0
  if((val_type == CFG_VAR_UNSET) && !group_id) {
321
0
    LM_ERR("Only group instance values can be deleted\n");
322
0
    return -1;
323
0
  }
324
325
0
  if(group_id && !cfg_shmized) {
326
    /* The config group has not been shmized yet, but
327
     * an additional instance of a variable needs to be added to the group.
328
     * Add this instance to the linked list of variables, they
329
     * will be fixed later. */
330
0
    return new_add_var(group_name, *group_id, var_name, val, val_type);
331
0
  }
332
333
  /* look-up the group and the variable */
334
0
  if(cfg_lookup_var(group_name, var_name, &group, &var)) {
335
0
    if(!cfg_shmized) {
336
      /* The group may be dynamic which is not yet ready
337
       * before forking */
338
0
      if((group = cfg_lookup_group(group_name->s, group_name->len))
339
0
          && (group->dynamic == CFG_GROUP_DYNAMIC))
340
0
        return cfg_set_script_var(group, var_name, val, val_type);
341
0
    }
342
0
    return 1;
343
0
  }
344
345
  /* check whether the variable is read-only */
346
0
  if(var->def->type & CFG_READONLY) {
347
0
    LM_ERR("variable is read-only\n");
348
0
    goto error0;
349
0
  }
350
351
  /* The additional variable instances having per-child process callback
352
   * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
353
   * The reason is that such variables typically set global parameters
354
   * as opposed to per-process variables. Hence, it is not possible to set
355
   * the group handle temporary to another block, and then reset it back later. */
356
0
  if(group_id && var->def->on_set_child_cb
357
0
      && var->def->type & CFG_CB_ONLY_ONCE) {
358
0
    LM_ERR("This variable does not support multiple values.\n");
359
0
    goto error0;
360
0
  }
361
362
  /* check whether we have to convert the type */
363
0
  if((val_type != CFG_VAR_UNSET)
364
0
      && convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
365
0
    goto error0;
366
367
0
  if((val_type != CFG_VAR_UNSET) && (CFG_INPUT_TYPE(var) == CFG_INPUT_INT)
368
0
      && (var->def->min || var->def->max)) {
369
    /* perform a simple min-max check for integers */
370
0
    if(((int)(long)v < var->def->min) || ((int)(long)v > var->def->max)) {
371
0
      LM_ERR("integer value is out of range\n");
372
0
      goto error0;
373
0
    }
374
0
  }
375
376
0
  if((val_type != CFG_VAR_UNSET) && var->def->on_change_cb) {
377
    /* Call the fixup function.
378
     * There is no need to set a temporary cfg handle,
379
     * because a single variable is changed */
380
0
    if(!group_id) {
381
0
      var_block = *(group->handle);
382
0
    } else {
383
0
      if(!cfg_local) {
384
0
        LM_ERR("Local configuration is missing\n");
385
0
        goto error0;
386
0
      }
387
0
      group_inst = cfg_find_group(
388
0
          CFG_GROUP_META(cfg_local, group), group->size, *group_id);
389
0
      if(!group_inst) {
390
0
        LM_ERR("local group instance %.*s[%u] is not found\n",
391
0
            group_name->len, group_name->s, *group_id);
392
0
        goto error0;
393
0
      }
394
0
      var_block = group_inst->vars;
395
0
    }
396
397
0
    if(var->def->on_change_cb(var_block, group_name, var_name, &v) < 0) {
398
0
      LM_ERR("fixup failed\n");
399
0
      goto error0;
400
0
    }
401
0
  }
402
403
  /* Set the per-child process callback only if the default value is changed.
404
   * The callback of other instances will be called when the config is
405
   * switched to that instance. */
406
0
  if(!group_id && var->def->on_set_child_cb) {
407
    /* get the name of the variable from the internal struct,
408
     * because var_name may be freed before the callback needs it */
409
0
    s.s = group->name;
410
0
    s.len = group->name_len;
411
0
    s2.s = var->def->name;
412
0
    s2.len = var->name_len;
413
0
    child_cb = cfg_child_cb_new(
414
0
        &s, &s2, var->def->on_set_child_cb, var->def->type);
415
0
    if(!child_cb) {
416
0
      SHM_MEM_ERROR;
417
0
      goto error0;
418
0
    }
419
0
  }
420
421
0
  if(cfg_shmized) {
422
    /* make sure that nobody else replaces the global config
423
     * while the new one is prepared */
424
0
    CFG_WRITER_LOCK();
425
426
0
    if(group_id) {
427
0
      group_inst = cfg_find_group(
428
0
          CFG_GROUP_META(*cfg_global, group), group->size, *group_id);
429
0
      if(!group_inst) {
430
0
        LM_ERR("global group instance %.*s[%u] is not found\n",
431
0
            group_name->len, group_name->s, *group_id);
432
0
        goto error;
433
0
      }
434
0
      if((val_type == CFG_VAR_UNSET) && !CFG_VAR_TEST(group_inst, var)) {
435
        /* nothing to do, the variable is not set in the group instance */
436
0
        CFG_WRITER_UNLOCK();
437
0
        LM_DBG("The variable is not set\n");
438
0
        return 0;
439
0
      }
440
0
      var_block = group_inst->vars;
441
0
    } else {
442
0
      group_inst = NULL;
443
0
      var_block = CFG_GROUP_DATA(*cfg_global, group);
444
0
    }
445
446
0
    if(var->def->type & CFG_ATOMIC) {
447
      /* atomic change is allowed, we can rewrite the value
448
       * directly in the global config */
449
0
      p = var_block + var->offset;
450
451
0
    } else {
452
      /* clone the memory block, and prepare the modification */
453
0
      if(!(block = cfg_clone_global()))
454
0
        goto error;
455
456
0
      if(group_inst) {
457
        /* The additional array of the group needs to be also cloned.
458
         * When any of the variables within this array is changed, then
459
         * the complete config block and this array is replaced. */
460
0
        if(!(new_array = cfg_clone_array(
461
0
               CFG_GROUP_META(*cfg_global, group), group)))
462
0
          goto error;
463
0
        group_inst = (cfg_group_inst_t *)translate_pointer(
464
0
            (char *)new_array,
465
0
            (char *)CFG_GROUP_META(*cfg_global, group)->array,
466
0
            (char *)group_inst);
467
0
        var_block = group_inst->vars;
468
0
        CFG_GROUP_META(block, group)->array = new_array;
469
0
      } else {
470
        /* The additional array may need to be replaced depending
471
         * on whether or not there is any variable in the array set
472
         * to the default value which is changed now. If this is the case,
473
         * then the array will be replaced later when the variables are
474
         * updated.
475
         */
476
0
        var_block = CFG_GROUP_DATA(block, group);
477
0
      }
478
0
      p = var_block + var->offset;
479
0
    }
480
0
  } else {
481
    /* we are allowed to rewrite the value on-the-fly
482
     * The handle either points to group->vars, or to the
483
     * shared memory block (dynamic group) */
484
0
    p = *(group->handle) + var->offset;
485
0
  }
486
487
0
  if(val_type != CFG_VAR_UNSET) {
488
    /* set the new value */
489
0
    switch(CFG_VAR_TYPE(var)) {
490
0
      case CFG_VAR_INT:
491
0
        *(int *)p = (int)(long)v;
492
0
        break;
493
494
0
      case CFG_VAR_STRING:
495
        /* clone the string to shm mem */
496
0
        s.s = v;
497
0
        s.len = (s.s) ? strlen(s.s) : 0;
498
0
        if(cfg_clone_str(&s, &s))
499
0
          goto error;
500
0
        old_string = *(char **)p;
501
0
        *(char **)p = s.s;
502
0
        break;
503
504
0
      case CFG_VAR_STR:
505
        /* clone the string to shm mem */
506
0
        s = *(str *)v;
507
0
        if(cfg_clone_str(&s, &s))
508
0
          goto error;
509
0
        old_string = *(char **)p;
510
0
        memcpy(p, &s, sizeof(str));
511
0
        break;
512
513
0
      case CFG_VAR_POINTER:
514
0
        *(void **)p = v;
515
0
        break;
516
0
    }
517
0
    if(group_inst && !CFG_VAR_TEST_AND_SET(group_inst, var))
518
0
      old_string = NULL; /* the string is the same as the default one,
519
                * it cannot be freed */
520
0
  } else {
521
    /* copy the default value to the group instance */
522
0
    if((CFG_VAR_TYPE(var) == CFG_VAR_STRING)
523
0
        || (CFG_VAR_TYPE(var) == CFG_VAR_STR))
524
0
      old_string = *(char **)p;
525
0
    memcpy(p, CFG_GROUP_DATA(*cfg_global, group) + var->offset,
526
0
        cfg_var_size(var));
527
528
0
    CFG_VAR_TEST_AND_RESET(group_inst, var);
529
0
  }
530
531
0
  if(cfg_shmized) {
532
0
    if(!group_inst) {
533
      /* the default value is changed, the copies of this value
534
       * need to be also updated */
535
0
      if(cfg_update_defaults(
536
0
             CFG_GROUP_META(block ? block : *cfg_global, group),
537
0
             group, var, p, block ? 1 : 0) /* clone if needed */
538
0
      )
539
0
        goto error;
540
0
      if(block
541
0
          && (CFG_GROUP_META(block, group)->array
542
0
              != CFG_GROUP_META(*cfg_global, group)->array))
543
0
        new_array = CFG_GROUP_META(block, group)->array;
544
0
    }
545
546
0
    if(old_string || new_array) {
547
      /* prepare the array of the replaced strings,
548
       * and replaced group instances,
549
       * they will be freed when the old block is freed */
550
0
      replaced = (void **)shm_malloc(
551
0
          sizeof(void *)
552
0
          * ((old_string ? 1 : 0) + (new_array ? 1 : 0) + 1));
553
0
      if(!replaced) {
554
0
        SHM_MEM_ERROR;
555
0
        goto error;
556
0
      }
557
0
      i = 0;
558
0
      if(old_string) {
559
0
        replaced[i] = old_string;
560
0
        i++;
561
0
      }
562
0
      if(new_array) {
563
0
        replaced[i] = CFG_GROUP_META(*cfg_global, group)->array;
564
0
        i++;
565
0
      }
566
0
      replaced[i] = NULL;
567
0
    }
568
    /* replace the global config with the new one */
569
0
    if(block)
570
0
      cfg_install_global(block, replaced, child_cb, child_cb);
571
0
    CFG_WRITER_UNLOCK();
572
0
  } else {
573
    /* cfg_set() may be called more than once before forking */
574
0
    if(old_string && (var->flag & cfg_var_shmized))
575
0
      shm_free(old_string);
576
577
    /* flag the variable because there is no need
578
     * to shmize it again */
579
0
    var->flag |= cfg_var_shmized;
580
581
    /* the global config does not have to be replaced,
582
     * but the child callback has to be installed, otherwise the
583
     * child processes will miss the change */
584
0
    if(child_cb)
585
0
      cfg_install_child_cb(child_cb, child_cb);
586
0
  }
587
588
0
  if(val_type == CFG_VAR_INT)
589
0
    LM_INFO("%.*s.%.*s has been changed to %d\n", group_name->len,
590
0
        group_name->s, var_name->len, var_name->s, (int)(long)val);
591
592
0
  else if(val_type == CFG_VAR_STRING)
593
0
    LM_INFO("%.*s.%.*s has been changed to \"%s\"\n", group_name->len,
594
0
        group_name->s, var_name->len, var_name->s, (char *)val);
595
596
0
  else if(val_type == CFG_VAR_STR)
597
0
    LM_INFO("%.*s.%.*s has been changed to \"%.*s\"\n", group_name->len,
598
0
        group_name->s, var_name->len, var_name->s, ((str *)val)->len,
599
0
        ((str *)val)->s);
600
601
0
  else if(val_type == CFG_VAR_UNSET)
602
0
    LM_INFO("%.*s.%.*s has been deleted\n", group_name->len, group_name->s,
603
0
        var_name->len, var_name->s);
604
605
0
  else
606
0
    LM_INFO("%.*s.%.*s has been changed\n", group_name->len, group_name->s,
607
0
        var_name->len, var_name->s);
608
609
0
  if(group_id)
610
0
    LM_INFO("group id = %u\n", *group_id);
611
612
0
  convert_val_cleanup();
613
0
  return 0;
614
615
0
error:
616
0
  if(cfg_shmized)
617
0
    CFG_WRITER_UNLOCK();
618
0
  if(block)
619
0
    cfg_block_free(block);
620
0
  if(new_array)
621
0
    shm_free(new_array);
622
0
  if(child_cb)
623
0
    cfg_child_cb_free_list(child_cb);
624
0
  if(replaced)
625
0
    shm_free(replaced);
626
627
0
error0:
628
0
  LM_ERR("failed to set the variable: %.*s.%.*s\n", group_name->len,
629
0
      group_name->s, var_name->len, var_name->s);
630
631
632
0
  convert_val_cleanup();
633
0
  return -1;
634
0
}
635
636
/* wrapper function for cfg_set_now */
637
int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
638
    str *var_name, int val)
639
0
{
640
0
  return cfg_set_now(ctx, group_name, group_id, var_name, (void *)(long)val,
641
0
      CFG_VAR_INT);
642
0
}
643
644
/* wrapper function for cfg_set_now */
645
int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
646
    str *var_name, char *val)
647
0
{
648
0
  return cfg_set_now(
649
0
      ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STRING);
650
0
}
651
652
/* wrapper function for cfg_set_now */
653
int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
654
    str *var_name, str *val)
655
0
{
656
0
  return cfg_set_now(
657
0
      ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STR);
658
0
}
659
660
/* Delete a variable from the group instance.
661
 * wrapper function for cfg_set_now */
662
int cfg_del_now(
663
    cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name)
664
0
{
665
0
  return cfg_set_now(
666
0
      ctx, group_name, group_id, var_name, NULL, CFG_VAR_UNSET);
667
0
}
668
669
/* sets the value of a variable but does not commit the change
670
 *
671
 * return value:
672
 *   0: success
673
 *  -1: error
674
 *   1: variable has not been found
675
 */
676
int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
677
    str *var_name, void *val, unsigned int val_type)
678
0
{
679
0
  cfg_group_t *group;
680
0
  cfg_mapping_t *var;
681
0
  void *v;
682
0
  unsigned char *temp_handle;
683
0
  int temp_handle_created;
684
0
  cfg_changed_var_t *changed = NULL, **changed_p;
685
0
  int size;
686
0
  str s;
687
0
  cfg_group_inst_t *group_inst = NULL;
688
0
  unsigned char *var_block;
689
690
0
  if(!cfg_shmized)
691
    /* the cfg has not been shmized yet, there is no
692
     * point in registering the change and committing it later */
693
0
    return cfg_set_now(ctx, group_name, group_id, var_name, val, val_type);
694
695
0
  if(!ctx) {
696
0
    LM_ERR("context is undefined\n");
697
0
    return -1;
698
0
  }
699
700
0
  if((val_type == CFG_VAR_UNSET) && !group_id) {
701
0
    LM_ERR("Only group instance values can be deleted\n");
702
0
    return -1;
703
0
  }
704
705
  /* look-up the group and the variable */
706
0
  if(cfg_lookup_var(group_name, var_name, &group, &var))
707
0
    return 1;
708
709
  /* check whether the variable is read-only */
710
0
  if(var->def->type & CFG_READONLY) {
711
0
    LM_ERR("variable is read-only\n");
712
0
    goto error0;
713
0
  }
714
715
  /* The additional variable instances having per-child process callback
716
   * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
717
   * The reason is that such variables typically set global parameters
718
   * as opposed to per-process variables. Hence, it is not possible to set
719
   * the group handle temporary to another block, and then reset it back later. */
720
0
  if(group_id && var->def->on_set_child_cb
721
0
      && var->def->type & CFG_CB_ONLY_ONCE) {
722
0
    LM_ERR("This variable does not support multiple values.\n");
723
0
    goto error0;
724
0
  }
725
726
  /* check whether we have to convert the type */
727
0
  if((val_type != CFG_VAR_UNSET)
728
0
      && convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
729
0
    goto error0;
730
731
0
  if((val_type != CFG_VAR_UNSET) && (CFG_INPUT_TYPE(var) == CFG_INPUT_INT)
732
0
      && (var->def->min || var->def->max)) {
733
    /* perform a simple min-max check for integers */
734
0
    if(((int)(long)v < var->def->min) || ((int)(long)v > var->def->max)) {
735
0
      LM_ERR("integer value is out of range\n");
736
0
      goto error0;
737
0
    }
738
0
  }
739
740
  /* the ctx must be locked while reading and writing
741
   * the list of changed variables */
742
0
  CFG_CTX_LOCK(ctx);
743
744
0
  if((val_type != CFG_VAR_UNSET) && var->def->on_change_cb) {
745
    /* The fixup function must see also the
746
     * not yet committed values, so a temporary handle
747
     * must be prepared that points to the new config.
748
     * Only the values within the group are applied,
749
     * other modifications are not visible to the callback.
750
     * The local config is the base. */
751
0
    if(!group_id) {
752
0
      var_block = *(group->handle);
753
0
    } else {
754
0
      if(!cfg_local) {
755
0
        LM_ERR("Local configuration is missing\n");
756
0
        goto error;
757
0
      }
758
0
      group_inst = cfg_find_group(
759
0
          CFG_GROUP_META(cfg_local, group), group->size, *group_id);
760
0
      if(!group_inst) {
761
0
        LM_ERR("local group instance %.*s[%u] is not found\n",
762
0
            group_name->len, group_name->s, *group_id);
763
0
        goto error;
764
0
      }
765
0
      var_block = group_inst->vars;
766
0
    }
767
768
0
    if(ctx->changed_first) {
769
0
      temp_handle = (unsigned char *)pkg_malloc(group->size);
770
0
      if(!temp_handle) {
771
0
        PKG_MEM_ERROR;
772
0
        goto error;
773
0
      }
774
0
      temp_handle_created = 1;
775
0
      memcpy(temp_handle, var_block, group->size);
776
777
      /* apply the changes */
778
0
      for(changed = ctx->changed_first; changed;
779
0
          changed = changed->next) {
780
0
        if(changed->group != group)
781
0
          continue;
782
0
        if((!group_id && !changed->group_id_set) /* default values */
783
0
            || (group_id && !changed->group_id_set
784
0
                && !CFG_VAR_TEST(group_inst, changed->var))
785
            /* default value is changed which affects the group_instance */
786
0
            || (group_id && changed->group_id_set
787
0
                && (*group_id == changed->group_id))
788
            /* change within the group instance */
789
0
        )
790
0
          memcpy(temp_handle + changed->var->offset,
791
0
              changed->new_val.vraw, cfg_var_size(changed->var));
792
0
      }
793
0
    } else {
794
      /* there is not any change */
795
0
      temp_handle = var_block;
796
0
      temp_handle_created = 0;
797
0
    }
798
799
0
    if(var->def->on_change_cb(temp_handle, group_name, var_name, &v) < 0) {
800
0
      LM_ERR("fixup failed\n");
801
0
      if(temp_handle_created)
802
0
        pkg_free(temp_handle);
803
0
      goto error;
804
0
    }
805
0
    if(temp_handle_created)
806
0
      pkg_free(temp_handle);
807
0
  }
808
809
  /* everything went ok, we can add the new value to the list */
810
0
  size = sizeof(cfg_changed_var_t) - sizeof(((cfg_changed_var_t *)0)->new_val)
811
0
       + ((val_type != CFG_VAR_UNSET) ? cfg_var_size(var) : 0);
812
0
  changed = (cfg_changed_var_t *)shm_malloc(size);
813
0
  if(!changed) {
814
0
    SHM_MEM_ERROR;
815
0
    goto error;
816
0
  }
817
0
  memset(changed, 0, size);
818
0
  changed->group = group;
819
0
  changed->var = var;
820
0
  if(group_id) {
821
0
    changed->group_id = *group_id;
822
0
    changed->group_id_set = 1;
823
0
  }
824
825
0
  if(val_type != CFG_VAR_UNSET) {
826
0
    switch(CFG_VAR_TYPE(var)) {
827
828
0
      case CFG_VAR_INT:
829
0
        changed->new_val.vint = (int)(long)v;
830
0
        break;
831
832
0
      case CFG_VAR_STRING:
833
        /* clone the string to shm mem */
834
0
        s.s = v;
835
0
        s.len = (s.s) ? strlen(s.s) : 0;
836
0
        if(cfg_clone_str(&s, &s))
837
0
          goto error;
838
0
        changed->new_val.vp = s.s;
839
0
        break;
840
841
0
      case CFG_VAR_STR:
842
        /* clone the string to shm mem */
843
0
        s = *(str *)v;
844
0
        if(cfg_clone_str(&s, &s))
845
0
          goto error;
846
0
        changed->new_val.vstr = s;
847
0
        break;
848
849
0
      case CFG_VAR_POINTER:
850
0
        changed->new_val.vp = v;
851
0
        break;
852
0
    }
853
0
  } else {
854
0
    changed->del_value = 1;
855
0
  }
856
857
  /* Order the changes by group + group_id + original order.
858
   * Hence, the list is still kept in order within the group.
859
   * The changes can be committed faster this way, the group instances
860
   * do not have to be looked-up for each and every variable. */
861
  /* Check whether there is any variable in the list which
862
   * belongs to the same group */
863
0
  for(changed_p = &ctx->changed_first;
864
0
      *changed_p && ((*changed_p)->group != changed->group);
865
0
      changed_p = &(*changed_p)->next)
866
0
    ;
867
  /* try to find the group instance, and move changed_p to the end of
868
   * the instance. */
869
0
  for(; *changed_p && ((*changed_p)->group == changed->group)
870
0
      && (!(*changed_p)->group_id_set
871
0
          || ((*changed_p)->group_id_set && changed->group_id_set
872
0
              && ((*changed_p)->group_id <= changed->group_id)));
873
0
      changed_p = &(*changed_p)->next)
874
0
    ;
875
  /* Add the new variable before *changed_p */
876
0
  changed->next = *changed_p;
877
0
  *changed_p = changed;
878
879
0
  CFG_CTX_UNLOCK(ctx);
880
881
0
  if(val_type == CFG_VAR_INT)
882
0
    LM_INFO("%.*s.%.*s is going to be changed to %d "
883
0
        "[context=%p]\n",
884
0
        group_name->len, group_name->s, var_name->len, var_name->s,
885
0
        (int)(long)val, ctx);
886
887
0
  else if(val_type == CFG_VAR_STRING)
888
0
    LM_INFO("%.*s.%.*s is going to be changed to \"%s\" "
889
0
        "[context=%p]\n",
890
0
        group_name->len, group_name->s, var_name->len, var_name->s,
891
0
        (char *)val, ctx);
892
893
0
  else if(val_type == CFG_VAR_STR)
894
0
    LM_INFO("%.*s.%.*s is going to be changed to \"%.*s\" "
895
0
        "[context=%p]\n",
896
0
        group_name->len, group_name->s, var_name->len, var_name->s,
897
0
        ((str *)val)->len, ((str *)val)->s, ctx);
898
899
0
  else if(val_type == CFG_VAR_UNSET)
900
0
    LM_INFO("%.*s.%.*s is going to be deleted "
901
0
        "[context=%p]\n",
902
0
        group_name->len, group_name->s, var_name->len, var_name->s,
903
0
        ctx);
904
905
0
  else
906
0
    LM_INFO("%.*s.%.*s is going to be changed "
907
0
        "[context=%p]\n",
908
0
        group_name->len, group_name->s, var_name->len, var_name->s,
909
0
        ctx);
910
911
0
  if(group_id)
912
0
    LM_INFO("group id = %u [context=%p]\n", *group_id, ctx);
913
914
0
  convert_val_cleanup();
915
0
  return 0;
916
917
0
error:
918
0
  CFG_CTX_UNLOCK(ctx);
919
0
  if(changed)
920
0
    shm_free(changed);
921
0
error0:
922
0
  LM_ERR("failed to set the variable: %.*s.%.*s\n", group_name->len,
923
0
      group_name->s, var_name->len, var_name->s);
924
925
0
  convert_val_cleanup();
926
0
  return -1;
927
0
}
928
929
/* wrapper function for cfg_set_delayed */
930
int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
931
    str *var_name, int val)
932
0
{
933
0
  return cfg_set_delayed(ctx, group_name, group_id, var_name,
934
0
      (void *)(long)val, CFG_VAR_INT);
935
0
}
936
937
/* wrapper function for cfg_set_delayed */
938
int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name,
939
    unsigned int *group_id, str *var_name, char *val)
940
0
{
941
0
  return cfg_set_delayed(
942
0
      ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STRING);
943
0
}
944
945
/* wrapper function for cfg_set_delayed */
946
int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
947
    str *var_name, str *val)
948
0
{
949
0
  return cfg_set_delayed(
950
0
      ctx, group_name, group_id, var_name, (void *)val, CFG_VAR_STR);
951
0
}
952
953
/* Delete a variable from the group instance.
954
 * wrapper function for cfg_set_delayed */
955
int cfg_del_delayed(
956
    cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name)
957
0
{
958
0
  return cfg_set_delayed(
959
0
      ctx, group_name, group_id, var_name, NULL, CFG_VAR_UNSET);
960
0
}
961
962
/* commits the previously prepared changes within the context */
963
int cfg_commit(cfg_ctx_t *ctx)
964
0
{
965
0
  int replaced_num = 0;
966
0
  cfg_changed_var_t *changed, *changed2;
967
0
  cfg_block_t *block = NULL;
968
0
  void **replaced = NULL;
969
0
  cfg_child_cb_t *child_cb;
970
0
  cfg_child_cb_t *child_cb_first = NULL;
971
0
  cfg_child_cb_t *child_cb_last = NULL;
972
0
  int size;
973
0
  void *p;
974
0
  str s, s2;
975
0
  cfg_group_t *group;
976
0
  cfg_group_inst_t *group_inst = NULL;
977
978
0
  if(!ctx) {
979
0
    LM_ERR("context is undefined\n");
980
0
    return -1;
981
0
  }
982
983
0
  if(!cfg_shmized)
984
0
    return 0; /* nothing to do */
985
986
  /* the ctx must be locked while reading and writing
987
   * the list of changed variables */
988
0
  CFG_CTX_LOCK(ctx);
989
990
  /* is there any change? */
991
0
  if(!ctx->changed_first)
992
0
    goto done;
993
994
  /* Count the number of replaced strings,
995
   * and replaced group arrays.
996
   * Prepare the linked list of per-child process
997
   * callbacks, that will be added to the global list. */
998
0
  for(changed = ctx->changed_first, group = NULL; changed;
999
0
      changed = changed->next) {
1000
    /* Each string/str potentially causes an old string to be freed
1001
     * unless the variable of an additional group instance is set
1002
     * which uses the default value. This case cannot be determined
1003
     * without locking *cfg_global, hence, it is better to count these
1004
     * strings as well even though the slot might not be used later. */
1005
0
    if((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
1006
0
        || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
1007
0
      replaced_num++;
1008
1009
    /* See the above comments for strings */
1010
0
    if(group != changed->group) {
1011
0
      replaced_num++;
1012
0
      group = changed->group;
1013
0
    }
1014
1015
0
    if(changed->group && !changed->group_id_set
1016
0
        && changed->var->def->on_set_child_cb) {
1017
0
      s.s = changed->group->name;
1018
0
      s.len = changed->group->name_len;
1019
0
      s2.s = changed->var->def->name;
1020
0
      s2.len = changed->var->name_len;
1021
0
      child_cb = cfg_child_cb_new(&s, &s2,
1022
0
          changed->var->def->on_set_child_cb,
1023
0
          changed->var->def->type);
1024
0
      if(!child_cb)
1025
0
        goto error0;
1026
1027
0
      if(child_cb_last)
1028
0
        child_cb_last->next = child_cb;
1029
0
      else
1030
0
        child_cb_first = child_cb;
1031
0
      child_cb_last = child_cb;
1032
0
    }
1033
0
  }
1034
1035
0
  if(replaced_num) {
1036
    /* allocate memory for the replaced string array */
1037
0
    size = sizeof(void *) * (replaced_num + 1);
1038
0
    replaced = (void **)shm_malloc(size);
1039
0
    if(!replaced) {
1040
0
      SHM_MEM_ERROR;
1041
0
      goto error0;
1042
0
    }
1043
0
    memset(replaced, 0, size);
1044
0
  } else {
1045
    /* no replacement */
1046
0
    LM_INFO("commit operation executed without having changes\n");
1047
0
    return 0;
1048
0
  }
1049
1050
  /* make sure that nobody else replaces the global config
1051
   * while the new one is prepared */
1052
0
  CFG_WRITER_LOCK();
1053
1054
  /* clone the memory block, and prepare the modification */
1055
0
  if(!(block = cfg_clone_global()))
1056
0
    goto error;
1057
1058
  /* Apply the modifications to the buffer.
1059
   * Note that the cycle relies on the order of the groups and group
1060
   * instances, i.e. the order is group + group_id + order of commits. */
1061
0
  replaced_num = 0;
1062
0
  for(changed = ctx->changed_first, group = NULL; /* group points to the
1063
                           * last group array that
1064
                           * has been cloned */
1065
0
      changed; changed = changed->next) {
1066
0
    if(!changed->group_id_set) {
1067
0
      p = CFG_GROUP_DATA(block, changed->group) + changed->var->offset;
1068
0
      group_inst = NULL; /* force the look-up of the next group_inst */
1069
0
    } else {
1070
0
      if(group != changed->group) {
1071
        /* The group array has not been cloned yet. */
1072
0
        group = changed->group;
1073
0
        if(!(CFG_GROUP_META(block, group)->array = cfg_clone_array(
1074
0
               CFG_GROUP_META(*cfg_global, group), group))) {
1075
0
          LM_ERR("group array cannot be cloned for %.*s[%u]\n",
1076
0
              group->name_len, group->name, changed->group_id);
1077
0
          goto error;
1078
0
        }
1079
1080
0
        replaced[replaced_num] =
1081
0
            CFG_GROUP_META(*cfg_global, group)->array;
1082
0
        replaced_num++;
1083
1084
0
        group_inst = NULL; /* fore the look-up of group_inst */
1085
0
      }
1086
0
      if(group
1087
0
          && (!group_inst || (group_inst->id != changed->group_id))) {
1088
0
        group_inst = cfg_find_group(CFG_GROUP_META(block, group),
1089
0
            group->size, changed->group_id);
1090
0
      }
1091
0
      if(group && !group_inst) {
1092
0
        LM_ERR("global group instance %.*s[%u] is not found\n",
1093
0
            group->name_len, group->name, changed->group_id);
1094
0
        goto error;
1095
0
      }
1096
0
      p = group_inst->vars + changed->var->offset;
1097
0
    }
1098
0
    if(p == NULL) {
1099
0
      LM_ERR("failed to resolve valid variable offset\n");
1100
0
      goto error;
1101
0
    }
1102
1103
0
    if(((changed->group_id_set && !changed->del_value
1104
0
          && CFG_VAR_TEST_AND_SET(group_inst, changed->var))
1105
0
           || (changed->group_id_set && changed->del_value
1106
0
               && CFG_VAR_TEST_AND_RESET(group_inst, changed->var))
1107
0
           || !changed->group_id_set)
1108
0
        && ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
1109
0
            || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))) {
1110
0
      replaced[replaced_num] = *(char **)p;
1111
0
      if(replaced[replaced_num])
1112
0
        replaced_num++;
1113
      /* else do not increase replaced_num, because
1114
       * the cfg_block_free() will stop at the first
1115
       * NULL value */
1116
0
    }
1117
1118
0
    if(!changed->del_value)
1119
0
      memcpy(p, changed->new_val.vraw, cfg_var_size(changed->var));
1120
0
    else
1121
0
      memcpy(p,
1122
0
          CFG_GROUP_DATA(block, changed->group)
1123
0
              + changed->var->offset,
1124
0
          cfg_var_size(changed->var));
1125
1126
1127
0
    if(!changed->group_id_set) {
1128
      /* the default value is changed, the copies of this value
1129
       * need to be also updated */
1130
0
      if(cfg_update_defaults(CFG_GROUP_META(block, changed->group),
1131
0
             changed->group, changed->var, p,
1132
0
             (group != changed->group)) /* clone if the array
1133
                          * has not been cloned yet */
1134
0
      )
1135
0
        goto error;
1136
0
      if((group != changed->group)
1137
0
          && (CFG_GROUP_META(block, changed->group)->array
1138
0
              != CFG_GROUP_META(*cfg_global, changed->group)
1139
0
                     ->array)) {
1140
        /* The array has been cloned */
1141
0
        group = changed->group;
1142
1143
0
        replaced[replaced_num] =
1144
0
            CFG_GROUP_META(*cfg_global, group)->array;
1145
0
        replaced_num++;
1146
0
      }
1147
0
    }
1148
0
  }
1149
1150
  /* replace the global config with the new one */
1151
0
  cfg_install_global(block, replaced, child_cb_first, child_cb_last);
1152
0
  CFG_WRITER_UNLOCK();
1153
1154
  /* free the changed list */
1155
0
  for(changed = ctx->changed_first; changed; changed = changed2) {
1156
0
    changed2 = changed->next;
1157
0
    shm_free(changed);
1158
0
  }
1159
0
  ctx->changed_first = NULL;
1160
1161
0
done:
1162
0
  LM_INFO("config changes have been applied [context=%p]\n", ctx);
1163
1164
0
  CFG_CTX_UNLOCK(ctx);
1165
0
  return 0;
1166
1167
0
error:
1168
0
  if(block) {
1169
    /* clean the new block from the cloned arrays */
1170
0
    for(group = cfg_group; group; group = group->next)
1171
0
      if(CFG_GROUP_META(block, group)->array
1172
0
          && (CFG_GROUP_META(block, group)->array
1173
0
              != CFG_GROUP_META(*cfg_global, group)->array))
1174
0
        shm_free(CFG_GROUP_META(block, group)->array);
1175
    /* the block can be freed outside of the writer lock */
1176
0
  }
1177
0
  CFG_WRITER_UNLOCK();
1178
0
  if(block)
1179
0
    shm_free(block);
1180
1181
0
error0:
1182
0
  CFG_CTX_UNLOCK(ctx);
1183
1184
0
  if(child_cb_first)
1185
0
    cfg_child_cb_free_list(child_cb_first);
1186
0
  if(replaced)
1187
0
    shm_free(replaced);
1188
1189
0
  return -1;
1190
0
}
1191
1192
/* drops the not yet committed changes within the context */
1193
int cfg_rollback(cfg_ctx_t *ctx)
1194
0
{
1195
0
  cfg_changed_var_t *changed, *changed2;
1196
1197
0
  if(!ctx) {
1198
0
    LM_ERR("context is undefined\n");
1199
0
    return -1;
1200
0
  }
1201
1202
0
  if(!cfg_shmized)
1203
0
    return 0; /* nothing to do */
1204
1205
0
  LM_INFO("deleting the config changes [context=%p]\n", ctx);
1206
1207
  /* the ctx must be locked while reading and writing
1208
   * the list of changed variables */
1209
0
  CFG_CTX_LOCK(ctx);
1210
1211
0
  for(changed = ctx->changed_first; changed; changed = changed2) {
1212
0
    changed2 = changed->next;
1213
1214
0
    if(!changed->del_value
1215
0
        && ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
1216
0
            || (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))) {
1217
0
      if(changed->new_val.vp)
1218
0
        shm_free(changed->new_val.vp);
1219
0
    }
1220
0
    shm_free(changed);
1221
0
  }
1222
0
  ctx->changed_first = NULL;
1223
1224
0
  CFG_CTX_UNLOCK(ctx);
1225
1226
0
  return 0;
1227
0
}
1228
1229
/* retrieves the value of a variable
1230
 * Return value:
1231
 *  0 - success
1232
 * -1 - error
1233
 *  1 - variable exists, but it is not readable
1234
 */
1235
int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id,
1236
    str *var_name, void **val, unsigned int *val_type)
1237
0
{
1238
0
  cfg_group_t *group;
1239
0
  cfg_mapping_t *var;
1240
0
  void *p;
1241
0
  static str s; /* we need the value even
1242
           * after the function returns */
1243
0
  cfg_group_inst_t *group_inst;
1244
1245
  /* verify the context even if we do not need it now
1246
   * to make sure that a cfg driver has called the function
1247
   * (very very weak security) */
1248
0
  if(!ctx) {
1249
0
    LM_ERR("context is undefined\n");
1250
0
    return -1;
1251
0
  }
1252
1253
  /* look-up the group and the variable */
1254
0
  if(cfg_lookup_var(group_name, var_name, &group, &var))
1255
0
    return -1;
1256
1257
0
  if(var->def->on_change_cb) {
1258
    /* The variable cannot be retrieved, because the fixup
1259
     * function may have changed it, and it is better to return
1260
     * an error than an incorrect value */
1261
0
    return 1;
1262
0
  }
1263
1264
0
  if(group_id) {
1265
0
    if(!cfg_local) {
1266
0
      LM_ERR("Local configuration is missing\n");
1267
0
      return -1;
1268
0
    }
1269
0
    group_inst = cfg_find_group(
1270
0
        CFG_GROUP_META(cfg_local, group), group->size, *group_id);
1271
0
    if(!group_inst) {
1272
0
      LM_ERR("local group instance %.*s[%u] is not found\n",
1273
0
          group_name->len, group_name->s, *group_id);
1274
0
      return -1;
1275
0
    }
1276
0
    p = group_inst->vars + var->offset;
1277
1278
0
  } else {
1279
    /* use the module's handle to access the variable
1280
     * It means that the variable is read from the local config
1281
     * after forking */
1282
0
    p = *(group->handle) + var->offset;
1283
0
  }
1284
1285
0
  switch(CFG_VAR_TYPE(var)) {
1286
0
    case CFG_VAR_INT:
1287
0
      *val = (void *)(long)*(int *)p;
1288
0
      break;
1289
1290
0
    case CFG_VAR_STRING:
1291
0
      *val = (void *)*(char **)p;
1292
0
      break;
1293
1294
0
    case CFG_VAR_STR:
1295
0
      memcpy(&s, p, sizeof(str));
1296
0
      *val = (void *)&s;
1297
0
      break;
1298
1299
0
    case CFG_VAR_POINTER:
1300
0
      *val = *(void **)p;
1301
0
      break;
1302
0
  }
1303
0
  *val_type = CFG_VAR_TYPE(var);
1304
1305
0
  return 0;
1306
0
}
1307
1308
/* retrieves the default value of a variable
1309
 * Return value:
1310
 *  0 - success
1311
 * -1 - error
1312
 *  1 - variable exists, but it is not readable
1313
 */
1314
int cfg_get_default_value_by_name(cfg_ctx_t *ctx, str *group_name,
1315
    unsigned int *group_id, str *var_name, void **val,
1316
    unsigned int *val_type)
1317
0
{
1318
0
  cfg_group_t *group;
1319
0
  cfg_mapping_t *var;
1320
0
  void *p;
1321
0
  static str s; /* we need the value even
1322
           * after the function returns */
1323
1324
  /* verify the context even if we do not need it now
1325
   * to make sure that a cfg driver has called the function
1326
   * (very very weak security) */
1327
0
  if(!ctx) {
1328
0
    LM_ERR("context is undefined\n");
1329
0
    return -1;
1330
0
  }
1331
1332
  /* look-up the group and the variable */
1333
0
  if(cfg_lookup_var(group_name, var_name, &group, &var)) {
1334
0
    return -1;
1335
0
  } else {
1336
    /* if variables exist then prevents resetting the read-only ones */
1337
0
    if(var->def->type & CFG_READONLY)
1338
0
      return -1;
1339
0
  }
1340
1341
0
  if(var->def->on_change_cb) {
1342
    /* The variable cannot be retrieved, because the fixup
1343
     * function may have changed it, and it is better to return
1344
     * an error than an incorrect value */
1345
0
    return 1;
1346
0
  }
1347
1348
  /* use the module's orig_handle to access the default registered value of
1349
   * the variable for any group*/
1350
0
  p = (group->orig_handle) + var->offset;
1351
1352
0
  switch(CFG_VAR_TYPE(var)) {
1353
0
    case CFG_VAR_INT:
1354
0
      *val = (void *)(long)*(int *)p;
1355
0
      break;
1356
1357
0
    case CFG_VAR_STRING:
1358
0
      *val = (void *)*(char **)p;
1359
0
      break;
1360
1361
0
    case CFG_VAR_STR:
1362
0
      memcpy(&s, p, sizeof(str));
1363
0
      *val = (void *)&s;
1364
0
      break;
1365
1366
0
    case CFG_VAR_POINTER:
1367
0
      *val = *(void **)p;
1368
0
      break;
1369
0
  }
1370
0
  *val_type = CFG_VAR_TYPE(var);
1371
1372
0
  return 0;
1373
0
}
1374
1375
1376
/* returns the description of a variable */
1377
int cfg_help(cfg_ctx_t *ctx, str *group_name, str *var_name, char **ch,
1378
    unsigned int *input_type)
1379
0
{
1380
0
  cfg_mapping_t *var;
1381
1382
  /* verify the context even if we do not need it now
1383
   * to make sure that a cfg driver has called the function
1384
   * (very very weak security) */
1385
0
  if(!ctx) {
1386
0
    LM_ERR("context is undefined\n");
1387
0
    return -1;
1388
0
  }
1389
1390
  /* look-up the group and the variable */
1391
0
  if(cfg_lookup_var(group_name, var_name, NULL, &var))
1392
0
    return -1;
1393
1394
0
  *ch = var->def->descr;
1395
0
  if(input_type)
1396
0
    *input_type = CFG_INPUT_TYPE(var);
1397
0
  return 0;
1398
0
}
1399
1400
/* return the group name and the cfg structure definition,
1401
 * and moves the handle to the next group
1402
 * Return value:
1403
 *  0: no more group
1404
 *  1: group exists
1405
 */
1406
int cfg_get_group_next(void **h, str *gname, cfg_def_t **def)
1407
0
{
1408
0
  cfg_group_t *group;
1409
1410
0
  group = (cfg_group_t *)(*h);
1411
0
  if(group == NULL)
1412
0
    return 0;
1413
1414
0
  gname->s = group->name;
1415
0
  gname->len = group->name_len;
1416
0
  (*def) = group->mapping->def;
1417
1418
0
  (*h) = (void *)group->next;
1419
0
  return 1;
1420
0
}
1421
1422
/* Initialize the handle for cfg_diff_next() */
1423
int cfg_diff_init(cfg_ctx_t *ctx, void **h)
1424
0
{
1425
0
  if(!ctx) {
1426
0
    LM_ERR("context is undefined\n");
1427
0
    return -1;
1428
0
  }
1429
1430
0
  CFG_CTX_LOCK(ctx);
1431
0
  (*h) = (void *)ctx->changed_first;
1432
1433
0
  return 0;
1434
0
}
1435
1436
/* return the pending changes that have not been
1437
 * committed yet
1438
 * return value:
1439
 *  1: valid value is found
1440
 *  0: no more changed value found
1441
 *  -1: error occurred
1442
 */
1443
int cfg_diff_next(void **h, str *gname, unsigned int **gid, str *vname,
1444
    void **old_val, void **new_val, unsigned int *val_type)
1445
0
{
1446
0
  cfg_changed_var_t *changed;
1447
0
  cfg_group_inst_t *group_inst;
1448
0
  union cfg_var_value *pval_old, *pval_new;
1449
0
  static str old_s, new_s; /* we need the value even
1450
                 * after the function returns */
1451
1452
0
  changed = (cfg_changed_var_t *)(*h);
1453
0
  if(changed == NULL)
1454
0
    return 0;
1455
1456
0
  gname->s = changed->group->name;
1457
0
  gname->len = changed->group->name_len;
1458
0
  *gid = (changed->group_id_set ? &changed->group_id : NULL);
1459
0
  vname->s = changed->var->def->name;
1460
0
  vname->len = changed->var->name_len;
1461
1462
  /* use the module's handle to access the variable
1463
   * It means that the variable is read from the local config
1464
   * after forking */
1465
0
  if(!changed->group_id_set) {
1466
0
    pval_old = (union cfg_var_value *)(*(changed->group->handle)
1467
0
                       + changed->var->offset);
1468
0
  } else {
1469
0
    if(!cfg_local) {
1470
0
      LM_ERR("Local configuration is missing\n");
1471
0
      return -1;
1472
0
    }
1473
0
    group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, changed->group),
1474
0
        changed->group->size, changed->group_id);
1475
0
    if(!group_inst) {
1476
0
      LM_ERR("local group instance %.*s[%u] is not found\n",
1477
0
          changed->group->name_len, changed->group->name,
1478
0
          changed->group_id);
1479
0
      return -1;
1480
0
    }
1481
0
    pval_old = (union cfg_var_value *)(group_inst->vars
1482
0
                       + changed->var->offset);
1483
0
  }
1484
0
  if(!changed->del_value)
1485
0
    pval_new = &changed->new_val;
1486
0
  else
1487
0
    pval_new = (union cfg_var_value *)(*(changed->group->handle)
1488
0
                       + changed->var->offset);
1489
1490
0
  switch(CFG_VAR_TYPE(changed->var)) {
1491
0
    case CFG_VAR_INT:
1492
0
      *old_val = (void *)(long)pval_old->vint;
1493
0
      *new_val = (void *)(long)pval_new->vint;
1494
0
      break;
1495
1496
0
    case CFG_VAR_STRING:
1497
0
      *old_val = pval_old->vp;
1498
0
      *new_val = pval_new->vp;
1499
0
      break;
1500
1501
0
    case CFG_VAR_STR:
1502
0
      old_s = pval_old->vstr;
1503
0
      *old_val = (void *)&old_s;
1504
0
      new_s = pval_new->vstr;
1505
0
      *new_val = (void *)&new_s;
1506
0
      break;
1507
1508
0
    case CFG_VAR_POINTER:
1509
0
      *old_val = pval_old->vp;
1510
0
      *new_val = pval_new->vp;
1511
0
      break;
1512
0
  }
1513
0
  *val_type = CFG_VAR_TYPE(changed->var);
1514
1515
0
  (*h) = (void *)changed->next;
1516
0
  return 1;
1517
0
}
1518
1519
/* release the handle of cfg_diff_next() */
1520
void cfg_diff_release(cfg_ctx_t *ctx)
1521
0
{
1522
0
  if(!ctx) {
1523
0
    LM_ERR("context is undefined\n");
1524
0
    return;
1525
0
  }
1526
1527
0
  CFG_CTX_UNLOCK(ctx);
1528
0
}
1529
1530
/* Add a new instance to an existing group */
1531
int cfg_add_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id)
1532
0
{
1533
0
  cfg_group_t *group;
1534
0
  cfg_block_t *block = NULL;
1535
0
  void **replaced = NULL;
1536
0
  cfg_group_inst_t *new_array = NULL, *new_inst;
1537
1538
  /* verify the context even if we do not need it now
1539
   * to make sure that a cfg driver has called the function
1540
   * (very very weak security) */
1541
0
  if(!ctx) {
1542
0
    LM_ERR("context is undefined\n");
1543
0
    return -1;
1544
0
  }
1545
1546
0
  if(!cfg_shmized) {
1547
    /* Add a new variable without any value to
1548
     * the linked list of additional values. This variable
1549
     * will force a new group instance to be created. */
1550
0
    return new_add_var(group_name, group_id, NULL /* var_name */,
1551
0
        NULL /* val */, 0 /* type */);
1552
0
  }
1553
1554
0
  if(!(group = cfg_lookup_group(group_name->s, group_name->len))) {
1555
0
    LM_ERR("group not found\n");
1556
0
    return -1;
1557
0
  }
1558
1559
  /* make sure that nobody else replaces the global config
1560
   * while the new one is prepared */
1561
0
  CFG_WRITER_LOCK();
1562
0
  if(cfg_find_group(
1563
0
         CFG_GROUP_META(*cfg_global, group), group->size, group_id)) {
1564
0
    LM_DBG("the group instance already exists\n");
1565
0
    CFG_WRITER_UNLOCK();
1566
0
    return 0; /* not an error */
1567
0
  }
1568
1569
  /* clone the global memory block because the additional array can be
1570
   * replaced only together with the block. */
1571
0
  if(!(block = cfg_clone_global()))
1572
0
    goto error;
1573
1574
  /* Extend the array with a new group instance */
1575
0
  if(!(new_array = cfg_extend_array(CFG_GROUP_META(*cfg_global, group), group,
1576
0
         group_id, &new_inst)))
1577
0
    goto error;
1578
1579
  /* fill in the new group instance with the default data */
1580
0
  memcpy(new_inst->vars, CFG_GROUP_DATA(*cfg_global, group), group->size);
1581
1582
0
  CFG_GROUP_META(block, group)->array = new_array;
1583
0
  CFG_GROUP_META(block, group)->num++;
1584
1585
0
  if(CFG_GROUP_META(*cfg_global, group)->array) {
1586
    /* prepare the array of the replaced strings,
1587
     * and replaced group instances,
1588
     * they will be freed when the old block is freed */
1589
0
    replaced = (void **)shm_malloc(sizeof(void *) * 2);
1590
0
    if(!replaced) {
1591
0
      SHM_MEM_ERROR;
1592
0
      goto error;
1593
0
    }
1594
0
    replaced[0] = CFG_GROUP_META(*cfg_global, group)->array;
1595
0
    replaced[1] = NULL;
1596
0
  }
1597
  /* replace the global config with the new one */
1598
0
  cfg_install_global(block, replaced, NULL, NULL);
1599
0
  CFG_WRITER_UNLOCK();
1600
1601
0
  LM_INFO("group instance is added: %.*s[%u]\n", group_name->len,
1602
0
      group_name->s, group_id);
1603
1604
  /* Make sure that cfg_set_*() sees the change when
1605
   * the function is immediately called after the group
1606
   * instance has been added. */
1607
0
  cfg_update();
1608
1609
0
  return 0;
1610
0
error:
1611
0
  CFG_WRITER_UNLOCK();
1612
0
  if(block)
1613
0
    cfg_block_free(block);
1614
0
  if(new_array)
1615
0
    shm_free(new_array);
1616
0
  if(replaced)
1617
0
    shm_free(replaced);
1618
1619
0
  LM_ERR("Failed to add the group instance: %.*s[%u]\n", group_name->len,
1620
0
      group_name->s, group_id);
1621
1622
0
  return -1;
1623
0
}
1624
1625
/* Delete an instance of a group */
1626
int cfg_del_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id)
1627
0
{
1628
0
  cfg_group_t *group;
1629
0
  cfg_block_t *block = NULL;
1630
0
  void **replaced = NULL;
1631
0
  cfg_group_inst_t *new_array = NULL, *group_inst;
1632
0
  cfg_mapping_t *var;
1633
0
  int i, num;
1634
1635
  /* verify the context even if we do not need it now
1636
   * to make sure that a cfg driver has called the function
1637
   * (very very weak security) */
1638
0
  if(!ctx) {
1639
0
    LM_ERR("context is undefined\n");
1640
0
    return -1;
1641
0
  }
1642
1643
0
  if(!cfg_shmized) {
1644
    /* It makes no sense to delete a group instance that has not
1645
     * been created yet */
1646
0
    return -1;
1647
0
  }
1648
1649
0
  if(!(group = cfg_lookup_group(group_name->s, group_name->len))) {
1650
0
    LM_ERR("group not found\n");
1651
0
    return -1;
1652
0
  }
1653
1654
  /* make sure that nobody else replaces the global config
1655
   * while the new one is prepared */
1656
0
  CFG_WRITER_LOCK();
1657
0
  if(!(group_inst = cfg_find_group(
1658
0
         CFG_GROUP_META(*cfg_global, group), group->size, group_id))) {
1659
0
    LM_DBG("the group instance does not exist\n");
1660
0
    goto error;
1661
0
  }
1662
1663
  /* clone the global memory block because the additional array can be
1664
   * replaced only together with the block. */
1665
0
  if(!(block = cfg_clone_global()))
1666
0
    goto error;
1667
1668
  /* Remove the group instance from the array. */
1669
0
  if(cfg_collapse_array(CFG_GROUP_META(*cfg_global, group), group, group_inst,
1670
0
         &new_array))
1671
0
    goto error;
1672
1673
0
  CFG_GROUP_META(block, group)->array = new_array;
1674
0
  CFG_GROUP_META(block, group)->num--;
1675
1676
0
  if(CFG_GROUP_META(*cfg_global, group)->array) {
1677
    /* prepare the array of the replaced strings,
1678
     * and replaced group instances,
1679
     * they will be freed when the old block is freed */
1680
1681
    /* count the number of strings that has to be freed */
1682
0
    num = 0;
1683
0
    for(i = 0; i < group->num; i++) {
1684
0
      var = &group->mapping[i];
1685
0
      if(CFG_VAR_TEST(group_inst, var)
1686
0
          && ((CFG_VAR_TYPE(var) == CFG_VAR_STRING)
1687
0
              || (CFG_VAR_TYPE(var) == CFG_VAR_STR))
1688
0
          && (*(char **)(group_inst->vars + var->offset) != NULL))
1689
0
        num++;
1690
0
    }
1691
1692
0
    replaced = (void **)shm_malloc(sizeof(void *) * (num + 2));
1693
0
    if(!replaced) {
1694
0
      SHM_MEM_ERROR;
1695
0
      goto error;
1696
0
    }
1697
1698
0
    if(num) {
1699
      /* There was at least one string to free, go though the list again */
1700
0
      num = 0;
1701
0
      for(i = 0; i < group->num; i++) {
1702
0
        var = &group->mapping[i];
1703
0
        if(CFG_VAR_TEST(group_inst, var)
1704
0
            && ((CFG_VAR_TYPE(var) == CFG_VAR_STRING)
1705
0
                || (CFG_VAR_TYPE(var) == CFG_VAR_STR))
1706
0
            && (*(char **)(group_inst->vars + var->offset)
1707
0
                != NULL)) {
1708
0
          replaced[num] = *(char **)(group_inst->vars + var->offset);
1709
0
          num++;
1710
0
        }
1711
0
      }
1712
0
    }
1713
1714
0
    replaced[num] = CFG_GROUP_META(*cfg_global, group)->array;
1715
0
    replaced[num + 1] = NULL;
1716
0
  }
1717
  /* replace the global config with the new one */
1718
0
  cfg_install_global(block, replaced, NULL, NULL);
1719
0
  CFG_WRITER_UNLOCK();
1720
1721
0
  LM_INFO("group instance is deleted: %.*s[%u]\n", group_name->len,
1722
0
      group_name->s, group_id);
1723
1724
  /* Make sure that cfg_set_*() sees the change when
1725
   * the function is immediately called after the group
1726
   * instance has been deleted. */
1727
0
  cfg_update();
1728
1729
0
  return 0;
1730
0
error:
1731
0
  CFG_WRITER_UNLOCK();
1732
0
  if(block)
1733
0
    cfg_block_free(block);
1734
0
  if(new_array)
1735
0
    shm_free(new_array);
1736
0
  if(replaced)
1737
0
    shm_free(replaced);
1738
1739
0
  LM_ERR("Failed to delete the group instance: %.*s[%u]\n", group_name->len,
1740
0
      group_name->s, group_id);
1741
1742
0
  return -1;
1743
0
}
1744
1745
/* Check the existence of a group instance.
1746
 * return value:
1747
 *  1: exists
1748
 *  0: does not exist
1749
 */
1750
int cfg_group_inst_exists(
1751
    cfg_ctx_t *ctx, str *group_name, unsigned int group_id)
1752
0
{
1753
0
  cfg_group_t *group;
1754
0
  cfg_add_var_t *add_var;
1755
0
  int found;
1756
1757
  /* verify the context even if we do not need it now
1758
   * to make sure that a cfg driver has called the function
1759
   * (very very weak security) */
1760
0
  if(!ctx) {
1761
0
    LM_ERR("context is undefined\n");
1762
0
    return 0;
1763
0
  }
1764
1765
0
  if(!(group = cfg_lookup_group(group_name->s, group_name->len))) {
1766
0
    LM_ERR("group not found\n");
1767
0
    return 0;
1768
0
  }
1769
1770
0
  if(!cfg_shmized) {
1771
    /* group instances are stored in the additional variable list
1772
     * before forking */
1773
0
    found = 0;
1774
0
    for(add_var = group->add_var; add_var; add_var = add_var->next)
1775
0
      if(add_var->group_id == group_id) {
1776
0
        found = 1;
1777
0
        break;
1778
0
      }
1779
1780
0
  } else {
1781
    /* make sure that nobody else replaces the global config meantime */
1782
0
    CFG_WRITER_LOCK();
1783
0
    found = (cfg_find_group(CFG_GROUP_META(*cfg_global, group), group->size,
1784
0
             group_id)
1785
0
         != NULL);
1786
0
    CFG_WRITER_UNLOCK();
1787
0
  }
1788
1789
0
  return found;
1790
0
}
1791
1792
/* Apply the changes to a group instance as long as the additional variable
1793
 * belongs to the specified group_id. *add_var_p is moved to the next additional
1794
 * variable, and all the consumed variables are freed.
1795
 * This function can be used only during the cfg shmize process.
1796
 * For internal use only!
1797
 */
1798
int cfg_apply_list(cfg_group_inst_t *ginst, cfg_group_t *group,
1799
    unsigned int group_id, cfg_add_var_t **add_var_p)
1800
0
{
1801
0
  cfg_add_var_t *add_var;
1802
0
  cfg_mapping_t *var;
1803
0
  void *val, *v, *p;
1804
0
  str group_name, var_name, s;
1805
0
  char *old_string;
1806
1807
0
  group_name.s = group->name;
1808
0
  group_name.len = group->name_len;
1809
0
  while(*add_var_p && ((*add_var_p)->group_id == group_id)) {
1810
0
    add_var = *add_var_p;
1811
1812
0
    if(add_var->type == 0)
1813
0
      goto done; /* Nothing needs to be changed,
1814
            * this additional variable only forces a new
1815
            * group instance to be created. */
1816
0
    var_name.s = add_var->name;
1817
0
    var_name.len = add_var->name_len;
1818
1819
0
    if(!(var = cfg_lookup_var2(group, add_var->name, add_var->name_len))) {
1820
0
      LM_ERR("Variable is not found: %.*s.%.*s\n", group->name_len,
1821
0
          group->name, add_var->name_len, add_var->name);
1822
0
      goto error;
1823
0
    }
1824
1825
    /* check whether the variable is read-only */
1826
0
    if(var->def->type & CFG_READONLY) {
1827
0
      LM_ERR("variable is read-only\n");
1828
0
      goto error;
1829
0
    }
1830
1831
    /* The additional variable instances having per-child process callback
1832
     * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
1833
     * The reason is that such variables typically set global parameters
1834
     * as opposed to per-process variables. Hence, it is not possible to set
1835
     * the group handle temporary to another block, and then reset it back later. */
1836
0
    if(var->def->on_set_child_cb && var->def->type & CFG_CB_ONLY_ONCE) {
1837
0
      LM_ERR("This variable does not support multiple values.\n");
1838
0
      goto error;
1839
0
    }
1840
1841
0
    switch(add_var->type) {
1842
0
      case CFG_VAR_INT:
1843
0
        val = (void *)(long)add_var->val.i;
1844
0
        break;
1845
0
      case CFG_VAR_STR:
1846
0
        val = (str *)&(add_var->val.s);
1847
0
        break;
1848
0
      case CFG_VAR_STRING:
1849
0
        val = (char *)add_var->val.ch;
1850
0
        break;
1851
0
      default:
1852
0
        LM_ERR("unsupported variable type: %d\n", add_var->type);
1853
0
        goto error;
1854
0
    }
1855
    /* check whether we have to convert the type */
1856
0
    if(convert_val(add_var->type, val, CFG_INPUT_TYPE(var), &v))
1857
0
      goto error;
1858
1859
0
    if((CFG_INPUT_TYPE(var) == CFG_INPUT_INT)
1860
0
        && (var->def->min || var->def->max)) {
1861
      /* perform a simple min-max check for integers */
1862
0
      if(((int)(long)v < var->def->min)
1863
0
          || ((int)(long)v > var->def->max)) {
1864
0
        LM_ERR("integer value is out of range\n");
1865
0
        goto error;
1866
0
      }
1867
0
    }
1868
1869
0
    if(var->def->on_change_cb) {
1870
      /* Call the fixup function.
1871
       * The handle can point to the variables of the group instance. */
1872
0
      if(var->def->on_change_cb(ginst->vars, &group_name, &var_name, &v)
1873
0
          < 0) {
1874
0
        LM_ERR("fixup failed\n");
1875
0
        goto error;
1876
0
      }
1877
0
    }
1878
1879
0
    p = ginst->vars + var->offset;
1880
0
    old_string = NULL;
1881
    /* set the new value */
1882
0
    switch(CFG_VAR_TYPE(var)) {
1883
0
      case CFG_VAR_INT:
1884
0
        *(int *)p = (int)(long)v;
1885
0
        break;
1886
1887
0
      case CFG_VAR_STRING:
1888
        /* clone the string to shm mem */
1889
0
        s.s = v;
1890
0
        s.len = (s.s) ? strlen(s.s) : 0;
1891
0
        if(cfg_clone_str(&s, &s))
1892
0
          goto error;
1893
0
        old_string = *(char **)p;
1894
0
        *(char **)p = s.s;
1895
0
        break;
1896
1897
0
      case CFG_VAR_STR:
1898
        /* clone the string to shm mem */
1899
0
        s = *(str *)v;
1900
0
        if(cfg_clone_str(&s, &s))
1901
0
          goto error;
1902
0
        old_string = *(char **)p;
1903
0
        memcpy(p, &s, sizeof(str));
1904
0
        break;
1905
1906
0
      case CFG_VAR_POINTER:
1907
0
        *(void **)p = v;
1908
0
        break;
1909
0
    }
1910
0
    if(CFG_VAR_TEST_AND_SET(ginst, var) && old_string)
1911
0
      shm_free(old_string); /* the string was already in shm memory,
1912
                * it needs to be freed.
1913
                * This can happen when the same variable is set
1914
                * multiple times before forking. */
1915
1916
0
    if(add_var->type == CFG_VAR_INT)
1917
0
      LM_INFO("%.*s[%u].%.*s has been set to %d\n", group_name.len,
1918
0
          group_name.s, group_id, var_name.len, var_name.s,
1919
0
          (int)(long)val);
1920
1921
0
    else if(add_var->type == CFG_VAR_STRING)
1922
0
      LM_INFO("%.*s[%u].%.*s has been set to \"%s\"\n", group_name.len,
1923
0
          group_name.s, group_id, var_name.len, var_name.s,
1924
0
          (char *)val);
1925
1926
0
    else /* str type */
1927
0
      LM_INFO("%.*s[%u].%.*s has been set to \"%.*s\"\n", group_name.len,
1928
0
          group_name.s, group_id, var_name.len, var_name.s,
1929
0
          ((str *)val)->len, ((str *)val)->s);
1930
1931
0
    convert_val_cleanup();
1932
1933
0
  done:
1934
0
    *add_var_p = add_var->next;
1935
1936
0
    if((add_var->type == CFG_VAR_STR) && add_var->val.s.s)
1937
0
      pkg_free(add_var->val.s.s);
1938
0
    else if((add_var->type == CFG_VAR_STRING) && add_var->val.ch)
1939
0
      pkg_free(add_var->val.ch);
1940
0
    pkg_free(add_var);
1941
0
  }
1942
0
  return 0;
1943
1944
0
error:
1945
0
  LM_ERR("Failed to set the value for: %.*s[%u].%.*s\n", group->name_len,
1946
0
      group->name, group_id, add_var->name_len, add_var->name);
1947
0
  convert_val_cleanup();
1948
0
  return -1;
1949
0
}