Don't declare a function that takes variable arguments as inline, because it's
[asterisk-bristuff.git] / channels / misdn_config.c
blobcb16622519bf830d78884a044de6aa780e4fd513
1 /*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2005, Christian Richter
6 * Christian Richter <crich@beronet.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
20 /*!
21 * \file
23 * \brief chan_misdn configuration management
24 * \author Christian Richter <crich@beronet.com>
26 * \ingroup channel_drivers
29 #include "asterisk.h"
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <errno.h>
38 #include "chan_misdn_config.h"
40 #include "asterisk/config.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/strings.h"
46 #include "asterisk/utils.h"
48 #define AST_LOAD_CFG ast_config_load
49 #define AST_DESTROY_CFG ast_config_destroy
51 #define NO_DEFAULT "<>"
52 #define NONE 0
54 #define GEN_CFG 1
55 #define PORT_CFG 2
56 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
57 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
59 enum misdn_cfg_type {
60 MISDN_CTYPE_STR,
61 MISDN_CTYPE_INT,
62 MISDN_CTYPE_BOOL,
63 MISDN_CTYPE_BOOLINT,
64 MISDN_CTYPE_MSNLIST,
65 MISDN_CTYPE_ASTGROUP
68 struct msn_list {
69 char *msn;
70 struct msn_list *next;
73 union misdn_cfg_pt {
74 char *str;
75 int *num;
76 struct msn_list *ml;
77 ast_group_t *grp;
78 void *any;
81 struct misdn_cfg_spec {
82 char name[BUFFERSIZE];
83 enum misdn_cfg_elements elem;
84 enum misdn_cfg_type type;
85 char def[BUFFERSIZE];
86 int boolint_def;
87 char desc[BUFFERSIZE];
91 static const char ports_description[] =
92 "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
94 static const struct misdn_cfg_spec port_spec[] = {
95 { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
96 "Name of the portgroup." },
97 { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
98 "Here you can define which bearers should be allowed." },
99 { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
100 "Set this between -8 and 8 to change the RX Gain." },
101 { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
102 "Set this between -8 and 8 to change the TX Gain." },
103 { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
104 "Some telcos espacially in NL seem to need this set to yes,\n"
105 "\talso in switzerland this seems to be important." },
106 { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
107 "If we should generate ringing for chan_sip and others." },
108 { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE,
109 "This option defines, if chan_misdn should check the L1 on a PMP\n"
110 "\tbefore makeing a group call on it. The L1 may go down for PMP Ports\n"
111 "\tso we might need this.\n"
112 "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
113 "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
114 "\tbecause of a lost Link or because the Provider shut it down..." },
115 { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE ,
116 "Block this port if we have an alarm on it."
117 "default: yes\n" },
118 { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE,
119 "Set this to yes, if you want to bridge a mISDN data channel to\n"
120 "\tanother channel type or to an application." },
121 { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE,
122 "Context to use for incoming calls." },
123 { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE,
124 "Language." },
125 { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE,
126 "Sets the musiconhold class." },
127 { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE,
128 "Sets the caller ID." },
129 { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE,
130 "Sets the method to use for channel selection:\n"
131 "\t standard - always choose the first free channel with the lowest number\n"
132 "\t round_robin - use the round robin algorithm to select a channel. use this\n"
133 "\t if you want to balance your load." },
134 { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE,
135 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
136 "\n"
137 "\tThere are different types of the dialplan:\n"
138 "\n"
139 "\tdialplan -> outgoing Number\n"
140 "\tlocaldialplan -> callerid\n"
141 "\tcpndialplan -> connected party number\n"
142 "\n"
143 "\tdialplan options:\n"
144 "\n"
145 "\t0 - unknown\n"
146 "\t1 - International\n"
147 "\t2 - National\n"
148 "\t4 - Subscriber\n"
149 "\n"
150 "\tThis setting is used for outgoing calls." },
151 { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
152 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
153 "\n"
154 "\tThere are different types of the dialplan:\n"
155 "\n"
156 "\tdialplan -> outgoing Number\n"
157 "\tlocaldialplan -> callerid\n"
158 "\tcpndialplan -> connected party number\n"
159 "\n"
160 "\tdialplan options:\n"
161 "\n"
162 "\t0 - unknown\n"
163 "\t1 - International\n"
164 "\t2 - National\n"
165 "\t4 - Subscriber\n"
166 "\n"
167 "\tThis setting is used for outgoing calls" },
168 { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
169 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
170 "\n"
171 "\tThere are different types of the dialplan:\n"
172 "\n"
173 "\tdialplan -> outgoing Number\n"
174 "\tlocaldialplan -> callerid\n"
175 "\tcpndialplan -> connected party number\n"
176 "\n"
177 "\tdialplan options:\n"
178 "\n"
179 "\t0 - unknown\n"
180 "\t1 - International\n"
181 "\t2 - National\n"
182 "\t4 - Subscriber\n"
183 "\n"
184 "\tThis setting is used for outgoing calls." },
185 { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE,
186 "Prefix for national, this is put before the\n"
187 "\toad if an according dialplan is set by the other end." },
188 { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE,
189 "Prefix for international, this is put before the\n"
190 "\toad if an according dialplan is set by the other end." },
191 { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE,
192 "These (presentation and screen) are the exact isdn screening and presentation\n"
193 "\tindicators.\n"
194 "\tIf -1 is given for both values, the presentation indicators are used from\n"
195 "\tAsterisks SetCallerPres application.\n"
196 "\n"
197 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
198 "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
199 { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE,
200 "These (presentation and screen) are the exact isdn screening and presentation\n"
201 "\tindicators.\n"
202 "\tIf -1 is given for both values, the presentation indicators are used from\n"
203 "\tAsterisks SetCallerPres application.\n"
204 "\n"
205 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
206 "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
207 { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
208 "Enable this to get into the s dialplan-extension.\n"
209 "\tThere you can use DigitTimeout if you can't or don't want to use\n"
210 "\tisdn overlap dial.\n"
211 "\tNOTE: This will jump into the s extension for every exten!" },
212 { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE,
213 "Enable this to prevent chan_misdn to generate the dialtone\n"
214 "\tThis makes only sense together with the always_immediate=yes option\n"
215 "\tto generate your own dialtone with Playtones or so."},
216 { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
217 "Enable this if you want callers which called exactly the base\n"
218 "\tnumber (so no extension is set) to jump into the s extension.\n"
219 "\tIf the user dials something more, it jumps to the correct extension\n"
220 "\tinstead." },
221 { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE,
222 "Enable this if we should produce DTMF Tones ourselves." },
223 { "astdtmf", MISDN_CFG_ASTDTMF, MISDN_CTYPE_BOOL, "no", NONE,
224 "Enable this if you want to use the Asterisk dtmf detector\n"
225 "instead of the mISDN_dsp/hfcmulti one."
227 { "hold_allowed", MISDN_CFG_HOLD_ALLOWED, MISDN_CTYPE_BOOL, "no", NONE,
228 "Enable this to have support for hold and retrieve." },
229 { "early_bconnect", MISDN_CFG_EARLY_BCONNECT, MISDN_CTYPE_BOOL, "yes", NONE,
230 "Disable this if you don't mind correct handling of Progress Indicators." },
231 { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO, MISDN_CTYPE_BOOL, "no", NONE,
232 "Turn this on if you like to send Tone Indications to a Incoming\n"
233 "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n"
234 "\tyou to send indications by yourself, normally the Telco sends the\n"
235 "\tindications to the remote party." },
236 { "echocancel", MISDN_CFG_ECHOCANCEL, MISDN_CTYPE_BOOLINT, "0", 128,
237 "This enables echocancellation, with the given number of taps.\n"
238 "\tBe aware, move this setting only to outgoing portgroups!\n"
239 "\tA value of zero turns echocancellation off.\n"
240 "\n"
241 "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
242 #ifdef MISDN_1_2
243 { "pipeline", MISDN_CFG_PIPELINE, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
244 "Set the configuration string for the mISDN dsp pipeline.\n"
245 "\n"
246 "\tExample for enabling the mg2 echo cancellation module with deftaps\n"
247 "\tset to 128:\n"
248 "\t\tmg2ec(deftaps=128)" },
249 #endif
250 #ifdef WITH_BEROEC
251 { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64,
252 "echotail in ms (1-200)\n"},
253 { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE,
254 "Use antihowl\n"},
255 { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE,
256 "Nonlinear Processing (much faster adaption)"},
257 { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE,
258 "ZeroCoeffeciens\n"},
259 { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE,
260 "Disable Tone\n"},
261 { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE,
262 "Adaption mode (0=no,1=full,2=fast)\n"},
263 #endif
264 { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE,
265 "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
266 "\tthis requests additional Infos, so we can waitfordigits without much\n"
267 "\tissues. This works only for PTP Ports" },
268 { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP, MISDN_CTYPE_BOOL, "0", NONE,
269 "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n"
270 "Instead we directly jump into the dialplan. This might be useful for fast call\n"
271 "rejection, or for some broken switches, that need hangup causes like busy in the.\n"
272 "RELEASE_COMPLETE Message, instead of the DISCONNECT Message.\n"},
273 { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE,
274 "The jitterbuffer." },
275 { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE,
276 "Change this threshold to enable dejitter functionality." },
277 { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
278 "Callgroup." },
279 { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
280 "Pickupgroup." },
281 { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
282 "Defines the maximum amount of incoming calls per port for this group.\n"
283 "\tCalls which exceed the maximum will be marked with the channel varible\n"
284 "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
285 { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
286 "Defines the maximum amount of outgoing calls per port for this group\n"
287 "\texceeding calls will be rejected" },
289 { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
290 "Defines the cause with which a 3. call is rejected on PTMP BRI."},
291 { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
292 "Setup fax detection:\n"
293 "\t no - no fax detection\n"
294 "\t incoming - fax detection for incoming calls\n"
295 "\t outgoing - fax detection for outgoing calls\n"
296 "\t both - fax detection for incoming and outgoing calls\n"
297 "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
298 "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
299 { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
300 "Number of seconds the fax detection should do its job. After the given period of time,\n"
301 "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
302 "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
303 { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
304 "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
305 { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
306 "Watches the layer 1. If the layer 1 is down, it tries to\n"
307 "\tget it up. The timeout is given in seconds. with 0 as value it\n"
308 "\tdoes not watch the l1 at all\n"
309 "\n"
310 "\tThis option is only read at loading time of chan_misdn, which\n"
311 "\tmeans you need to unload and load chan_misdn to change the value,\n"
312 "\tan Asterisk restart should do the trick." },
313 { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
314 "Enables overlap dial for the given amount of seconds.\n"
315 "\tPossible values are positive integers or:\n"
316 "\t yes (= 4 seconds)\n"
317 "\t no (= 0 seconds = disabled)" },
318 { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
319 "Set this to yes if you want calls disconnected in overlap mode"
320 "when a timeout happens.\n"},
321 { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
322 "Set this to yes/no, default is yes.\n"
323 "This can be used to have bridging enabled in general and to\n"
324 "disable it for specific ports. It makes sense to disable\n"
325 "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
326 "features with ISDN phones.\n"
328 { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
329 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
330 "\tindicate the incoming calls to Asterisk.\n"
331 "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
334 static const struct misdn_cfg_spec gen_spec[] = {
335 { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
336 "Sets the debugging flag:\n"
337 "\t0 - No Debug\n"
338 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
339 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
340 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
341 "\t4 - even more Verbose than 3" },
342 #ifndef MISDN_1_2
343 { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
344 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
345 #endif
346 { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
347 "Set the path to the massively growing trace file, if you want that." },
348 { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
349 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
350 { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
351 "Stops dialtone after getting first digit on NT Port." },
352 { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
353 "Wether to append overlapdialed Digits to Extension or not." },
354 { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
355 "Wether to look out for dynamic crypting attempts." },
356 { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
357 "What is used for crypting Protocol." },
358 { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
359 "Keys for cryption, you reference them in the dialplan\n"
360 "\tLater also in dynamic encr." },
361 { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE,
362 "avoid dropping calls if the L2 goes down. some nortel pbx\n"
363 "do put down the L2/L1 for some milliseconds even if there\n"
364 "are running calls. with this option you can avoid dropping them\n" },
365 { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
366 "No description yet."},
367 { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
368 "No description yet." }
372 /* array of port configs, default is at position 0. */
373 static union misdn_cfg_pt **port_cfg;
374 /* max number of available ports, is set on init */
375 static int max_ports;
376 /* general config */
377 static union misdn_cfg_pt *general_cfg;
378 /* storing the ptp flag separated to save memory */
379 static int *ptp;
380 /* maps enum config elements to array positions */
381 static int *map;
383 static ast_mutex_t config_mutex;
385 #define CLI_ERROR(name, value, section) ({ \
386 ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
387 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
390 static int _enum_array_map (void)
392 int i, j, ok;
394 for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
395 if (i == MISDN_CFG_PTP)
396 continue;
397 ok = 0;
398 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
399 if (port_spec[j].elem == i) {
400 map[i] = j;
401 ok = 1;
402 break;
405 if (!ok) {
406 ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
407 return -1;
410 for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
411 ok = 0;
412 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
413 if (gen_spec[j].elem == i) {
414 map[i] = j;
415 ok = 1;
416 break;
419 if (!ok) {
420 ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
421 return -1;
424 return 0;
427 static int get_cfg_position (char *name, int type)
429 int i;
431 switch (type) {
432 case PORT_CFG:
433 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
434 if (!strcasecmp(name, port_spec[i].name))
435 return i;
437 break;
438 case GEN_CFG:
439 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
440 if (!strcasecmp(name, gen_spec[i].name))
441 return i;
445 return -1;
448 static inline void misdn_cfg_lock (void)
450 ast_mutex_lock(&config_mutex);
453 static inline void misdn_cfg_unlock (void)
455 ast_mutex_unlock(&config_mutex);
458 static void _free_msn_list (struct msn_list* iter)
460 if (iter->next)
461 _free_msn_list(iter->next);
462 if (iter->msn)
463 free(iter->msn);
464 free(iter);
467 static void _free_port_cfg (void)
469 int i, j;
470 int gn = map[MISDN_CFG_GROUPNAME];
471 union misdn_cfg_pt* free_list[max_ports + 2];
473 memset(free_list, 0, sizeof(free_list));
474 free_list[0] = port_cfg[0];
475 for (i = 1; i <= max_ports; ++i) {
476 if (port_cfg[i][gn].str) {
477 /* we always have a groupname in the non-default case, so this is fine */
478 for (j = 1; j <= max_ports; ++j) {
479 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
480 break;
481 else if (!free_list[j]) {
482 free_list[j] = port_cfg[i];
483 break;
488 for (j = 0; free_list[j]; ++j) {
489 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
490 if (free_list[j][i].any) {
491 if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
492 _free_msn_list(free_list[j][i].ml);
493 else
494 free(free_list[j][i].any);
500 static void _free_general_cfg (void)
502 int i;
504 for (i = 0; i < NUM_GEN_ELEMENTS; i++)
505 if (general_cfg[i].any)
506 free(general_cfg[i].any);
509 void misdn_cfg_get (int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
511 int place;
513 if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
514 memset(buf, 0, bufsize);
515 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
516 return;
519 misdn_cfg_lock();
520 if (elem == MISDN_CFG_PTP) {
521 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
522 memset(buf, 0, bufsize);
523 } else {
524 if ((place = map[elem]) < 0) {
525 memset (buf, 0, bufsize);
526 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
527 } else {
528 if (elem < MISDN_CFG_LAST) {
529 switch (port_spec[place].type) {
530 case MISDN_CTYPE_STR:
531 if (port_cfg[port][place].str) {
532 if (!memccpy(buf, port_cfg[port][place].str, 0, bufsize))
533 memset(buf, 0, 1);
534 } else if (port_cfg[0][place].str) {
535 if (!memccpy(buf, port_cfg[0][place].str, 0, bufsize))
536 memset(buf, 0, 1);
537 } else
538 memset(buf, 0, bufsize);
539 break;
540 default:
541 if (port_cfg[port][place].any)
542 memcpy(buf, port_cfg[port][place].any, bufsize);
543 else if (port_cfg[0][place].any)
544 memcpy(buf, port_cfg[0][place].any, bufsize);
545 else
546 memset(buf, 0, bufsize);
548 } else {
549 switch (gen_spec[place].type) {
550 case MISDN_CTYPE_STR:
551 if (!general_cfg[place].str || !memccpy(buf, general_cfg[place].str, 0, bufsize))
552 memset(buf, 0, 1);
553 break;
554 default:
555 if (general_cfg[place].any)
556 memcpy(buf, general_cfg[place].any, bufsize);
557 else
558 memset(buf, 0, bufsize);
563 misdn_cfg_unlock();
566 enum misdn_cfg_elements misdn_cfg_get_elem (char *name)
568 int pos;
570 /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
571 if (!strcmp(name, "ports"))
572 return MISDN_CFG_GROUPNAME;
573 if (!strcmp(name, "name"))
574 return MISDN_CFG_FIRST;
576 pos = get_cfg_position (name, PORT_CFG);
577 if (pos >= 0)
578 return port_spec[pos].elem;
580 pos = get_cfg_position (name, GEN_CFG);
581 if (pos >= 0)
582 return gen_spec[pos].elem;
584 return MISDN_CFG_FIRST;
587 void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize)
589 struct misdn_cfg_spec *spec = NULL;
590 int place = map[elem];
592 /* the ptp hack */
593 if (elem == MISDN_CFG_PTP) {
594 memset(buf, 0, 1);
595 return;
598 /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
599 if (elem == MISDN_CFG_GROUPNAME) {
600 if (!snprintf(buf, bufsize, "ports"))
601 memset(buf, 0, 1);
602 return;
605 if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
606 spec = (struct misdn_cfg_spec *)port_spec;
607 else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
608 spec = (struct misdn_cfg_spec *)gen_spec;
610 if (!spec || !memccpy(buf, spec[place].name, 0, bufsize))
611 memset(buf, 0, 1);
614 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
616 int place = map[elem];
617 struct misdn_cfg_spec *spec = NULL;
619 /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
620 if (elem == MISDN_CFG_GROUPNAME) {
621 if (!memccpy(buf, ports_description, 0, bufsize))
622 memset(buf, 0, 1);
623 if (buf_default && bufsize_default)
624 memset(buf_default, 0, 1);
625 return;
628 if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
629 spec = (struct misdn_cfg_spec *)port_spec;
630 else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
631 spec = (struct misdn_cfg_spec *)gen_spec;
633 if (!spec || !spec[place].desc)
634 memset(buf, 0, 1);
635 else {
636 if (!memccpy(buf, spec[place].desc, 0, bufsize))
637 memset(buf, 0, 1);
638 if (buf_default && bufsize) {
639 if (!strcmp(spec[place].def, NO_DEFAULT))
640 memset(buf_default, 0, 1);
641 else if (!memccpy(buf_default, spec[place].def, 0, bufsize_default))
642 memset(buf_default, 0, 1);
647 int misdn_cfg_is_msn_valid (int port, char* msn)
649 int re = 0;
650 struct msn_list *iter;
652 if (!misdn_cfg_is_port_valid(port)) {
653 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
654 return 0;
657 misdn_cfg_lock();
658 if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
659 iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
660 else
661 iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
662 for (; iter; iter = iter->next)
663 if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
664 re = 1;
665 break;
667 misdn_cfg_unlock();
669 return re;
672 int misdn_cfg_is_port_valid (int port)
674 int gn = map[MISDN_CFG_GROUPNAME];
676 return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
679 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
681 int i, re = 0;
682 char *method ;
684 misdn_cfg_lock();
686 method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
688 for (i = 1; i <= max_ports; i++) {
689 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
690 if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
691 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ?
692 port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
696 if (method) {
697 switch (meth) {
698 case METHOD_STANDARD: re = !strcasecmp(method, "standard");
699 break;
700 case METHOD_ROUND_ROBIN: re = !strcasecmp(method, "round_robin");
701 break;
702 case METHOD_STANDARD_DEC: re = !strcasecmp(method, "standard_dec");
703 break;
706 misdn_cfg_unlock();
708 return re;
711 void misdn_cfg_get_ports_string (char *ports)
713 char tmp[16];
714 int l, i;
715 int gn = map[MISDN_CFG_GROUPNAME];
717 *ports = 0;
719 misdn_cfg_lock();
720 for (i = 1; i <= max_ports; i++) {
721 if (port_cfg[i][gn].str) {
722 if (ptp[i])
723 sprintf(tmp, "%dptp,", i);
724 else
725 sprintf(tmp, "%d,", i);
726 strcat(ports, tmp);
729 misdn_cfg_unlock();
731 if ((l = strlen(ports)))
732 ports[l-1] = 0;
735 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
737 int place;
738 char tempbuf[BUFFERSIZE] = "";
739 struct msn_list *iter;
741 if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
742 *buf = 0;
743 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
744 return;
747 place = map[elem];
749 misdn_cfg_lock();
750 if (elem == MISDN_CFG_PTP) {
751 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
753 else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
754 switch (port_spec[place].type) {
755 case MISDN_CTYPE_INT:
756 case MISDN_CTYPE_BOOLINT:
757 if (port_cfg[port][place].num)
758 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
759 else if (port_cfg[0][place].num)
760 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
761 else
762 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
763 break;
764 case MISDN_CTYPE_BOOL:
765 if (port_cfg[port][place].num)
766 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
767 else if (port_cfg[0][place].num)
768 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
769 else
770 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
771 break;
772 case MISDN_CTYPE_ASTGROUP:
773 if (port_cfg[port][place].grp)
774 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
775 ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
776 else if (port_cfg[0][place].grp)
777 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
778 ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
779 else
780 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
781 break;
782 case MISDN_CTYPE_MSNLIST:
783 if (port_cfg[port][place].ml)
784 iter = port_cfg[port][place].ml;
785 else
786 iter = port_cfg[0][place].ml;
787 if (iter) {
788 for (; iter; iter = iter->next)
789 sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
790 tempbuf[strlen(tempbuf)-2] = 0;
792 snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
793 break;
794 case MISDN_CTYPE_STR:
795 if ( port_cfg[port][place].str) {
796 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
797 } else if (port_cfg[0][place].str) {
798 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
799 } else {
800 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
802 break;
804 } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
805 switch (gen_spec[place].type) {
806 case MISDN_CTYPE_INT:
807 case MISDN_CTYPE_BOOLINT:
808 if (general_cfg[place].num)
809 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
810 else
811 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
812 break;
813 case MISDN_CTYPE_BOOL:
814 if (general_cfg[place].num)
815 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
816 else
817 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
818 break;
819 case MISDN_CTYPE_STR:
820 if ( general_cfg[place].str) {
821 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
822 } else {
823 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
825 break;
826 default:
827 snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
828 break;
830 } else {
831 *buf = 0;
832 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
834 misdn_cfg_unlock();
837 int misdn_cfg_get_next_port (int port)
839 int p = -1;
840 int gn = map[MISDN_CFG_GROUPNAME];
842 misdn_cfg_lock();
843 for (port++; port <= max_ports; port++) {
844 if (port_cfg[port][gn].str) {
845 p = port;
846 break;
849 misdn_cfg_unlock();
851 return p;
854 int misdn_cfg_get_next_port_spin (int port)
856 int p = misdn_cfg_get_next_port(port);
857 return (p > 0) ? p : misdn_cfg_get_next_port(0);
860 static int _parse (union misdn_cfg_pt *dest, char *value, enum misdn_cfg_type type, int boolint_def)
862 int re = 0;
863 int len, tmp;
864 char *valtmp;
866 switch (type) {
867 case MISDN_CTYPE_STR:
868 if ((len = strlen(value))) {
869 dest->str = (char *)malloc((len + 1) * sizeof(char));
870 strncpy(dest->str, value, len);
871 dest->str[len] = 0;
872 } else {
873 dest->str = (char *)malloc( sizeof(char));
874 dest->str[0] = 0;
876 break;
877 case MISDN_CTYPE_INT:
879 char *pat;
880 if (strchr(value,'x'))
881 pat="%x";
882 else
883 pat="%d";
884 if (sscanf(value, pat, &tmp)) {
885 dest->num = (int *)malloc(sizeof(int));
886 memcpy(dest->num, &tmp, sizeof(int));
887 } else
888 re = -1;
890 break;
891 case MISDN_CTYPE_BOOL:
892 dest->num = (int *)malloc(sizeof(int));
893 *(dest->num) = (ast_true(value) ? 1 : 0);
894 break;
895 case MISDN_CTYPE_BOOLINT:
896 dest->num = (int *)malloc(sizeof(int));
897 if (sscanf(value, "%d", &tmp)) {
898 memcpy(dest->num, &tmp, sizeof(int));
899 } else {
900 *(dest->num) = (ast_true(value) ? boolint_def : 0);
902 break;
903 case MISDN_CTYPE_MSNLIST:
904 for (valtmp = strsep(&value, ","); valtmp; valtmp = strsep(&value, ",")) {
905 if ((len = strlen(valtmp))) {
906 struct msn_list *ml = (struct msn_list *)malloc(sizeof(struct msn_list));
907 ml->msn = (char *)calloc(len+1, sizeof(char));
908 strncpy(ml->msn, valtmp, len);
909 ml->next = dest->ml;
910 dest->ml = ml;
913 break;
914 case MISDN_CTYPE_ASTGROUP:
915 dest->grp = (ast_group_t *)malloc(sizeof(ast_group_t));
916 *(dest->grp) = ast_get_group(value);
917 break;
920 return re;
923 static void _build_general_config (struct ast_variable *v)
925 int pos;
927 for (; v; v = v->next) {
928 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) ||
929 (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
930 CLI_ERROR(v->name, v->value, "general");
934 static void _build_port_config (struct ast_variable *v, char *cat)
936 int pos, i;
937 union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
938 int cfg_for_ports[max_ports + 1];
940 if (!v || !cat)
941 return;
943 memset(cfg_tmp, 0, sizeof(cfg_tmp));
944 memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
946 if (!strcasecmp(cat, "default")) {
947 cfg_for_ports[0] = 1;
950 if (((pos = get_cfg_position("name", PORT_CFG)) < 0) ||
951 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
952 CLI_ERROR(v->name, v->value, cat);
953 return;
956 for (; v; v = v->next) {
957 if (!strcasecmp(v->name, "ports")) {
958 char *token;
959 char ptpbuf[BUFFERSIZE] = "";
960 int start, end;
961 for (token = strsep(&v->value, ","); token; token = strsep(&v->value, ","), *ptpbuf = 0) {
962 if (!*token)
963 continue;
964 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
965 for (; start <= end; start++) {
966 if (start <= max_ports && start > 0) {
967 cfg_for_ports[start] = 1;
968 ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
969 } else
970 CLI_ERROR(v->name, v->value, cat);
972 } else {
973 if (sscanf(token, "%d%s", &start, ptpbuf)) {
974 if (start <= max_ports && start > 0) {
975 cfg_for_ports[start] = 1;
976 ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
977 } else
978 CLI_ERROR(v->name, v->value, cat);
979 } else
980 CLI_ERROR(v->name, v->value, cat);
983 } else {
984 if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) ||
985 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
986 CLI_ERROR(v->name, v->value, cat);
990 for (i = 0; i < (max_ports + 1); ++i) {
991 if (cfg_for_ports[i]) {
992 memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
997 void misdn_cfg_update_ptp (void)
999 #ifndef MISDN_1_2
1000 char misdn_init[BUFFERSIZE];
1001 char line[BUFFERSIZE];
1002 FILE *fp;
1003 char *tok, *p, *end;
1004 int port;
1006 misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
1008 if (!ast_strlen_zero(misdn_init)) {
1009 fp = fopen(misdn_init, "r");
1010 if (fp) {
1011 while(fgets(line, sizeof(line), fp)) {
1012 if (!strncmp(line, "nt_ptp", 6)) {
1013 for (tok = strtok_r(line,",=", &p);
1014 tok;
1015 tok = strtok_r(NULL,",=", &p)) {
1016 port = strtol(tok, &end, 10);
1017 if (end != tok && misdn_cfg_is_port_valid(port)) {
1018 misdn_cfg_lock();
1019 ptp[port] = 1;
1020 misdn_cfg_unlock();
1025 fclose(fp);
1026 } else {
1027 ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
1030 #else
1031 int i;
1032 int proto;
1033 char filename[128];
1034 FILE *fp;
1036 for (i = 1; i <= max_ports; ++i) {
1037 snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
1038 fp = fopen(filename, "r");
1039 if (!fp) {
1040 ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
1041 continue;
1043 if (fscanf(fp, "0x%08x", &proto) != 1)
1044 ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
1045 else
1046 ptp[i] = proto & 1<<5 ? 1 : 0;
1047 fclose(fp);
1049 #endif
1052 static void _fill_defaults (void)
1054 int i;
1056 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1057 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1058 _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1060 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1061 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1062 _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1066 void misdn_cfg_reload (void)
1068 misdn_cfg_init (0);
1071 void misdn_cfg_destroy (void)
1073 misdn_cfg_lock();
1075 _free_port_cfg();
1076 _free_general_cfg();
1078 free(port_cfg);
1079 free(general_cfg);
1080 free(ptp);
1081 free(map);
1083 misdn_cfg_unlock();
1084 ast_mutex_destroy(&config_mutex);
1087 int misdn_cfg_init (int this_max_ports)
1089 char config[] = "misdn.conf";
1090 char *cat, *p;
1091 int i;
1092 struct ast_config *cfg;
1093 struct ast_variable *v;
1095 if (!(cfg = AST_LOAD_CFG(config))) {
1096 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1097 return -1;
1100 ast_mutex_init(&config_mutex);
1102 misdn_cfg_lock();
1104 if (this_max_ports) {
1105 /* this is the first run */
1106 max_ports = this_max_ports;
1107 map = (int *)calloc(MISDN_GEN_LAST + 1, sizeof(int));
1108 if (_enum_array_map())
1109 return -1;
1110 p = (char *)calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1111 + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1112 port_cfg = (union misdn_cfg_pt **)p;
1113 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1114 for (i = 0; i <= max_ports; ++i) {
1115 port_cfg[i] = (union misdn_cfg_pt *)p;
1116 p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1118 general_cfg = (union misdn_cfg_pt *)calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1119 ptp = (int *)calloc(max_ports + 1, sizeof(int));
1121 else {
1122 /* misdn reload */
1123 _free_port_cfg();
1124 _free_general_cfg();
1125 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1126 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1127 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1130 cat = ast_category_browse(cfg, NULL);
1132 while(cat) {
1133 v = ast_variable_browse(cfg, cat);
1134 if (!strcasecmp(cat, "general")) {
1135 _build_general_config(v);
1136 } else {
1137 _build_port_config(v, cat);
1139 cat = ast_category_browse(cfg, cat);
1142 _fill_defaults();
1144 misdn_cfg_unlock();
1145 AST_DESTROY_CFG(cfg);
1147 return 0;