2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.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.
21 * \brief Core PBX routines.
23 * \author Mark Spencer <markster@digium.com>
26 #include <sys/types.h>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include "asterisk/lock.h"
41 #include "asterisk/cli.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/options.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/file.h"
47 #include "asterisk/callerid.h"
48 #include "asterisk/cdr.h"
49 #include "asterisk/config.h"
50 #include "asterisk/term.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/ast_expr.h"
53 #include "asterisk/linkedlists.h"
54 #include "asterisk/say.h"
55 #include "asterisk/utils.h"
56 #include "asterisk/causes.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/app.h"
59 #include "asterisk/devicestate.h"
60 #include "asterisk/compat.h"
63 * \note I M P O R T A N T :
65 * The speed of extension handling will likely be among the most important
66 * aspects of this PBX. The switching scheme as it exists right now isn't
67 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
68 * of priorities, but a constant search time here would be great ;-)
73 #define EXT_DATA_SIZE 256
75 #define EXT_DATA_SIZE 8192
78 #define SWITCH_DATA_LENGTH 256
80 #define VAR_BUF_SIZE 4096
83 #define VAR_SOFTTRAN 2
84 #define VAR_HARDTRAN 3
86 #define BACKGROUND_SKIP (1 << 0)
87 #define BACKGROUND_NOANSWER (1 << 1)
88 #define BACKGROUND_MATCHEXTEN (1 << 2)
89 #define BACKGROUND_PLAYBACK (1 << 3)
91 AST_APP_OPTIONS(background_opts, {
92 AST_APP_OPTION('s', BACKGROUND_SKIP),
93 AST_APP_OPTION('n', BACKGROUND_NOANSWER),
94 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
95 AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
98 #define WAITEXTEN_MOH (1 << 0)
100 AST_APP_OPTIONS(waitexten_opts, {
101 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 1),
107 \brief ast_exten: An extension
108 The dialplan is saved as a linked list with each context
109 having it's own linked list of extensions - one item per
113 char *exten; /*!< Extension name */
114 int matchcid; /*!< Match caller id ? */
115 char *cidmatch; /*!< Caller id to match for this extension */
116 int priority; /*!< Priority */
117 char *label; /*!< Label */
118 struct ast_context *parent; /*!< The context this extension belongs to */
119 char *app; /*!< Application to execute */
120 void *data; /*!< Data to use (arguments) */
121 void (*datad)(void *); /*!< Data destructor */
122 struct ast_exten *peer; /*!< Next higher priority with our extension */
123 const char *registrar; /*!< Registrar */
124 struct ast_exten *next; /*!< Extension with a greater ID */
128 /*! \brief ast_include: include= support in extensions.conf */
131 char *rname; /*!< Context to include */
132 const char *registrar; /*!< Registrar */
133 int hastime; /*!< If time construct exists */
134 struct ast_timing timing; /*!< time construct */
135 struct ast_include *next; /*!< Link them together */
139 /*! \brief ast_sw: Switch statement in extensions.conf */
142 const char *registrar; /*!< Registrar */
143 char *data; /*!< Data load */
145 struct ast_sw *next; /*!< Link them together */
150 /*! \brief ast_ignorepat: Ignore patterns in dial plan */
151 struct ast_ignorepat {
152 const char *registrar;
153 struct ast_ignorepat *next;
157 /*! \brief ast_context: An extension context */
159 ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
160 struct ast_exten *root; /*!< The root of the list of extensions */
161 struct ast_context *next; /*!< Link them together */
162 struct ast_include *includes; /*!< Include other contexts */
163 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
164 const char *registrar; /*!< Registrar */
165 struct ast_sw *alts; /*!< Alternative switches */
166 char name[0]; /*!< Name of the context */
170 /*! \brief ast_app: A registered application */
172 int (*execute)(struct ast_channel *chan, void *data);
173 const char *synopsis; /*!< Synopsis text for 'show applications' */
174 const char *description; /*!< Description (help text) for 'show application <name>' */
175 struct ast_app *next; /*!< Next app in list */
176 char name[0]; /*!< Name of the application */
179 /*! \brief ast_state_cb: An extension state notify register item */
180 struct ast_state_cb {
183 ast_state_cb_type callback;
184 struct ast_state_cb *next;
187 /*! \brief Structure for dial plan hints
189 Hints are pointers from an extension in the dialplan to one or
190 more devices (tech/name) */
192 struct ast_exten *exten; /*!< Extension */
193 int laststate; /*!< Last known state */
194 struct ast_state_cb *callbacks; /*!< Callback list for this extension */
195 AST_LIST_ENTRY(ast_hint) list; /*!< Pointer to next hint in list */
198 int ast_pbx_outgoing_cdr_failed(void);
200 static int pbx_builtin_answer(struct ast_channel *, void *);
201 static int pbx_builtin_goto(struct ast_channel *, void *);
202 static int pbx_builtin_hangup(struct ast_channel *, void *);
203 static int pbx_builtin_background(struct ast_channel *, void *);
204 static int pbx_builtin_wait(struct ast_channel *, void *);
205 static int pbx_builtin_waitexten(struct ast_channel *, void *);
206 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
207 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
208 static int pbx_builtin_ringing(struct ast_channel *, void *);
209 static int pbx_builtin_progress(struct ast_channel *, void *);
210 static int pbx_builtin_congestion(struct ast_channel *, void *);
211 static int pbx_builtin_busy(struct ast_channel *, void *);
212 static int pbx_builtin_setglobalvar(struct ast_channel *, void *);
213 static int pbx_builtin_noop(struct ast_channel *, void *);
214 static int pbx_builtin_gotoif(struct ast_channel *, void *);
215 static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
216 static int pbx_builtin_execiftime(struct ast_channel *, void *);
217 static int pbx_builtin_saynumber(struct ast_channel *, void *);
218 static int pbx_builtin_saydigits(struct ast_channel *, void *);
219 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
220 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
221 int pbx_builtin_setvar(struct ast_channel *, void *);
222 static int pbx_builtin_importvar(struct ast_channel *, void *);
224 static struct varshead globals;
226 static int autofallthrough = 0;
228 AST_MUTEX_DEFINE_STATIC(maxcalllock);
229 static int countcalls = 0;
231 AST_MUTEX_DEFINE_STATIC(acflock); /*!< Lock for the custom function list */
232 static struct ast_custom_function *acf_root = NULL;
234 /*! \brief Declaration of builtin applications */
235 static struct pbx_builtin {
236 char name[AST_MAX_APP];
237 int (*execute)(struct ast_channel *chan, void *data);
242 /* These applications are built into the PBX core and do not
243 need separate modules */
245 { "Answer", pbx_builtin_answer,
246 "Answer a channel if ringing",
247 " Answer([delay]): If the call has not been answered, this application will\n"
248 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
249 "Asterisk will wait this number of milliseconds before answering the call.\n"
252 { "BackGround", pbx_builtin_background,
253 "Play a file while awaiting extension",
254 " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n"
255 "This application will play the given list of files while waiting for an\n"
256 "extension to be dialed by the calling channel. To continue waiting for digits\n"
257 "after this application has finished playing files, the WaitExten application\n"
258 "should be used. The 'langoverride' option explicity specifies which language\n"
259 "to attempt to use for the requested sound files. If a 'context' is specified,\n"
260 "this is the dialplan context that this application will use when exiting to a\n"
262 " If one of the requested sound files does not exist, call processing will be\n"
265 " s - causes the playback of the message to be skipped\n"
266 " if the channel is not in the 'up' state (i.e. it\n"
267 " hasn't been answered yet.) If this happens, the\n"
268 " application will return immediately.\n"
269 " n - don't answer the channel before playing the files\n"
270 " m - only break if a digit hit matches a one digit\n"
271 " extension in the destination context\n"
274 { "Busy", pbx_builtin_busy,
275 "Indicate the Busy condition",
276 " Busy([timeout]): This application will indicate the busy condition to\n"
277 "the calling channel. If the optional timeout is specified, the calling channel\n"
278 "will be hung up after the specified number of seconds. Otherwise, this\n"
279 "application will wait until the calling channel hangs up.\n"
282 { "Congestion", pbx_builtin_congestion,
283 "Indicate the Congestion condition",
284 " Congestion([timeout]): This application will indicate the congenstion\n"
285 "condition to the calling channel. If the optional timeout is specified, the\n"
286 "calling channel will be hung up after the specified number of seconds.\n"
287 "Otherwise, this application will wait until the calling channel hangs up.\n"
290 { "Goto", pbx_builtin_goto,
291 "Jump to a particular priority, extension, or context",
292 " Goto([[context|]extension|]priority): This application will cause the\n"
293 "calling channel to continue dialplan execution at the specified priority.\n"
294 "If no specific extension, or extension and context, are specified, then this\n"
295 "application will jump to the specified priority of the current extension.\n"
296 " If the attempt to jump to another location in the dialplan is not successful,\n"
297 "then the channel will continue at the next priority of the current extension.\n"
300 { "GotoIf", pbx_builtin_gotoif,
302 " GotoIf(Condition?[label1]:[label2]): This application will cause the calling\n"
303 "channel to jump to the speicifed location in the dialplan based on the\n"
304 "evaluation of the given condition. The channel will continue at 'label1' if the\n"
305 "condition is true, or 'label2' if the condition is false. The labels are\n"
306 "specified in the same syntax that is used with the Goto application.\n"
309 { "GotoIfTime", pbx_builtin_gotoiftime,
310 "Conditional Goto based on the current time",
311 " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n"
312 "This application will have the calling channel jump to the speicified location\n"
313 "int the dialplan if the current time matches the given time specification.\n"
314 "Further information on the time specification can be found in examples\n"
315 "illustrating how to do time-based context includes in the dialplan.\n"
318 { "ExecIfTime", pbx_builtin_execiftime,
319 "Conditional application execution based on the current time",
320 " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n"
321 "This application will execute the specified dialplan application, with optional\n"
322 "arguments, if the current time matches the given time specification. Further\n"
323 "information on the time speicification can be found in examples illustrating\n"
324 "how to do time-based context includes in the dialplan.\n"
327 { "Hangup", pbx_builtin_hangup,
328 "Hang up the calling channel",
329 " Hangup(): This application will hang up the calling channel.\n"
332 { "NoOp", pbx_builtin_noop,
334 " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n"
335 "purposes. Any text that is provided as arguments to this application can be\n"
336 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
337 "variables or functions without having any effect."
340 { "Progress", pbx_builtin_progress,
342 " Progress(): This application will request that in-band progress information\n"
343 "be provided to the calling channel.\n"
346 { "ResetCDR", pbx_builtin_resetcdr,
347 "Resets the Call Data Record",
348 " ResetCDR([options]): This application causes the Call Data Record to be\n"
351 " w -- Store the current CDR record before resetting it.\n"
352 " a -- Store any stacked records.\n"
353 " v -- Save CDR variables.\n"
356 { "Ringing", pbx_builtin_ringing,
357 "Indicate ringing tone",
358 " Ringing(): This application will request that the channel indicate a ringing\n"
359 "tone to the user.\n"
362 { "SayNumber", pbx_builtin_saynumber,
364 " SayNumber(digits[,gender]): This application will play the sounds that\n"
365 "correspond to the given number. Optionally, a gender may be specified.\n"
366 "This will use the language that is currently set for the channel. See the\n"
367 "LANGUAGE function for more information on setting the language for the channel.\n"
370 { "SayDigits", pbx_builtin_saydigits,
372 " SayDigits(digits): This application will play the sounds that correspond\n"
373 "to the digits of the given number. This will use the language that is currently\n"
374 "set for the channel. See the LANGUAGE function for more information on setting\n"
375 "the language for the channel.\n"
378 { "SayAlpha", pbx_builtin_saycharacters,
380 " SayAlpha(string): This application will play the sounds that correspond to\n"
381 "the letters of the given string.\n"
384 { "SayPhonetic", pbx_builtin_sayphonetic,
386 " SayPhonetic(string): This application will play the sounds from the phonetic\n"
387 "alphabet that correspond to the letters in the given string.\n"
390 { "SetAMAFlags", pbx_builtin_setamaflags,
392 " SetAMAFlags([flag]): This channel will set the channel's AMA Flags for billing\n"
396 { "SetGlobalVar", pbx_builtin_setglobalvar,
397 "Set a global variable to a given value",
398 " SetGlobalVar(variable=value): This application sets a given global variable to\n"
399 "the specified value.\n"
402 { "Set", pbx_builtin_setvar,
403 "Set channel variable(s) or function value(s)",
404 " Set(name1=value1|name2=value2|..[|options])\n"
405 "This function can be used to set the value of channel variables or dialplan\n"
406 "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
407 "if the variable name is prefixed with _, the variable will be inherited into\n"
408 "channels created from the current channel. If the variable name is prefixed\n"
409 "with __, the variable will be inherited into channels created from the current\n"
410 "channel and all children channels.\n"
412 " g - Set variable globally instead of on the channel\n"
413 " (applies only to variables, not functions)\n"
416 { "ImportVar", pbx_builtin_importvar,
417 "Import a variable from a channel into a new variable",
418 " ImportVar(newvar=channelname|variable): This application imports a variable\n"
419 "from the specified channel (as opposed to the current one) and stores it as\n"
420 "a variable in the current channel (the channel that is calling this\n"
421 "application). Variables created by this application have the same inheritance\n"
422 "properties as those created with the Set application. See the documentation for\n"
423 "Set for more information.\n"
426 { "Wait", pbx_builtin_wait,
427 "Waits for some time",
428 " Wait(seconds): This application waits for a specified number of seconds.\n"
429 "Then, dialplan execution will continue at the next priority.\n"
430 " Note that the seconds can be passed with fractions of a second. For example,\n"
431 "'1.5' will ask the application to wait for 1.5 seconds.\n"
434 { "WaitExten", pbx_builtin_waitexten,
435 "Waits for an extension to be entered",
436 " WaitExten([seconds][|options]): This application waits for the user to enter\n"
437 "a new extension for a specified number of seconds.\n"
438 " Note that the seconds can be passed with fractions of a second. For example,\n"
439 "'1.5' will ask the application to wait for 1.5 seconds.\n"
441 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
442 " Optionally, specify the class for music on hold within parenthesis.\n"
447 static struct ast_context *contexts = NULL;
448 AST_MUTEX_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
449 static struct ast_app *apps = NULL;
450 AST_MUTEX_DEFINE_STATIC(applock); /*!< Lock for the application list */
452 struct ast_switch *switches = NULL;
453 AST_MUTEX_DEFINE_STATIC(switchlock); /*!< Lock for switches */
455 static int stateid = 1;
456 static AST_LIST_HEAD_STATIC(hints, ast_hint);
457 struct ast_state_cb *statecbs = NULL;
460 \note This function is special. It saves the stack so that no matter
461 how many times it is called, it returns to the same place */
462 int pbx_exec(struct ast_channel *c, /*!< Channel */
463 struct ast_app *app, /*!< Application */
464 void *data, /*!< Data for execution */
465 int newstack) /*!< Force stack increment */
472 int (*execute)(struct ast_channel *chan, void *data) = app->execute;
476 ast_cdr_setapp(c->cdr, app->name, data);
478 /* save channel values */
479 saved_c_appl= c->appl;
480 saved_c_data= c->data;
484 res = execute(c, data);
485 /* restore channel values */
486 c->appl= saved_c_appl;
487 c->data= saved_c_data;
490 ast_log(LOG_WARNING, "You really didn't want to call this function with newstack set to 0\n");
495 /*! Go no deeper than this through includes (not counting loops) */
496 #define AST_PBX_MAX_STACK 128
498 #define HELPER_EXISTS 0
499 #define HELPER_SPAWN 1
500 #define HELPER_EXEC 2
501 #define HELPER_CANMATCH 3
502 #define HELPER_MATCHMORE 4
503 #define HELPER_FINDLABEL 5
505 /*! \brief Find application handle in linked list
507 struct ast_app *pbx_findapp(const char *app)
511 if (ast_mutex_lock(&applock)) {
512 ast_log(LOG_WARNING, "Unable to obtain application lock\n");
515 for (tmp = apps; tmp; tmp = tmp->next) {
516 if (!strcasecmp(tmp->name, app))
519 ast_mutex_unlock(&applock);
523 static struct ast_switch *pbx_findswitch(const char *sw)
525 struct ast_switch *asw;
527 if (ast_mutex_lock(&switchlock)) {
528 ast_log(LOG_WARNING, "Unable to obtain application lock\n");
531 for (asw = switches; asw; asw = asw->next) {
532 if (!strcasecmp(asw->name, sw))
535 ast_mutex_unlock(&switchlock);
539 static inline int include_valid(struct ast_include *i)
544 return ast_check_timing(&(i->timing));
547 static void pbx_destroy(struct ast_pbx *p)
552 #define EXTENSION_MATCH_CORE(data,pattern,match) {\
553 /* All patterns begin with _ */\
554 if (pattern[0] != '_') \
556 /* Start optimistic */\
559 while(match && *data && *pattern && (*pattern != '/')) {\
560 while (*data == '-' && (*(data+1) != '\0')) data++;\
561 switch(toupper(*pattern)) {\
568 where=strchr(pattern,']');\
570 border=(int)(where-pattern);\
571 if (!where || border > strlen(pattern)) {\
572 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");\
575 for (i=0; i<border; i++) {\
578 if (pattern[i+1]=='-') {\
579 if (*data >= pattern[i] && *data <= pattern[i+2]) {\
586 if (res==1 || *data==pattern[i]) {\
595 if ((*data < '2') || (*data > '9'))\
599 if ((*data < '0') || (*data > '9'))\
603 if ((*data < '1') || (*data > '9'))\
614 /* Ignore these characters */\
618 if (*data != *pattern)\
624 /* If we ran off the end of the data and the pattern ends in '!', match */\
625 if (match && !*data && (*pattern == '!'))\
629 int ast_extension_match(const char *pattern, const char *data)
632 /* If they're the same return */
633 if (!strcmp(pattern, data))
635 EXTENSION_MATCH_CORE(data,pattern,match);
636 /* Must be at the end of both */
637 if (*data || (*pattern && (*pattern != '/')))
642 int ast_extension_close(const char *pattern, const char *data, int needmore)
645 /* If "data" is longer, it can'be a subset of pattern unless
646 pattern is a pattern match */
647 if ((strlen(pattern) < strlen(data)) && (pattern[0] != '_'))
650 if ((ast_strlen_zero((char *)data) || !strncasecmp(pattern, data, strlen(data))) &&
651 (!needmore || (strlen(pattern) > strlen(data)))) {
654 EXTENSION_MATCH_CORE(data,pattern,match);
655 /* If there's more or we don't care about more, or if it's a possible early match,
656 return non-zero; otherwise it's a miss */
657 if (!needmore || *pattern || match == 2) {
663 struct ast_context *ast_context_find(const char *name)
665 struct ast_context *tmp;
666 ast_mutex_lock(&conlock);
668 for (tmp = contexts; tmp; tmp = tmp->next) {
669 if (!strcasecmp(name, tmp->name))
674 ast_mutex_unlock(&conlock);
678 #define STATUS_NO_CONTEXT 1
679 #define STATUS_NO_EXTENSION 2
680 #define STATUS_NO_PRIORITY 3
681 #define STATUS_NO_LABEL 4
682 #define STATUS_SUCCESS 5
684 static int matchcid(const char *cidpattern, const char *callerid)
688 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
689 failing to get a number should count as a match, otherwise not */
691 if (!ast_strlen_zero(cidpattern))
699 return ast_extension_match(cidpattern, callerid);
702 static struct ast_exten *pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, const char *context, const char *exten, int priority, const char *label, const char *callerid, int action, char *incstack[], int *stacklen, int *status, struct ast_switch **swo, char **data, const char **foundcontext)
705 struct ast_context *tmp;
706 struct ast_exten *e, *eroot;
707 struct ast_include *i;
709 struct ast_switch *asw;
711 /* Initialize status if appropriate */
713 *status = STATUS_NO_CONTEXT;
717 /* Check for stack overflow */
718 if (*stacklen >= AST_PBX_MAX_STACK) {
719 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
722 /* Check first to see if we've already been checked */
723 for (x = 0; x < *stacklen; x++) {
724 if (!strcasecmp(incstack[x], context))
731 for (; tmp; tmp = tmp->next) {
733 if (bypass || !strcmp(tmp->name, context)) {
734 struct ast_exten *earlymatch = NULL;
736 if (*status < STATUS_NO_EXTENSION)
737 *status = STATUS_NO_EXTENSION;
738 for (eroot = tmp->root; eroot; eroot = eroot->next) {
740 /* Match extension */
741 if ((((action != HELPER_MATCHMORE) && ast_extension_match(eroot->exten, exten)) ||
742 ((action == HELPER_CANMATCH) && (ast_extension_close(eroot->exten, exten, 0))) ||
743 ((action == HELPER_MATCHMORE) && (match = ast_extension_close(eroot->exten, exten, 1)))) &&
744 (!eroot->matchcid || matchcid(eroot->cidmatch, callerid))) {
746 if (action == HELPER_MATCHMORE && match == 2 && !earlymatch) {
747 /* It matched an extension ending in a '!' wildcard
748 So ignore it for now, unless there's a better match */
751 if (*status < STATUS_NO_PRIORITY)
752 *status = STATUS_NO_PRIORITY;
753 for (e = eroot; e; e = e->peer) {
755 if (action == HELPER_FINDLABEL) {
756 if (*status < STATUS_NO_LABEL)
757 *status = STATUS_NO_LABEL;
758 if (label && e->label && !strcmp(label, e->label)) {
759 *status = STATUS_SUCCESS;
760 *foundcontext = context;
763 } else if (e->priority == priority) {
764 *status = STATUS_SUCCESS;
765 *foundcontext = context;
773 /* Bizarre logic for HELPER_MATCHMORE. We return zero to break out
774 of the loop waiting for more digits, and _then_ match (normally)
775 the extension we ended up with. We got an early-matching wildcard
776 pattern, so return NULL to break out of the loop. */
779 /* Check alternative switches */
780 for (sw = tmp->alts; sw; sw = sw->next) {
781 if ((asw = pbx_findswitch(sw->name))) {
782 /* Substitute variables now */
784 pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1);
785 if (action == HELPER_CANMATCH)
786 res = asw->canmatch ? asw->canmatch(chan, context, exten, priority, callerid, sw->eval ? sw->tmpdata : sw->data) : 0;
787 else if (action == HELPER_MATCHMORE)
788 res = asw->matchmore ? asw->matchmore(chan, context, exten, priority, callerid, sw->eval ? sw->tmpdata : sw->data) : 0;
790 res = asw->exists ? asw->exists(chan, context, exten, priority, callerid, sw->eval ? sw->tmpdata : sw->data) : 0;
794 *data = sw->eval ? sw->tmpdata : sw->data;
795 *foundcontext = context;
799 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
802 /* Setup the stack */
803 incstack[*stacklen] = tmp->name;
805 /* Now try any includes we have in this context */
806 for (i = tmp->includes; i; i = i->next) {
807 if (include_valid(i)) {
808 if ((e = pbx_find_extension(chan, bypass, i->rname, exten, priority, label, callerid, action, incstack, stacklen, status, swo, data, foundcontext)))
820 /* Note that it's negative -- that's important later. */
821 #define DONT_HAVE_LENGTH 0x80000000
823 /*! \brief extract offset:length from variable name.
824 * Returns 1 if there is a offset:length part, which is
825 * trimmed off (values go into variables)
827 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
832 *length = DONT_HAVE_LENGTH;
834 for (; *var; var++) {
838 } else if (*var == ')') {
840 } else if (*var == ':' && parens == 0) {
842 sscanf(var, "%d:%d", offset, length);
843 return 1; /* offset:length valid */
849 /*! \brief takes a substring. It is ok to call with value == workspace. */
850 static char *substring(char *value, int offset, int length, char *workspace, size_t workspace_len)
852 char *ret = workspace;
854 /* No need to do anything */
855 if (offset == 0 && length==-1) {
859 ast_copy_string(workspace, value, workspace_len);
861 if (abs(offset) > strlen(ret)) { /* Offset beyond string */
863 offset = strlen(ret);
865 offset =- strlen(ret);
868 /* Detect too-long length */
869 if ((offset < 0 && length > -offset) || (offset >= 0 && offset+length > strlen(ret))) {
871 length = strlen(ret)-offset;
873 length = strlen(ret)+offset;
876 /* Bounce up to the right offset */
880 ret += strlen(ret)+offset;
882 /* Chop off at the requisite length */
889 /*! \brief pbx_retrieve_variable: Support for Asterisk built-in variables and
890 functions in the dialplan
892 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
894 const char not_found = '\0';
895 char tmpvar[80], *deprecated = NULL;
896 const char *s; /* the result */
898 int i, need_substring;
899 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
902 places[0] = &c->varshead;
905 * Make a copy of var because parse_variable_name() modifies the string.
906 * Then if called directly, we might need to run substring() on the result;
907 * remember this for later in 'need_substring', 'offset' and 'length'
909 ast_copy_string(tmpvar, var, sizeof(tmpvar)); /* parse_variable_name modifies the string */
910 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
913 * Look first into predefined variables, then into variable lists.
914 * s == ¬_found (set at the beginning) means that we did not find a
915 * matching variable and need to look into more places.
916 * If s != ¬_found, s is a valid result string as follows:
917 * s = NULL if the variable does not have a value;
918 * s = workspace if the result has been assembled there;
919 * s != workspace in case we have a string, that needs to be copied
920 * (the ast_copy_string is done once for all at the end).
921 * Deprecated variables have the replacement indicated in 'deprecated'.
923 s = ¬_found; /* default value */
924 if (c) { /* This group requires a valid channel */
925 /* Names with common parts are looked up a piece at a time using strncmp. */
926 if (!strncmp(var, "CALL", 4)) {
927 if (!strncmp(var + 4, "ER", 2)) {
928 if (!strncmp(var + 6, "ID", 2)) {
929 if (!var[8]) { /* CALLERID */
930 if (c->cid.cid_num) {
931 if (c->cid.cid_name) {
932 snprintf(workspace, workspacelen, "\"%s\" <%s>",
933 c->cid.cid_name, c->cid.cid_num);
939 s = c->cid.cid_name; /* possibly empty */
940 deprecated = "CALLERID(all)";
941 } else if (!strcmp(var + 8, "NUM")) { /* CALLERIDNUM */
943 deprecated = "CALLERID(num)";
944 } else if (!strcmp(var + 8, "NAME")) { /* CALLERIDNAME */
946 deprecated = "CALLERID(name)";
948 } else if (!strcmp(var + 6, "ANI")) { /* CALLERANI */
950 deprecated = "CALLERID(ANI)";
952 } else if (!strncmp(var + 4, "ING", 3)) {
953 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
954 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
956 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
957 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
959 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
960 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
962 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
963 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
967 } else if (!strcmp(var, "DNID")) {
969 deprecated = "CALLERID(DNID)";
970 } else if (!strcmp(var, "HINT")) {
971 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
972 } else if (!strcmp(var, "HINTNAME")) {
973 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
974 } else if (!strcmp(var, "EXTEN")) {
976 } else if (!strcmp(var, "RDNIS")) {
977 s = c->cid.cid_rdnis;
978 deprecated = "CALLERID(RDNIS)";
979 } else if (!strcmp(var, "CONTEXT")) {
981 } else if (!strcmp(var, "PRIORITY")) {
982 snprintf(workspace, workspacelen, "%d", c->priority);
984 } else if (!strcmp(var, "CHANNEL")) {
986 } else if (!strcmp(var, "UNIQUEID")) {
988 } else if (!strcmp(var, "HANGUPCAUSE")) {
989 snprintf(workspace, workspacelen, "%d", c->hangupcause);
991 } else if (!strcmp(var, "ACCOUNTCODE")) {
993 deprecated = "CDR(accountcode)";
994 } else if (!strcmp(var, "LANGUAGE")) {
996 deprecated = "LANGUAGE()";
999 if (s == ¬_found) { /* look for more */
1001 struct tm brokentime;
1003 if (!strcmp(var, "EPOCH")) {
1004 snprintf(workspace, workspacelen, "%u",(int)time(NULL));
1006 } else if (!strcmp(var, "DATETIME")) {
1007 thistime=time(NULL);
1008 localtime_r(&thistime, &brokentime);
1009 snprintf(workspace, workspacelen, "%02d%02d%04d-%02d:%02d:%02d",
1011 brokentime.tm_mon+1,
1012 brokentime.tm_year+1900,
1018 deprecated = "STRFTIME(${EPOCH},,\%d\%m\%Y-\%H:\%M:\%S)";
1019 } else if (!strcmp(var, "TIMESTAMP")) {
1020 thistime=time(NULL);
1021 localtime_r(&thistime, &brokentime);
1022 /* 20031130-150612 */
1023 snprintf(workspace, workspacelen, "%04d%02d%02d-%02d%02d%02d",
1024 brokentime.tm_year+1900,
1025 brokentime.tm_mon+1,
1032 deprecated = "STRFTIME(${EPOCH},,\%Y\%m\%d-\%H\%M\%S)";
1035 /* if not found, look into chanvars or global vars */
1036 for (i = 0; s == ¬_found && i < (sizeof(places) / sizeof(places[0])); i++) {
1037 struct ast_var_t *variables;
1040 AST_LIST_TRAVERSE(places[i], variables, entries) {
1041 if (strcasecmp(ast_var_name(variables), var)==0) {
1042 s = ast_var_value(variables);
1047 if (s == ¬_found || s == NULL)
1051 ast_copy_string(workspace, s, workspacelen);
1054 *ret = substring(*ret, offset, length, workspace, workspacelen);
1058 ast_log(LOG_WARNING, "${%s} is deprecated. Please use ${%s} instead.\n", var, deprecated);
1061 /*! \brief CLI function to show installed custom functions
1062 \addtogroup CLI_functions
1064 static int handle_show_functions(int fd, int argc, char *argv[])
1066 struct ast_custom_function *acf;
1071 if (argc == 4 && (!strcmp(argv[2], "like")) ) {
1073 } else if (argc != 2) {
1074 return RESULT_SHOWUSAGE;
1077 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1079 for (acf = acf_root ; acf; acf = acf->next) {
1082 if (strstr(acf->name, argv[3])) {
1092 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1096 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1101 static int handle_show_function(int fd, int argc, char *argv[])
1103 struct ast_custom_function *acf;
1104 /* Maximum number of characters added by terminal coloring is 22 */
1105 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1106 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1107 char stxtitle[40], *syntax = NULL;
1108 int synopsis_size, description_size, syntax_size;
1110 if (argc < 3) return RESULT_SHOWUSAGE;
1112 if (!(acf = ast_custom_function_find(argv[2]))) {
1113 ast_cli(fd, "No function by that name registered.\n");
1114 return RESULT_FAILURE;
1119 synopsis_size = strlen(acf->synopsis) + 23;
1121 synopsis_size = strlen("Not available") + 23;
1122 synopsis = alloca(synopsis_size);
1125 description_size = strlen(acf->desc) + 23;
1127 description_size = strlen("Not available") + 23;
1128 description = alloca(description_size);
1131 syntax_size = strlen(acf->syntax) + 23;
1133 syntax_size = strlen("Not available") + 23;
1134 syntax = alloca(syntax_size);
1136 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1137 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1138 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1139 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1140 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1142 acf->syntax ? acf->syntax : "Not available",
1143 COLOR_CYAN, 0, syntax_size);
1144 term_color(synopsis,
1145 acf->synopsis ? acf->synopsis : "Not available",
1146 COLOR_CYAN, 0, synopsis_size);
1147 term_color(description,
1148 acf->desc ? acf->desc : "Not available",
1149 COLOR_CYAN, 0, description_size);
1151 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1153 return RESULT_SUCCESS;
1156 static char *complete_show_function(char *line, char *word, int pos, int state)
1158 struct ast_custom_function *acf;
1161 int wordlen = strlen(word);
1163 /* try to lock functions list ... */
1164 if (ast_mutex_lock(&acflock)) {
1165 ast_log(LOG_ERROR, "Unable to lock function list\n");
1169 /* case-insensitive for convenience in this 'complete' function */
1170 for (acf = acf_root; acf && !ret; acf = acf->next) {
1171 if (!strncasecmp(word, acf->name, wordlen) && ++which > state)
1172 ret = strdup(acf->name);
1175 ast_mutex_unlock(&acflock);
1180 struct ast_custom_function* ast_custom_function_find(const char *name)
1182 struct ast_custom_function *acfptr;
1184 /* try to lock functions list ... */
1185 if (ast_mutex_lock(&acflock)) {
1186 ast_log(LOG_ERROR, "Unable to lock function list\n");
1190 for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
1191 if (!strcmp(name, acfptr->name))
1195 ast_mutex_unlock(&acflock);
1200 int ast_custom_function_unregister(struct ast_custom_function *acf)
1202 struct ast_custom_function *acfptr, *lastacf = NULL;
1208 /* try to lock functions list ... */
1209 if (ast_mutex_lock(&acflock)) {
1210 ast_log(LOG_ERROR, "Unable to lock function list\n");
1214 for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
1215 if (acfptr == acf) {
1217 lastacf->next = acf->next;
1219 acf_root = acf->next;
1227 ast_mutex_unlock(&acflock);
1229 if (!res && (option_verbose > 1))
1230 ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
1235 int ast_custom_function_register(struct ast_custom_function *acf)
1237 struct ast_custom_function *cur, *last = NULL;
1243 /* try to lock functions list ... */
1244 if (ast_mutex_lock(&acflock)) {
1245 ast_log(LOG_ERROR, "Unable to lock function list. Failed registering function %s\n", acf->name);
1249 if (ast_custom_function_find(acf->name)) {
1250 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
1251 ast_mutex_unlock(&acflock);
1255 for (cur = acf_root; cur; cur = cur->next) {
1256 if (strcmp(acf->name, cur->name) < 0) {
1262 acf->next = acf_root;
1270 /* Wasn't before anything else, put it at the end */
1279 ast_mutex_unlock(&acflock);
1281 if (option_verbose > 1)
1282 ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
1287 char *ast_func_read(struct ast_channel *chan, const char *in, char *workspace, size_t len)
1289 char *args = NULL, *function, *p;
1291 struct ast_custom_function *acfptr;
1293 function = ast_strdupa(in);
1295 ast_log(LOG_ERROR, "Out of memory\n");
1298 if ((args = strchr(function, '('))) {
1301 if ((p = strrchr(args, ')'))) {
1304 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
1307 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
1310 if ((acfptr = ast_custom_function_find(function))) {
1311 /* run the custom function */
1313 return acfptr->read(chan, function, args, workspace, len);
1315 ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
1318 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1323 void ast_func_write(struct ast_channel *chan, const char *in, const char *value)
1325 char *args = NULL, *function, *p;
1326 struct ast_custom_function *acfptr;
1328 function = ast_strdupa(in);
1330 ast_log(LOG_ERROR, "Out of memory\n");
1333 if ((args = strchr(function, '('))) {
1336 if ((p = strrchr(args, ')'))) {
1339 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
1342 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
1345 if ((acfptr = ast_custom_function_find(function))) {
1346 /* run the custom function */
1347 if (acfptr->write) {
1348 acfptr->write(chan, function, args, value);
1350 ast_log(LOG_ERROR, "Function %s is read-only, it cannot be written to\n", function);
1353 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1357 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
1360 const char *tmp, *whereweare;
1361 int length, offset, offset2, isfunction;
1362 char *workspace = NULL;
1363 char *ltmp = NULL, *var = NULL;
1364 char *nextvar, *nextexp, *nextthing;
1366 int pos, brackets, needsub, len;
1368 /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
1371 while(!ast_strlen_zero(whereweare) && count) {
1372 /* Assume we're copying the whole remaining string */
1373 pos = strlen(whereweare);
1376 nextthing = strchr(whereweare, '$');
1378 switch(nextthing[1]) {
1380 nextvar = nextthing;
1381 pos = nextvar - whereweare;
1384 nextexp = nextthing;
1385 pos = nextexp - whereweare;
1391 /* Can't copy more than 'count' bytes */
1395 /* Copy that many bytes */
1396 memcpy(cp2, whereweare, pos);
1404 /* We have a variable. Find the start and end, and determine
1405 if we are going to have to recursively call ourselves on the
1407 vars = vare = nextvar + 2;
1411 /* Find the end of it */
1412 while(brackets && *vare) {
1413 if ((vare[0] == '$') && (vare[1] == '{')) {
1416 } else if (vare[0] == '}') {
1418 } else if ((vare[0] == '$') && (vare[1] == '['))
1423 ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
1424 len = vare - vars - 1;
1426 /* Skip totally over variable string */
1427 whereweare += (len + 3);
1430 var = alloca(VAR_BUF_SIZE);
1432 /* Store variable name (and truncate) */
1433 ast_copy_string(var, vars, len + 1);
1435 /* Substitute if necessary */
1438 ltmp = alloca(VAR_BUF_SIZE);
1440 memset(ltmp, 0, VAR_BUF_SIZE);
1441 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1448 workspace = alloca(VAR_BUF_SIZE);
1450 workspace[0] = '\0';
1452 parse_variable_name(vars, &offset, &offset2, &isfunction);
1454 /* Evaluate function */
1455 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE);
1457 ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
1459 /* Retrieve variable value */
1460 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
1463 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
1465 length = strlen(cp4);
1468 memcpy(cp2, cp4, length);
1472 } else if (nextexp) {
1473 /* We have an expression. Find the start and end, and determine
1474 if we are going to have to recursively call ourselves on the
1476 vars = vare = nextexp + 2;
1480 /* Find the end of it */
1481 while(brackets && *vare) {
1482 if ((vare[0] == '$') && (vare[1] == '[')) {
1486 } else if (vare[0] == '[') {
1488 } else if (vare[0] == ']') {
1490 } else if ((vare[0] == '$') && (vare[1] == '{')) {
1497 ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
1498 len = vare - vars - 1;
1500 /* Skip totally over expression */
1501 whereweare += (len + 3);
1504 var = alloca(VAR_BUF_SIZE);
1506 /* Store variable name (and truncate) */
1507 ast_copy_string(var, vars, len + 1);
1509 /* Substitute if necessary */
1512 ltmp = alloca(VAR_BUF_SIZE);
1514 memset(ltmp, 0, VAR_BUF_SIZE);
1515 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1521 length = ast_expr(vars, cp2, count);
1524 ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
1533 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
1535 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
1538 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
1540 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
1543 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
1545 memset(passdata, 0, datalen);
1547 /* No variables or expressions in e->data, so why scan it? */
1548 if (!strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
1549 ast_copy_string(passdata, e->data, datalen);
1553 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
1556 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con, const char *context, const char *exten, int priority, const char *label, const char *callerid, int action)
1558 struct ast_exten *e;
1559 struct ast_app *app;
1560 struct ast_switch *sw;
1562 const char *foundcontext=NULL;
1566 char *incstack[AST_PBX_MAX_STACK];
1567 char passdata[EXT_DATA_SIZE];
1571 char tmp3[EXT_DATA_SIZE];
1573 char atmp2[EXT_DATA_SIZE+100];
1575 if (ast_mutex_lock(&conlock)) {
1576 ast_log(LOG_WARNING, "Unable to obtain lock\n");
1577 if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH) || (action == HELPER_MATCHMORE))
1582 e = pbx_find_extension(c, con, context, exten, priority, label, callerid, action, incstack, &stacklen, &status, &sw, &data, &foundcontext);
1585 case HELPER_CANMATCH:
1586 ast_mutex_unlock(&conlock);
1589 ast_mutex_unlock(&conlock);
1591 case HELPER_FINDLABEL:
1593 ast_mutex_unlock(&conlock);
1595 case HELPER_MATCHMORE:
1596 ast_mutex_unlock(&conlock);
1602 app = pbx_findapp(e->app);
1603 ast_mutex_unlock(&conlock);
1605 if (c->context != context)
1606 ast_copy_string(c->context, context, sizeof(c->context));
1607 if (c->exten != exten)
1608 ast_copy_string(c->exten, exten, sizeof(c->exten));
1609 c->priority = priority;
1610 pbx_substitute_variables(passdata, sizeof(passdata), c, e);
1612 ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
1613 snprintf(atmp, 80, "STACK-%s-%s-%d", context, exten, priority);
1614 snprintf(atmp2, EXT_DATA_SIZE+100, "%s(\"%s\", \"%s\") %s", app->name, c->name, passdata, (newstack ? "in new stack" : "in same stack"));
1615 pbx_builtin_setvar_helper(c, atmp, atmp2);
1617 if (option_verbose > 2)
1618 ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n",
1619 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
1620 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
1621 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
1622 (newstack ? "in new stack" : "in same stack"));
1623 manager_event(EVENT_FLAG_CALL, "Newexten",
1628 "Application: %s\r\n"
1631 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
1632 res = pbx_exec(c, app, passdata, newstack);
1635 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
1639 ast_log(LOG_WARNING, "Huh (%d)?\n", action);
1644 case HELPER_CANMATCH:
1645 ast_mutex_unlock(&conlock);
1648 ast_mutex_unlock(&conlock);
1650 case HELPER_MATCHMORE:
1651 ast_mutex_unlock(&conlock);
1653 case HELPER_FINDLABEL:
1654 ast_mutex_unlock(&conlock);
1660 ast_mutex_unlock(&conlock);
1662 res = sw->exec(c, foundcontext ? foundcontext : context, exten, priority, callerid, newstack, data);
1664 ast_log(LOG_WARNING, "No execution engine for switch %s\n", sw->name);
1669 ast_log(LOG_WARNING, "Huh (%d)?\n", action);
1673 ast_mutex_unlock(&conlock);
1675 case STATUS_NO_CONTEXT:
1676 if ((action != HELPER_EXISTS) && (action != HELPER_MATCHMORE))
1677 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
1679 case STATUS_NO_EXTENSION:
1680 if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
1681 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
1683 case STATUS_NO_PRIORITY:
1684 if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
1685 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
1687 case STATUS_NO_LABEL:
1689 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
1692 ast_log(LOG_DEBUG, "Shouldn't happen!\n");
1695 if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
1703 /*! \brief ast_hint_extension: Find hint for given extension in context */
1704 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
1706 struct ast_exten *e;
1707 struct ast_switch *sw;
1709 const char *foundcontext = NULL;
1711 char *incstack[AST_PBX_MAX_STACK];
1714 if (ast_mutex_lock(&conlock)) {
1715 ast_log(LOG_WARNING, "Unable to obtain lock\n");
1718 e = pbx_find_extension(c, NULL, context, exten, PRIORITY_HINT, NULL, "", HELPER_EXISTS, incstack, &stacklen, &status, &sw, &data, &foundcontext);
1719 ast_mutex_unlock(&conlock);
1723 /*! \brief ast_extensions_state2: Check state of extension by using hints */
1724 static int ast_extension_state2(struct ast_exten *e)
1726 char hint[AST_MAX_EXTENSION] = "";
1729 int allunavailable = 1, allbusy = 1, allfree = 1;
1730 int busy = 0, inuse = 0, ring = 0;
1735 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
1737 cur = hint; /* On or more devices separated with a & character */
1739 rest = strchr(cur, '&');
1745 res = ast_device_state(cur);
1747 case AST_DEVICE_NOT_INUSE:
1751 case AST_DEVICE_INUSE:
1756 case AST_DEVICE_RINGING:
1761 case AST_DEVICE_BUSY:
1766 case AST_DEVICE_UNAVAILABLE:
1767 case AST_DEVICE_INVALID:
1780 return AST_EXTENSION_RINGING;
1782 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
1784 return AST_EXTENSION_INUSE;
1786 return AST_EXTENSION_NOT_INUSE;
1788 return AST_EXTENSION_BUSY;
1790 return AST_EXTENSION_UNAVAILABLE;
1792 return AST_EXTENSION_INUSE;
1794 return AST_EXTENSION_NOT_INUSE;
1797 /*! \brief ast_extension_state2str: Return extension_state as string */
1798 const char *ast_extension_state2str(int extension_state)
1802 for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
1803 if (extension_states[i].extension_state == extension_state) {
1804 return extension_states[i].text;
1810 /*! \brief ast_extension_state: Check extension state for an extension by using hint */
1811 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
1813 struct ast_exten *e;
1815 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
1817 return -1; /* No hint, return -1 */
1819 return ast_extension_state2(e); /* Check all devices in the hint */
1822 void ast_hint_state_changed(const char *device)
1824 struct ast_hint *hint;
1825 struct ast_state_cb *cblist;
1826 char buf[AST_MAX_EXTENSION];
1831 AST_LIST_LOCK(&hints);
1833 AST_LIST_TRAVERSE(&hints, hint, list) {
1834 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
1836 for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) {
1837 if (strcasecmp(cur, device))
1840 /* Get device state for this hint */
1841 state = ast_extension_state2(hint->exten);
1843 if ((state == -1) || (state == hint->laststate))
1846 /* Device state changed since last check - notify the watchers */
1848 /* For general callbacks */
1849 for (cblist = statecbs; cblist; cblist = cblist->next)
1850 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
1852 /* For extension callbacks */
1853 for (cblist = hint->callbacks; cblist; cblist = cblist->next)
1854 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
1856 hint->laststate = state;
1861 AST_LIST_UNLOCK(&hints);
1864 /*! \brief ast_extension_state_add: Add watcher for extension states */
1865 int ast_extension_state_add(const char *context, const char *exten,
1866 ast_state_cb_type callback, void *data)
1868 struct ast_hint *hint;
1869 struct ast_state_cb *cblist;
1870 struct ast_exten *e;
1872 /* If there's no context and extension: add callback to statecbs list */
1873 if (!context && !exten) {
1874 AST_LIST_LOCK(&hints);
1876 for (cblist = statecbs; cblist; cblist = cblist->next) {
1877 if (cblist->callback == callback) {
1878 cblist->data = data;
1879 AST_LIST_UNLOCK(&hints);
1884 /* Now insert the callback */
1885 cblist = calloc(1, sizeof(struct ast_state_cb));
1887 AST_LIST_UNLOCK(&hints);
1891 cblist->callback = callback;
1892 cblist->data = data;
1894 cblist->next = statecbs;
1897 AST_LIST_UNLOCK(&hints);
1901 if (!context || !exten)
1904 /* This callback type is for only one hint, so get the hint */
1905 e = ast_hint_extension(NULL, context, exten);
1910 /* Find the hint in the list of hints */
1911 AST_LIST_LOCK(&hints);
1913 AST_LIST_TRAVERSE(&hints, hint, list) {
1914 if (hint->exten == e)
1919 /* We have no hint, sorry */
1920 AST_LIST_UNLOCK(&hints);
1924 /* Now insert the callback in the callback list */
1925 cblist = calloc(1, sizeof(struct ast_state_cb));
1927 AST_LIST_UNLOCK(&hints);
1930 cblist->id = stateid++; /* Unique ID for this callback */
1931 cblist->callback = callback; /* Pointer to callback routine */
1932 cblist->data = data; /* Data for the callback */
1934 cblist->next = hint->callbacks;
1935 hint->callbacks = cblist;
1937 AST_LIST_UNLOCK(&hints);
1941 /*! \brief ast_extension_state_del: Remove a watcher from the callback list */
1942 int ast_extension_state_del(int id, ast_state_cb_type callback)
1944 struct ast_hint *hint;
1945 struct ast_state_cb *cblist, *cbprev;
1947 if (!id && !callback)
1950 AST_LIST_LOCK(&hints);
1952 /* id is zero is a callback without extension */
1955 for (cblist = statecbs; cblist; cblist = cblist->next) {
1956 if (cblist->callback == callback) {
1958 statecbs = cblist->next;
1960 cbprev->next = cblist->next;
1964 AST_LIST_UNLOCK(&hints);
1970 AST_LIST_UNLOCK(&hints);
1974 /* id greater than zero is a callback with extension */
1975 /* Find the callback based on ID */
1976 AST_LIST_TRAVERSE(&hints, hint, list) {
1978 for (cblist = hint->callbacks; cblist; cblist = cblist->next) {
1979 if (cblist->id==id) {
1981 hint->callbacks = cblist->next;
1983 cbprev->next = cblist->next;
1987 AST_LIST_UNLOCK(&hints);
1994 AST_LIST_UNLOCK(&hints);
1998 /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
1999 static int ast_add_hint(struct ast_exten *e)
2001 struct ast_hint *hint;
2006 AST_LIST_LOCK(&hints);
2008 /* Search if hint exists, do nothing */
2009 AST_LIST_TRAVERSE(&hints, hint, list) {
2010 if (hint->exten == e) {
2011 AST_LIST_UNLOCK(&hints);
2012 if (option_debug > 1)
2013 ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2018 if (option_debug > 1)
2019 ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2021 hint = calloc(1, sizeof(struct ast_hint));
2023 AST_LIST_UNLOCK(&hints);
2024 if (option_debug > 1)
2025 ast_log(LOG_DEBUG, "HINTS: Out of memory...\n");
2028 /* Initialize and insert new item at the top */
2030 hint->laststate = ast_extension_state2(e);
2031 AST_LIST_INSERT_HEAD(&hints, hint, list);
2033 AST_LIST_UNLOCK(&hints);
2037 /*! \brief ast_change_hint: Change hint for an extension */
2038 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
2040 struct ast_hint *hint;
2043 AST_LIST_LOCK(&hints);
2044 AST_LIST_TRAVERSE(&hints, hint, list) {
2045 if (hint->exten == oe) {
2051 AST_LIST_UNLOCK(&hints);
2056 /*! \brief ast_remove_hint: Remove hint from extension */
2057 static int ast_remove_hint(struct ast_exten *e)
2059 /* Cleanup the Notifys if hint is removed */
2060 struct ast_hint *hint;
2061 struct ast_state_cb *cblist, *cbprev;
2067 AST_LIST_LOCK(&hints);
2068 AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
2069 if (hint->exten == e) {
2071 cblist = hint->callbacks;
2073 /* Notify with -1 and remove all callbacks */
2075 cblist = cblist->next;
2076 cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
2079 hint->callbacks = NULL;
2080 AST_LIST_REMOVE_CURRENT(&hints, list);
2086 AST_LIST_TRAVERSE_SAFE_END
2087 AST_LIST_UNLOCK(&hints);
2093 /*! \brief ast_get_hint: Get hint for channel */
2094 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
2096 struct ast_exten *e;
2099 e = ast_hint_extension(c, context, exten);
2102 ast_copy_string(hint, ast_get_extension_app(e), hintsize);
2104 tmp = ast_get_extension_app_data(e);
2106 ast_copy_string(name, (char *) tmp, namesize);
2113 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2115 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_EXISTS);
2118 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
2120 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, HELPER_FINDLABEL);
2123 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
2125 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, HELPER_FINDLABEL);
2128 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2130 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_CANMATCH);
2133 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2135 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_MATCHMORE);
2138 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2140 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_SPAWN);
2143 int ast_exec_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2145 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_EXEC);
2148 static int __ast_pbx_run(struct ast_channel *c)
2158 /* A little initial setup here */
2160 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
2161 c->pbx = calloc(1, sizeof(struct ast_pbx));
2163 ast_log(LOG_ERROR, "Out of memory\n");
2168 c->cdr = ast_cdr_alloc();
2170 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
2174 ast_cdr_init(c->cdr, c);
2177 /* Set reasonable defaults */
2178 c->pbx->rtimeout = 10;
2179 c->pbx->dtimeout = 5;
2181 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP);
2182 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
2184 /* Start by trying whatever the channel is set to */
2185 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2186 /* If not successful fall back to 's' */
2187 if (option_verbose > 1)
2188 ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
2189 ast_copy_string(c->exten, "s", sizeof(c->exten));
2190 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2191 /* JK02: And finally back to default if everything else failed */
2192 if (option_verbose > 1)
2193 ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
2194 ast_copy_string(c->context, "default", sizeof(c->context));
2198 if (c->cdr && !c->cdr->start.tv_sec && !c->cdr->start.tv_usec)
2199 ast_cdr_start(c->cdr);
2203 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2204 memset(exten, 0, sizeof(exten));
2205 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2206 /* Something bad happened, or a hangup has been requested. */
2207 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
2208 (res == '*') || (res == '#')) {
2209 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
2210 memset(exten, 0, sizeof(exten));
2212 exten[pos++] = digit = res;
2216 case AST_PBX_KEEPALIVE:
2218 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2219 else if (option_verbose > 1)
2220 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2225 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2226 else if (option_verbose > 1)
2227 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2228 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2233 if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2243 if ((c->_softhangup == AST_SOFTHANGUP_TIMEOUT) && (ast_exists_extension(c,c->context,"T",1,c->cid.cid_num))) {
2244 ast_copy_string(c->exten, "T", sizeof(c->exten));
2245 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
2246 c->whentohangup = 0;
2248 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
2249 } else if (c->_softhangup) {
2250 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
2251 c->exten, c->priority);
2257 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
2258 /* It's not a valid extension anymore */
2259 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2260 if (option_verbose > 2)
2261 ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
2262 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
2263 ast_copy_string(c->exten, "i", sizeof(c->exten));
2266 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
2267 c->name, c->exten, c->context);
2270 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2271 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
2274 /* Done, wait for an extension */
2277 waittime = c->pbx->dtimeout;
2278 else if (!autofallthrough)
2279 waittime = c->pbx->rtimeout;
2281 while (ast_matchmore_extension(c, c->context, exten, 1, c->cid.cid_num)) {
2282 /* As long as we're willing to wait, and as long as it's not defined,
2283 keep reading digits until we can't possibly get a right answer anymore. */
2284 digit = ast_waitfordigit(c, waittime * 1000);
2285 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2292 /* Error, maybe a hangup */
2294 exten[pos++] = digit;
2295 waittime = c->pbx->dtimeout;
2298 if (ast_exists_extension(c, c->context, exten, 1, c->cid.cid_num)) {
2299 /* Prepare the next cycle */
2300 ast_copy_string(c->exten, exten, sizeof(c->exten));
2303 /* No such extension */
2304 if (!ast_strlen_zero(exten)) {
2305 /* An invalid extension */
2306 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2307 if (option_verbose > 2)
2308 ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", exten, c->context, c->name);
2309 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", exten);
2310 ast_copy_string(c->exten, "i", sizeof(c->exten));
2313 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", exten, c->context);
2317 /* A simple timeout */
2318 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
2319 if (option_verbose > 2)
2320 ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
2321 ast_copy_string(c->exten, "t", sizeof(c->exten));
2324 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
2330 if (option_verbose > 2)
2331 ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
2337 status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
2340 if (option_verbose > 2)
2341 ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
2342 if (!strcasecmp(status, "CONGESTION"))
2343 res = pbx_builtin_congestion(c, "10");
2344 else if (!strcasecmp(status, "CHANUNAVAIL"))
2345 res = pbx_builtin_congestion(c, "10");
2346 else if (!strcasecmp(status, "BUSY"))
2347 res = pbx_builtin_busy(c, "10");
2353 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
2355 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
2359 while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2360 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2361 /* Something bad happened, or a hangup has been requested. */
2363 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2364 else if (option_verbose > 1)
2365 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2371 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
2373 pbx_destroy(c->pbx);
2375 if (res != AST_PBX_KEEPALIVE)
2380 /* Returns 0 on success, non-zero if call limit was reached */
2381 static int increase_call_count(const struct ast_channel *c)
2385 ast_mutex_lock(&maxcalllock);
2386 if (option_maxcalls) {
2387 if (countcalls >= option_maxcalls) {
2388 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
2392 if (option_maxload) {
2393 getloadavg(&curloadavg, 1);
2394 if (curloadavg >= option_maxload) {
2395 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
2401 ast_mutex_unlock(&maxcalllock);
2406 static void decrease_call_count(void)
2408 ast_mutex_lock(&maxcalllock);
2411 ast_mutex_unlock(&maxcalllock);
2414 static void *pbx_thread(void *data)
2416 /* Oh joyeous kernel, we're a new thread, with nothing to do but
2417 answer this channel and get it going.
2420 The launcher of this function _MUST_ increment 'countcalls'
2421 before invoking the function; it will be decremented when the
2422 PBX has finished running on the channel
2424 struct ast_channel *c = data;
2427 decrease_call_count();
2434 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
2437 pthread_attr_t attr;
2440 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
2441 return AST_PBX_FAILED;
2444 if (increase_call_count(c))
2445 return AST_PBX_CALL_LIMIT;
2447 /* Start a new thread, and get something handling this channel. */
2448 pthread_attr_init(&attr);
2449 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2450 if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
2451 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
2452 return AST_PBX_FAILED;
2455 return AST_PBX_SUCCESS;
2458 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
2460 enum ast_pbx_result res = AST_PBX_SUCCESS;
2462 if (increase_call_count(c))
2463 return AST_PBX_CALL_LIMIT;
2465 res = __ast_pbx_run(c);
2466 decrease_call_count();
2471 int ast_active_calls(void)
2476 int pbx_set_autofallthrough(int newval)
2479 oldval = autofallthrough;
2480 if (oldval != newval)
2481 autofallthrough = newval;
2486 * This function locks contexts list by &conlist, search for the right context
2487 * structure, leave context list locked and call ast_context_remove_include2
2488 * which removes include, unlock contexts list and return ...
2490 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
2492 struct ast_context *c = NULL;
2494 if (ast_lock_contexts())
2497 /* walk contexts and search for the right one ...*/
2498 while ( (c = ast_walk_contexts(c)) ) {
2499 /* we found one ... */
2500 if (!strcmp(ast_get_context_name(c), context)) {
2502 /* remove include from this context ... */
2503 ret = ast_context_remove_include2(c, include, registrar);
2505 ast_unlock_contexts();
2507 /* ... return results */
2512 /* we can't find the right one context */
2513 ast_unlock_contexts();
2518 * When we call this function, &conlock lock must be locked, because when
2519 * we giving *con argument, some process can remove/change this context
2520 * and after that there can be segfault.
2522 * This function locks given context, removes include, unlock context and
2525 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
2527 struct ast_include *i, *pi = NULL;
2529 if (ast_mutex_lock(&con->lock)) return -1;
2532 for (i = con->includes; i; i = i->next) {
2533 /* find our include */
2534 if (!strcmp(i->name, include) &&
2535 (!registrar || !strcmp(i->registrar, registrar))) {
2536 /* remove from list */
2540 con->includes = i->next;
2541 /* free include and return */
2543 ast_mutex_unlock(&con->lock);
2549 /* we can't find the right include */
2550 ast_mutex_unlock(&con->lock);
2555 * \note This function locks contexts list by &conlist, search for the rigt context
2556 * structure, leave context list locked and call ast_context_remove_switch2
2557 * which removes switch, unlock contexts list and return ...
2559 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
2561 struct ast_context *c = NULL;
2562 int ret = -1; /* default error return */
2564 if (ast_lock_contexts())
2567 /* walk contexts and search for the right one ...*/
2568 while ( (c = ast_walk_contexts(c)) ) {
2569 /* we found one ... */
2570 if (!strcmp(ast_get_context_name(c), context)) {
2571 /* remove switch from this context ... */
2572 ret = ast_context_remove_switch2(c, sw, data, registrar);
2577 /* found or error */
2578 ast_unlock_contexts();
2583 * \brief This function locks given context, removes switch, unlock context and
2585 * \note When we call this function, &conlock lock must be locked, because when
2586 * we giving *con argument, some process can remove/change this context
2587 * and after that there can be segfault.
2590 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
2592 struct ast_sw *i, *pi = NULL;
2594 if (ast_mutex_lock(&con->lock)) return -1;
2597 for (i = con->alts; i; i = i->next) {
2598 /* find our switch */
2599 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
2600 (!registrar || !strcmp(i->registrar, registrar))) {
2601 /* remove from list */
2605 con->alts = i->next;
2606 /* free switch and return */
2608 ast_mutex_unlock(&con->lock);
2614 /* we can't find the right switch */
2615 ast_mutex_unlock(&con->lock);
2620 * \note This functions lock contexts list, search for the right context,
2621 * call ast_context_remove_extension2, unlock contexts list and return.
2622 * In this function we are using
2624 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
2626 struct ast_context *c = NULL;
2627 int ret = -1; /* default error return */
2629 if (ast_lock_contexts())
2632 /* walk contexts ... */
2633 while ( (c = ast_walk_contexts(c)) ) {
2634 /* ... search for the right one ... */
2635 if (!strcmp(ast_get_context_name(c), context)) {
2636 /* ... remove extension ... */
2637 ret = ast_context_remove_extension2(c, extension, priority,
2642 /* found or error */
2643 ast_unlock_contexts();
2648 * \brief This functionc locks given context, search for the right extension and
2649 * fires out all peer in this extensions with given priority. If priority
2650 * is set to 0, all peers are removed. After that, unlock context and
2652 * \note When do you want to call this function, make sure that &conlock is locked,
2653 * because some process can handle with your *con context before you lock
2657 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
2659 struct ast_exten *exten, *prev_exten = NULL;
2661 if (ast_mutex_lock(&con->lock)) return -1;
2663 /* go through all extensions in context and search the right one ... */
2667 /* look for right extension */
2668 if (!strcmp(exten->exten, extension) &&
2669 (!registrar || !strcmp(exten->registrar, registrar))) {
2670 struct ast_exten *peer;
2672 /* should we free all peers in this extension? (priority == 0)? */
2673 if (priority == 0) {
2674 /* remove this extension from context list */
2676 prev_exten->next = exten->next;
2678 con->root = exten->next;
2680 /* fire out all peers */
2685 if (!peer->priority==PRIORITY_HINT)
2686 ast_remove_hint(peer);
2688 peer->datad(peer->data);
2694 ast_mutex_unlock(&con->lock);
2697 /* remove only extension with exten->priority == priority */
2698 struct ast_exten *previous_peer = NULL;
2702 /* is this our extension? */
2703 if (peer->priority == priority &&
2704 (!registrar || !strcmp(peer->registrar, registrar) )) {
2705 /* we are first priority extension? */
2706 if (!previous_peer) {
2707 /* exists previous extension here? */
2709 /* yes, so we must change next pointer in
2710 * previous connection to next peer
2713 prev_exten->next = peer->peer;
2714 peer->peer->next = exten->next;
2716 prev_exten->next = exten->next;
2718 /* no previous extension, we are first
2719 * extension, so change con->root ...
2722 con->root = peer->peer;
2724 con->root = exten->next;
2727 /* we are not first priority in extension */
2728 previous_peer->peer = peer->peer;
2731 /* now, free whole priority extension */
2732 if (peer->priority==PRIORITY_HINT)
2733 ast_remove_hint(peer);
2734 peer->datad(peer->data);
2737 ast_mutex_unlock(&con->lock);
2740 /* this is not right extension, skip to next peer */
2741 previous_peer = peer;
2746 ast_mutex_unlock(&con->lock);
2752 exten = exten->next;
2755 /* we can't find right extension */
2756 ast_mutex_unlock(&con->lock);
2761 /*! \brief Dynamically register a new dial plan application */
2762 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
2764 struct ast_app *tmp, *prev, *cur;
2767 length = sizeof(struct ast_app);
2768 length += strlen(app) + 1;
2769 if (ast_mutex_lock(&applock)) {
2770 ast_log(LOG_ERROR, "Unable to lock application list\n");
2773 for (tmp = apps; tmp; tmp = tmp->next) {
2774 if (!strcasecmp(app, tmp->name)) {
2775 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
2776 ast_mutex_unlock(&applock);
2781 tmp = calloc(1, length);
2783 ast_log(LOG_ERROR, "Out of memory\n");
2784 ast_mutex_unlock(&applock);
2788 strcpy(tmp->name, app);
2789 tmp->execute = execute;
2790 tmp->synopsis = synopsis;
2791 tmp->description = description;
2792 /* Store in alphabetical order */
2794 for (cur = apps; cur; cur = cur->next) {
2795 if (strcasecmp(tmp->name, cur->name) < 0)
2800 tmp->next = prev->next;
2807 if (option_verbose > 1)
2808 ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
2809 ast_mutex_unlock(&applock);
2813 int ast_register_switch(struct ast_switch *sw)
2815 struct ast_switch *tmp, *prev=NULL;
2816 if (ast_mutex_lock(&switchlock)) {
2817 ast_log(LOG_ERROR, "Unable to lock switch lock\n");
2820 for (tmp = switches; tmp; tmp = tmp->next) {
2821 if (!strcasecmp(tmp->name, sw->name))
2826 ast_mutex_unlock(&switchlock);
2827 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
2835 ast_mutex_unlock(&switchlock);
2839 void ast_unregister_switch(struct ast_switch *sw)
2841 struct ast_switch *tmp, *prev=NULL;
2842 if (ast_mutex_lock(&switchlock)) {
2843 ast_log(LOG_ERROR, "Unable to lock switch lock\n");
2846 for (tmp = switches; tmp; tmp = tmp->next) {
2849 prev->next = tmp->next;
2851 switches = tmp->next;
2857 ast_mutex_unlock(&switchlock);
2861 * Help for CLI commands ...
2863 static char show_application_help[] =
2864 "Usage: show application <application> [<application> [<application> [...]]]\n"
2865 " Describes a particular application.\n";
2867 static char show_functions_help[] =
2868 "Usage: show functions [like <text>]\n"
2869 " List builtin functions, optionally only those matching a given string\n";
2871 static char show_function_help[] =
2872 "Usage: show function <function>\n"
2873 " Describe a particular dialplan function.\n";
2875 static char show_applications_help[] =
2876 "Usage: show applications [{like|describing} <text>]\n"
2877 " List applications which are currently available.\n"
2878 " If 'like', <text> will be a substring of the app name\n"
2879 " If 'describing', <text> will be a substring of the description\n";
2881 static char show_dialplan_help[] =
2882 "Usage: show dialplan [exten@][context]\n"
2885 static char show_switches_help[] =
2886 "Usage: show switches\n"
2887 " Show registered switches\n";
2889 static char show_hints_help[] =
2890 "Usage: show hints\n"
2891 " Show registered hints\n";
2895 * IMPLEMENTATION OF CLI FUNCTIONS IS IN THE SAME ORDER AS COMMANDS HELPS
2900 * \brief 'show application' CLI command implementation functions ...
2904 * There is a possibility to show informations about more than one
2905 * application at one time. You can type 'show application Dial Echo' and
2906 * you will see informations about these two applications ...
2908 static char *complete_show_application(char *line, char *word, int pos, int state)
2913 int wordlen = strlen(word);
2915 /* try to lock applications list ... */
2916 if (ast_mutex_lock(&applock)) {
2917 ast_log(LOG_ERROR, "Unable to lock application list\n");
2921 /* return the n-th [partial] matching entry */
2922 for (a = apps; a && !ret; a = a->next) {
2923 if (!strncasecmp(word, a->name, wordlen) && ++which > state)
2924 ret = strdup(a->name);
2927 ast_mutex_unlock(&applock);
2932 static int handle_show_application(int fd, int argc, char *argv[])
2935 int app, no_registered_app = 1;
2937 if (argc < 3) return RESULT_SHOWUSAGE;
2939 /* try to lock applications list ... */
2940 if (ast_mutex_lock(&applock)) {
2941 ast_log(LOG_ERROR, "Unable to lock application list\n");
2945 /* ... go through all applications ... */
2946 for (a = apps; a; a = a->next) {
2947 /* ... compare this application name with all arguments given
2948 * to 'show application' command ... */
2949 for (app = 2; app < argc; app++) {
2950 if (!strcasecmp(a->name, argv[app])) {
2951 /* Maximum number of characters added by terminal coloring is 22 */
2952 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
2953 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
2954 int synopsis_size, description_size;
2956 no_registered_app = 0;
2959 synopsis_size = strlen(a->synopsis) + 23;
2961 synopsis_size = strlen("Not available") + 23;
2962 synopsis = alloca(synopsis_size);
2965 description_size = strlen(a->description) + 23;
2967 description_size = strlen("Not available") + 23;
2968 description = alloca(description_size);
2970 if (synopsis && description) {
2971 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
2972 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
2973 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
2974 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
2975 term_color(synopsis,
2976 a->synopsis ? a->synopsis : "Not available",
2977 COLOR_CYAN, 0, synopsis_size);
2978 term_color(description,
2979 a->description ? a->description : "Not available",
2980 COLOR_CYAN, 0, description_size);
2982 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
2984 /* ... one of our applications, show info ...*/
2985 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
2986 "[Synopsis]\n %s\n\n"
2987 "[Description]\n%s\n",
2989 a->synopsis ? a->synopsis : "Not available",
2990 a->description ? a->description : "Not available");
2996 ast_mutex_unlock(&applock);
2998 /* we found at least one app? no? */
2999 if (no_registered_app) {
3000 ast_cli(fd, "Your application(s) is (are) not registered\n");
3001 return RESULT_FAILURE;
3004 return RESULT_SUCCESS;
3007 /*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
3008 static int handle_show_hints(int fd, int argc, char *argv[])
3010 struct ast_hint *hint;
3013 struct ast_state_cb *watcher;
3015 if (AST_LIST_EMPTY(&hints)) {
3016 ast_cli(fd, "There are no registered dialplan hints\n");
3017 return RESULT_SUCCESS;
3019 /* ... we have hints ... */
3020 ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
3021 if (AST_LIST_LOCK(&hints)) {
3022 ast_log(LOG_ERROR, "Unable to lock hints\n");
3025 AST_LIST_TRAVERSE(&hints, hint, list) {
3027 for (watcher = hint->callbacks; watcher; watcher = watcher->next)
3029 ast_cli(fd, " %-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
3030 ast_get_extension_name(hint->exten), ast_get_extension_app(hint->exten),
3031 ast_extension_state2str(hint->laststate), watchers);
3034 ast_cli(fd, "----------------\n");
3035 ast_cli(fd, "- %d hints registered\n", num);
3036 AST_LIST_UNLOCK(&hints);
3037 return RESULT_SUCCESS;
3040 /*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
3041 static int handle_show_switches(int fd, int argc, char *argv[])
3043 struct ast_switch *sw;
3045 ast_cli(fd, "There are no registered alternative switches\n");
3046 return RESULT_SUCCESS;
3048 /* ... we have applications ... */
3049 ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
3050 if (ast_mutex_lock(&switchlock)) {
3051 ast_log(LOG_ERROR, "Unable to lock switches\n");
3054 for (sw = switches; sw; sw = sw->next) {
3055 ast_cli(fd, "%s: %s\n", sw->name, sw->description);
3057 ast_mutex_unlock(&switchlock);
3058 return RESULT_SUCCESS;
3062 * 'show applications' CLI command implementation functions ...
3064 static int handle_show_applications(int fd, int argc, char *argv[])
3067 int like = 0, describing = 0;
3068 int total_match = 0; /* Number of matches in like clause */
3069 int total_apps = 0; /* Number of apps registered */
3071 /* try to lock applications list ... */
3072 if (ast_mutex_lock(&applock)) {
3073 ast_log(LOG_ERROR, "Unable to lock application list\n");
3077 /* ... have we got at least one application (first)? no? */
3079 ast_cli(fd, "There are no registered applications\n");
3080 ast_mutex_unlock(&applock);
3084 /* show applications like <keyword> */
3085 if ((argc == 4) && (!strcmp(argv[2], "like"))) {
3087 } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
3091 /* show applications describing <keyword1> [<keyword2>] [...] */
3092 if ((!like) && (!describing)) {
3093 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3095 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3098 /* ... go through all applications ... */
3099 for (a = apps; a; a = a->next) {
3100 /* ... show informations about applications ... */
3104 if (strcasestr(a->name, argv[3])) {
3108 } else if (describing) {
3109 if (a->description) {
3110 /* Match all words on command line */
3113 for (i = 3; i < argc; i++) {
3114 if (!strcasestr(a->description, argv[i])) {
3126 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3129 if ((!like) && (!describing)) {
3130 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3132 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3135 /* ... unlock and return */
3136 ast_mutex_unlock(&applock);
3138 return RESULT_SUCCESS;
3141 static char *complete_show_applications(char *line, char *word, int pos, int state)
3143 int wordlen = strlen(word);
3146 if (ast_strlen_zero(word)) {
3149 return strdup("like");
3151 return strdup("describing");
3155 } else if (! strncasecmp(word, "like", wordlen)) {
3157 return strdup("like");
3161 } else if (! strncasecmp(word, "describing", wordlen)) {
3163 return strdup("describing");
3173 * 'show dialplan' CLI command implementation functions ...
3175 static char *complete_show_dialplan_context(char *line, char *word, int pos,
3178 struct ast_context *c = NULL;
3183 /* we are do completion of [exten@]context on second position only */
3187 /* try to lock contexts list ... */
3188 if (ast_lock_contexts()) {
3189 ast_log(LOG_ERROR, "Unable to lock context list\n");
3193 wordlen = strlen(word);
3195 /* ... walk through all contexts ... */
3196 while ( (c = ast_walk_contexts(c)) ) {
3197 /* ... word matches context name? yes? ... */
3198 if (!strncasecmp(word, ast_get_context_name(c), wordlen)) {
3199 /* ... for serve? ... */
3200 if (++which > state) {
3201 /* ... yes, serve this context name ... */
3202 ret = strdup(ast_get_context_name(c));
3208 /* ... unlock and return */
3209 ast_unlock_contexts();
3213 struct dialplan_counters {
3217 int context_existence;
3218 int extension_existence;
3221 static int show_dialplan_helper(int fd, char *context, char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, char *includes[])
3223 struct ast_context *c = NULL;
3224 int res = 0, old_total_exten = dpc->total_exten;
3226 /* try to lock contexts */
3227 if (ast_lock_contexts()) {
3228 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
3232 /* walk all contexts ... */
3233 while ( (c = ast_walk_contexts(c)) ) {
3234 /* show this context? */
3236 !strcmp(ast_get_context_name(c), context)) {
3237 dpc->context_existence = 1;
3239 /* try to lock context before walking in ... */
3240 if (!ast_lock_context(c)) {
3241 struct ast_exten *e;
3242 struct ast_include *i;
3243 struct ast_ignorepat *ip;
3245 char buf[256], buf2[256];
3246 int context_info_printed = 0;
3248 /* are we looking for exten too? if yes, we print context
3249 * if we our extension only
3252 dpc->total_context++;
3253 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3254 ast_get_context_name(c), ast_get_context_registrar(c));
3255 context_info_printed = 1;
3258 /* walk extensions ... */
3259 for (e = ast_walk_context_extensions(c, NULL); e; e = ast_walk_context_extensions(c, e)) {
3260 struct ast_exten *p;
3263 /* looking for extension? is this our extension? */
3265 !ast_extension_match(ast_get_extension_name(e), exten))
3267 /* we are looking for extension and it's not our
3268 * extension, so skip to next extension */
3272 dpc->extension_existence = 1;
3274 /* may we print context info? */
3275 if (!context_info_printed) {
3276 dpc->total_context++;
3278 /* TODO Print more info about rinclude */
3279 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
3280 ast_get_context_name(c),
3281 ast_get_context_registrar(c));
3283 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3284 ast_get_context_name(c),
3285 ast_get_context_registrar(c));
3287 context_info_printed = 1;
3291 /* write extension name and first peer */
3292 bzero(buf, sizeof(buf));
3293 snprintf(buf, sizeof(buf), "'%s' =>",
3294 ast_get_extension_name(e));
3296 prio = ast_get_extension_priority(e);
3297 if (prio == PRIORITY_HINT) {
3298 snprintf(buf2, sizeof(buf2),
3300 ast_get_extension_app(e));
3302 snprintf(buf2, sizeof(buf2),
3305 ast_get_extension_app(e),
3306 (char *)ast_get_extension_app_data(e));
3309 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
3310 ast_get_extension_registrar(e));
3313 /* walk next extension peers */
3314 for (p=ast_walk_extension_priorities(e, e); p; p=ast_walk_extension_priorities(e, p)) {
3316 bzero((void *)buf2, sizeof(buf2));
3317 bzero((void *)buf, sizeof(buf));
3318 if (ast_get_extension_label(p))
3319 snprintf(buf, sizeof(buf), " [%s]", ast_get_extension_label(p));
3320 prio = ast_get_extension_priority(p);
3321 if (prio == PRIORITY_HINT) {
3322 snprintf(buf2, sizeof(buf2),
3324 ast_get_extension_app(p));
3326 snprintf(buf2, sizeof(buf2),
3329 ast_get_extension_app(p),
3330 (char *)ast_get_extension_app_data(p));
3333 ast_cli(fd," %-17s %-45s [%s]\n",
3335 ast_get_extension_registrar(p));
3339 /* walk included and write info ... */
3340 for (i = ast_walk_context_includes(c, NULL); i; i = ast_walk_context_includes(c, i)) {
3341 bzero(buf, sizeof(buf));
3342 snprintf(buf, sizeof(buf), "'%s'",
3343 ast_get_include_name(i));
3345 /* Check all includes for the requested extension */
3346 if (includecount >= AST_PBX_MAX_STACK) {
3347 ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
3351 for (x=0;x<includecount;x++) {
3352 if (!strcasecmp(includes[x], ast_get_include_name(i))) {
3358 includes[includecount] = (char *)ast_get_include_name(i);
3359 show_dialplan_helper(fd, (char *)ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
3361 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
3365 ast_cli(fd, " Include => %-45s [%s]\n",
3366 buf, ast_get_include_registrar(i));
3370 /* walk ignore patterns and write info ... */
3371 for (ip = ast_walk_context_ignorepats(c, NULL); ip; ip = ast_walk_context_ignorepats(c, ip)) {
3372 const char *ipname = ast_get_ignorepat_name(ip);
3373 char ignorepat[AST_MAX_EXTENSION];
3374 snprintf(buf, sizeof(buf), "'%s'", ipname);
3375 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
3376 if ((!exten) || ast_extension_match(ignorepat, exten)) {
3377 ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
3378 buf, ast_get_ignorepat_registrar(ip));
3382 for (sw = ast_walk_context_switches(c, NULL); sw; sw = ast_walk_context_switches(c, sw)) {
3383 snprintf(buf, sizeof(buf), "'%s/%s'",
3384 ast_get_switch_name(sw),
3385 ast_get_switch_data(sw));
3386 ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
3387 buf, ast_get_switch_registrar(sw));
3391 ast_unlock_context(c);
3393 /* if we print something in context, make an empty line */
3394 if (context_info_printed) ast_cli(fd, "\r\n");
3398 ast_unlock_contexts();
3400 if (dpc->total_exten == old_total_exten) {
3401 /* Nothing new under the sun */
3408 static int handle_show_dialplan(int fd, int argc, char *argv[])
3410 char *exten = NULL, *context = NULL;
3411 /* Variables used for different counters */
3412 struct dialplan_counters counters;
3413 char *incstack[AST_PBX_MAX_STACK];
3414 memset(&counters, 0, sizeof(counters));
3416 if (argc != 2 && argc != 3)
3417 return RESULT_SHOWUSAGE;
3419 /* we obtain [exten@]context? if yes, split them ... */
3421 char *splitter = ast_strdupa(argv[2]);
3422 /* is there a '@' character? */
3423 if (splitter && strchr(argv[2], '@')) {
3424 /* yes, split into exten & context ... */
3425 exten = strsep(&splitter, "@");
3428 /* check for length and change to NULL if ast_strlen_zero() */