all: updated FSF address in GPL text
[sip-router] / cfg / cfg_script.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2008 iptelorg GmbH
5  *
6  * This file is part of SIP-router, a free SIP server.
7  *
8  * SIP-router is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * SIP-router is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  * History
23  * -------
24  *  2008-01-24  dynamic groups are introduced in order to make
25  *              variable declaration possible in the script (Miklos)
26  */
27
28 #include <string.h>
29
30 #include "../mem/mem.h"
31 #include "../ut.h"
32 #include "cfg_struct.h"
33 #include "cfg.h"
34 #include "cfg_ctx.h"
35 #include "cfg_script.h"
36
37 /* allocates memory for a new config script variable
38  * The value of the variable is not set!
39  */
40 cfg_script_var_t *new_cfg_script_var(char *gname, char *vname, unsigned int type,
41                                         char *descr)
42 {
43         cfg_group_t     *group;
44         cfg_script_var_t        *var;
45         int     gname_len, vname_len, descr_len;
46
47         LOG(L_DBG, "DEBUG: new_cfg_script_var(): declaring %s.%s\n", gname, vname);
48
49         if (cfg_shmized) {
50                 LOG(L_ERR, "ERROR: new_cfg_script_var(): too late variable declaration, "
51                         "the config has been already shmized\n");
52                 return NULL;
53         }
54
55         gname_len = strlen(gname);
56         vname_len = strlen(vname);
57         /* the group may have been already declared */
58         group = cfg_lookup_group(gname, gname_len);
59         if (group) {
60                 if (group->dynamic == CFG_GROUP_STATIC) {
61                         /* the group has been already declared by a module or by the core */
62                         LOG(L_ERR, "ERROR: new_cfg_script_var(): "
63                                 "configuration group has been already declared: %s\n",
64                                 gname);
65                         return NULL;
66                 }
67                 /* the dynamic or empty group is found */
68                 /* verify that the variable does not exist */
69                 for (   var = (cfg_script_var_t *)group->vars;
70                         var;
71                         var = var->next
72                 ) {
73                         if ((var->name_len == vname_len) &&
74                         (memcmp(var->name, vname, vname_len) == 0)) {
75                                 LOG(L_ERR, "ERROR: new_cfg_script_var(): variable already exists: %s.%s\n",
76                                                 gname, vname);
77                                 return NULL;
78                         }
79                 }
80                 if (group->dynamic == CFG_GROUP_UNKNOWN)
81                         group->dynamic = CFG_GROUP_DYNAMIC;
82
83         } else {
84                 /* create a new group with NULL values, we will fix it later,
85                 when all the variables are known */
86                 group = cfg_new_group(gname, gname_len,
87                                         0 /* num */, NULL /* mapping */,
88                                         NULL /* vars */, 0 /* size */, NULL /* handle */);
89                                         
90                 if (!group) goto error;
91                 group->dynamic = CFG_GROUP_DYNAMIC;
92         }
93
94         switch (type) {
95         case CFG_VAR_INT:
96                 group->size = ROUND_INT(group->size);
97                 group->size += sizeof(int);
98                 break;
99
100         case CFG_VAR_STR:
101                 group->size = ROUND_POINTER(group->size);
102                 group->size += sizeof(str);
103                 break;
104
105         default:
106                 LOG(L_ERR, "ERROR: new_cfg_script_var(): unsupported variable type\n");
107                 return NULL;
108         }
109
110         group->num++;
111         if (group->num > CFG_MAX_VAR_NUM) {
112                 LOG(L_ERR, "ERROR: new_cfg_script_var(): too many variables (%d) within a single group,"
113                         " the limit is %d. Increase CFG_MAX_VAR_NUM, or split the group into multiple"
114                         " definitions.\n",
115                         group->num, CFG_MAX_VAR_NUM);
116                 return NULL;
117         }
118
119         var = (cfg_script_var_t *)pkg_malloc(sizeof(cfg_script_var_t));
120         if (!var) goto error;
121         memset(var, 0, sizeof(cfg_script_var_t));
122         var->type = type;
123
124         /* add the variable to the group */
125         var->next = (cfg_script_var_t *)(void *)group->vars;
126         group->vars = (char *)(void *)var;
127
128         /* clone the name of the variable */
129         var->name = (char *)pkg_malloc(sizeof(char) * (vname_len + 1));
130         if (!var->name) goto error;
131         memcpy(var->name, vname, vname_len + 1);
132         var->name_len = vname_len;
133
134         if (descr) {
135                 /* save the description */
136                 descr_len = strlen(descr);
137                 var->descr = (char *)pkg_malloc(sizeof(char) * (descr_len + 1));
138                 if (!var->descr) goto error;
139                 memcpy(var->descr, descr, descr_len + 1);
140         }
141
142         return var;
143
144 error:
145         LOG(L_ERR, "ERROR: new_cfg_script_var(): not enough memory\n");
146         return NULL;
147 }
148
149 /* Rewrite the value of an already declared script variable before forking.
150  * Return value:
151  *       0: success
152  *      -1: error
153  *       1: variable not found
154  */
155 int cfg_set_script_var(cfg_group_t *group, str *var_name,
156                         void *val, unsigned int val_type)
157 {
158         cfg_script_var_t        *var;
159         void    *v;
160         str     s;
161
162         if (cfg_shmized || (group->dynamic != CFG_GROUP_DYNAMIC)) {
163                 LOG(L_ERR, "BUG: cfg_set_script_var(): Not a dynamic group before forking\n");
164                 return -1;
165         }
166
167         for (   var = (cfg_script_var_t *)(void *)group->vars;
168                 var;
169                 var = var->next
170         ) {
171                 if ((var->name_len == var_name->len)
172                         && (memcmp(var->name, var_name->s, var_name->len) == 0)
173                 ) {
174                         switch (var->type) {
175                         case CFG_VAR_INT:
176                                 if (convert_val(val_type, val, CFG_INPUT_INT, &v))
177                                         goto error;
178                                 if ((var->min || var->max)
179                                         && ((var->min > (int)(long)v) || (var->max < (int)(long)v))
180                                 ) {
181                                         LOG(L_ERR, "ERROR: cfg_set_script_var(): integer value is out of range\n");
182                                         goto error;
183                                 }
184                                 var->val.i = (int)(long)v;
185                                 break;
186
187                         case CFG_VAR_STR:
188                                 if (convert_val(val_type, val, CFG_INPUT_STR, &v))
189                                         goto error;
190                                 if (((str *)v)->s) {
191                                         s.len = ((str *)v)->len;
192                                         s.s = pkg_malloc(sizeof(char) * (s.len + 1));
193                                         if (!s.s) {
194                                                 LOG(L_ERR, "ERROR: cfg_set_script_var(): not enough memory\n");
195                                                 goto error;
196                                         }
197                                         memcpy(s.s, ((str *)v)->s, s.len);
198                                         s.s[s.len] = '\0';
199                                 } else {
200                                         s.s = NULL;
201                                         s.len = 0;
202                                 }
203                                 if (var->val.s.s)
204                                         pkg_free(var->val.s.s);
205                                 var->val.s = s;
206                                 break;
207
208                         default:
209                                 LOG(L_ERR, "ERROR: cfg_set_script_var(): unsupported variable type\n");
210                                 goto error;
211                         }
212
213                         convert_val_cleanup();
214                         return 0;
215                 }
216         }
217
218         return 1;
219
220 error:
221         LOG(L_ERR, "ERROR: cfg_set_script_var(): failed to set the script variable: %.*s.%.*s\n",
222                         group->name_len, group->name,
223                         var_name->len, var_name->s);
224         return -1;
225 }
226
227 /* fix-up the dynamically declared group:
228  *  - allocate memory for the arrays
229  *  - set the values within the memory block
230  */
231 int cfg_script_fixup(cfg_group_t *group, unsigned char *block)
232 {
233         cfg_mapping_t           *mapping = NULL;
234         cfg_def_t               *def = NULL;
235         void                    **handle = NULL;
236         int                     i, offset;
237         cfg_script_var_t        *script_var, *script_var2;
238         str                     s;
239
240         mapping = (cfg_mapping_t *)pkg_malloc(sizeof(cfg_mapping_t)*group->num);
241         if (!mapping) goto error;
242         memset(mapping, 0, sizeof(cfg_mapping_t)*group->num);
243
244         /* The variable definition array must look like as if it was declared
245          * in C code, thus, add an additional slot at the end with NULL values */
246         def = (cfg_def_t *)pkg_malloc(sizeof(cfg_def_t)*(group->num + 1));
247         if (!def) goto error;
248         memset(def, 0, sizeof(cfg_def_t)*(group->num + 1));
249
250         /* fill the definition and the mapping arrays */
251         offset = 0;
252         for (   i = 0, script_var = (cfg_script_var_t *)group->vars;
253                 script_var;
254                 i++, script_var = script_var->next
255         ) {
256                 /* there has been already memory allocated for the name */
257                 def[i].name = script_var->name;
258                 def[i].type = script_var->type | (script_var->type << CFG_INPUT_SHIFT);
259                 def[i].descr = script_var->descr;
260                 def[i].min = script_var->min;
261                 def[i].max = script_var->max;
262
263                 mapping[i].def = &(def[i]);
264                 mapping[i].name_len = script_var->name_len;
265                 mapping[i].pos = i;
266
267                 switch (script_var->type) {
268                 case CFG_VAR_INT:
269                         offset = ROUND_INT(offset);
270                         mapping[i].offset = offset;
271
272                         *(int *)(block + offset) = script_var->val.i;
273
274                         offset += sizeof(int);
275                         break;
276
277                 case CFG_VAR_STR:
278                         offset = ROUND_POINTER(offset);
279                         mapping[i].offset = offset;
280
281                         if (cfg_clone_str(&(script_var->val.s), &s)) goto error;
282                         memcpy(block + offset, &s, sizeof(str));
283                         mapping[i].flag |= cfg_var_shmized;
284
285                         offset += sizeof(str);
286                         break;
287                 }
288         }
289
290         /* allocate a handle even if it will not be used to
291         directly access the variable, like handle->variable
292         cfg_get_* functions access the memory block via the handle
293         to make sure that it is always safe, thus, it must be created */
294         handle = (void **)pkg_malloc(sizeof(void *));
295         if (!handle) goto error;
296         *handle = NULL;
297         group->handle = handle;
298
299         group->mapping = mapping;
300
301         /* everything went fine, we can free the temporary list */
302         script_var = (cfg_script_var_t *)group->vars;
303         group->vars = NULL;
304         while (script_var) {
305                 script_var2 = script_var->next;
306                 if ((script_var->type == CFG_VAR_STR) && script_var->val.s.s)
307                         pkg_free(script_var->val.s.s);
308                 pkg_free(script_var);
309                 script_var = script_var2;
310         }
311
312         return 0;
313
314 error:
315         if (mapping) pkg_free(mapping);
316         if (def) pkg_free(def);
317         if (handle) pkg_free(handle);
318
319         LOG(L_ERR, "ERROR: cfg_script_fixup(): not enough memory\n");
320         return -1;
321 }
322
323 /* destory a dynamically allocated group definition */
324 void cfg_script_destroy(cfg_group_t *group)
325 {
326         int     i;
327         cfg_script_var_t        *script_var, *script_var2;
328
329         if (group->mapping && group->mapping->def) {
330                 for (i=0; i<group->num; i++) {
331                         if (group->mapping->def[i].name)
332                                 pkg_free(group->mapping->def[i].name);
333                         if (group->mapping->def[i].descr)
334                                 pkg_free(group->mapping->def[i].descr);
335                 }
336                 pkg_free(group->mapping->def);
337         }
338         if (group->mapping) pkg_free(group->mapping);
339         if (group->handle) pkg_free(group->handle);
340
341         /* it may happen that the temporary var list
342         still exists because the fixup failed and did not complete */
343         script_var = (cfg_script_var_t *)group->vars;
344         while (script_var) {
345                 script_var2 = script_var->next;
346                 if ((script_var->type == CFG_VAR_STR) && script_var->val.s.s) 
347                         pkg_free(script_var->val.s.s);
348                 pkg_free(script_var);
349                 script_var = script_var2;
350         }
351 }