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>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <sys/types.h>
40 #if defined(HAVE_SYSINFO)
41 #include <sys/sysinfo.h>
44 #include "asterisk/lock.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/options.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/file.h"
51 #include "asterisk/callerid.h"
52 #include "asterisk/cdr.h"
53 #include "asterisk/config.h"
54 #include "asterisk/term.h"
55 #include "asterisk/manager.h"
56 #include "asterisk/ast_expr.h"
57 #include "asterisk/linkedlists.h"
58 #define SAY_STUBS /* generate declarations and stubs for say methods */
59 #include "asterisk/say.h"
60 #include "asterisk/utils.h"
61 #include "asterisk/causes.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/app.h"
64 #include "asterisk/devicestate.h"
65 #include "asterisk/stringfields.h"
66 #include "asterisk/event.h"
67 #include "asterisk/module.h"
70 * \note I M P O R T A N T :
72 * The speed of extension handling will likely be among the most important
73 * aspects of this PBX. The switching scheme as it exists right now isn't
74 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
75 * of priorities, but a constant search time here would be great ;-)
80 #define EXT_DATA_SIZE 256
82 #define EXT_DATA_SIZE 8192
85 #define SWITCH_DATA_LENGTH 256
87 #define VAR_BUF_SIZE 4096
90 #define VAR_SOFTTRAN 2
91 #define VAR_HARDTRAN 3
93 #define BACKGROUND_SKIP (1 << 0)
94 #define BACKGROUND_NOANSWER (1 << 1)
95 #define BACKGROUND_MATCHEXTEN (1 << 2)
96 #define BACKGROUND_PLAYBACK (1 << 3)
98 AST_APP_OPTIONS(background_opts, {
99 AST_APP_OPTION('s', BACKGROUND_SKIP),
100 AST_APP_OPTION('n', BACKGROUND_NOANSWER),
101 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
102 AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
105 #define WAITEXTEN_MOH (1 << 0)
107 AST_APP_OPTIONS(waitexten_opts, {
108 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
115 \brief ast_exten: An extension
116 The dialplan is saved as a linked list with each context
117 having it's own linked list of extensions - one item per
121 char *exten; /*!< Extension name */
122 int matchcid; /*!< Match caller id ? */
123 const char *cidmatch; /*!< Caller id to match for this extension */
124 int priority; /*!< Priority */
125 const char *label; /*!< Label */
126 struct ast_context *parent; /*!< The context this extension belongs to */
127 const char *app; /*!< Application to execute */
128 struct ast_app *cached_app; /*!< Cached location of application */
129 void *data; /*!< Data to use (arguments) */
130 void (*datad)(void *); /*!< Data destructor */
131 struct ast_exten *peer; /*!< Next higher priority with our extension */
132 const char *registrar; /*!< Registrar */
133 struct ast_exten *next; /*!< Extension with a greater ID */
137 /*! \brief ast_include: include= support in extensions.conf */
140 const char *rname; /*!< Context to include */
141 const char *registrar; /*!< Registrar */
142 int hastime; /*!< If time construct exists */
143 struct ast_timing timing; /*!< time construct */
144 struct ast_include *next; /*!< Link them together */
148 /*! \brief ast_sw: Switch statement in extensions.conf */
151 const char *registrar; /*!< Registrar */
152 char *data; /*!< Data load */
154 AST_LIST_ENTRY(ast_sw) list;
159 /*! \brief ast_ignorepat: Ignore patterns in dial plan */
160 struct ast_ignorepat {
161 const char *registrar;
162 struct ast_ignorepat *next;
163 const char pattern[0];
166 /*! \brief ast_context: An extension context */
168 ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
169 struct ast_exten *root; /*!< The root of the list of extensions */
170 struct ast_context *next; /*!< Link them together */
171 struct ast_include *includes; /*!< Include other contexts */
172 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
173 const char *registrar; /*!< Registrar */
174 AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
175 ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
176 char name[0]; /*!< Name of the context */
180 /*! \brief ast_app: A registered application */
182 int (*execute)(struct ast_channel *chan, void *data);
183 const char *synopsis; /*!< Synopsis text for 'show applications' */
184 const char *description; /*!< Description (help text) for 'show application <name>' */
185 AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
186 struct ast_module *module; /*!< Module this app belongs to */
187 char name[0]; /*!< Name of the application */
190 /*! \brief ast_state_cb: An extension state notify register item */
191 struct ast_state_cb {
194 ast_state_cb_type callback;
195 struct ast_state_cb *next;
198 /*! \brief Structure for dial plan hints
200 \note Hints are pointers from an extension in the dialplan to one or
201 more devices (tech/name)
202 - See \ref AstExtState
205 struct ast_exten *exten; /*!< Extension */
206 int laststate; /*!< Last known state */
207 struct ast_state_cb *callbacks; /*!< Callback list for this extension */
208 AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
211 static const struct cfextension_states {
213 const char * const text;
214 } extension_states[] = {
215 { AST_EXTENSION_NOT_INUSE, "Idle" },
216 { AST_EXTENSION_INUSE, "InUse" },
217 { AST_EXTENSION_BUSY, "Busy" },
218 { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
219 { AST_EXTENSION_RINGING, "Ringing" },
220 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
221 { AST_EXTENSION_ONHOLD, "Hold" },
222 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
226 AST_LIST_ENTRY(statechange) entry;
231 * \brief Data used by the device state thread
234 /*! Set to 1 to stop the thread */
236 /*! The device state monitoring thread */
238 /*! Lock for the state change queue */
240 /*! Condition for the state change queue */
242 /*! Queue of state changes */
243 AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
245 .thread = AST_PTHREADT_NULL,
248 static int pbx_builtin_answer(struct ast_channel *, void *);
249 static int pbx_builtin_goto(struct ast_channel *, void *);
250 static int pbx_builtin_hangup(struct ast_channel *, void *);
251 static int pbx_builtin_background(struct ast_channel *, void *);
252 static int pbx_builtin_wait(struct ast_channel *, void *);
253 static int pbx_builtin_waitexten(struct ast_channel *, void *);
254 static int pbx_builtin_keepalive(struct ast_channel *, void *);
255 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
256 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
257 static int pbx_builtin_ringing(struct ast_channel *, void *);
258 static int pbx_builtin_progress(struct ast_channel *, void *);
259 static int pbx_builtin_congestion(struct ast_channel *, void *);
260 static int pbx_builtin_busy(struct ast_channel *, void *);
261 static int pbx_builtin_noop(struct ast_channel *, void *);
262 static int pbx_builtin_gotoif(struct ast_channel *, void *);
263 static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
264 static int pbx_builtin_execiftime(struct ast_channel *, void *);
265 static int pbx_builtin_saynumber(struct ast_channel *, void *);
266 static int pbx_builtin_saydigits(struct ast_channel *, void *);
267 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
268 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
269 int pbx_builtin_setvar(struct ast_channel *, void *);
270 static int pbx_builtin_importvar(struct ast_channel *, void *);
272 AST_RWLOCK_DEFINE_STATIC(globalslock);
273 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
275 static int autofallthrough = 1;
277 /*! \brief Subscription for device state change events */
278 static struct ast_event_sub *device_state_sub;
280 AST_MUTEX_DEFINE_STATIC(maxcalllock);
281 static int countcalls;
283 static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
285 /*! \brief Declaration of builtin applications */
286 static struct pbx_builtin {
287 char name[AST_MAX_APP];
288 int (*execute)(struct ast_channel *chan, void *data);
293 /* These applications are built into the PBX core and do not
294 need separate modules */
296 { "Answer", pbx_builtin_answer,
297 "Answer a channel if ringing",
298 " Answer([delay]): If the call has not been answered, this application will\n"
299 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
300 "Asterisk will wait this number of milliseconds before returning to\n"
301 "the dialplan after answering the call.\n"
304 { "BackGround", pbx_builtin_background,
305 "Play an audio file while waiting for digits of an extension to go to.",
306 " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n"
307 "This application will play the given list of files while waiting for an\n"
308 "extension to be dialed by the calling channel. To continue waiting for digits\n"
309 "after this application has finished playing files, the WaitExten application\n"
310 "should be used. The 'langoverride' option explicitly specifies which language\n"
311 "to attempt to use for the requested sound files. If a 'context' is specified,\n"
312 "this is the dialplan context that this application will use when exiting to a\n"
314 " If one of the requested sound files does not exist, call processing will be\n"
317 " s - Causes the playback of the message to be skipped\n"
318 " if the channel is not in the 'up' state (i.e. it\n"
319 " hasn't been answered yet). If this happens, the\n"
320 " application will return immediately.\n"
321 " n - Don't answer the channel before playing the files.\n"
322 " m - Only break if a digit hit matches a one digit\n"
323 " extension in the destination context.\n"
324 "This application sets the following channel variable upon completion:\n"
325 " BACKGROUNDSTATUS The status of the background attempt as a text string, one of\n"
326 " SUCCESS | FAILED\n"
329 { "Busy", pbx_builtin_busy,
330 "Indicate the Busy condition",
331 " Busy([timeout]): This application will indicate the busy condition to\n"
332 "the calling channel. If the optional timeout is specified, the calling channel\n"
333 "will be hung up after the specified number of seconds. Otherwise, this\n"
334 "application will wait until the calling channel hangs up.\n"
337 { "Congestion", pbx_builtin_congestion,
338 "Indicate the Congestion condition",
339 " Congestion([timeout]): This application will indicate the congestion\n"
340 "condition to the calling channel. If the optional timeout is specified, the\n"
341 "calling channel will be hung up after the specified number of seconds.\n"
342 "Otherwise, this application will wait until the calling channel hangs up.\n"
345 { "ExecIfTime", pbx_builtin_execiftime,
346 "Conditional application execution based on the current time",
347 " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n"
348 "This application will execute the specified dialplan application, with optional\n"
349 "arguments, if the current time matches the given time specification.\n"
352 { "Goto", pbx_builtin_goto,
353 "Jump to a particular priority, extension, or context",
354 " Goto([[context|]extension|]priority): This application will set the current\n"
355 "context, extension, and priority in the channel structure. After it completes, the\n"
356 "pbx engine will continue dialplan execution at the specified location.\n"
357 "If no specific extension, or extension and context, are specified, then this\n"
358 "application will just set the specified priority of the current extension.\n"
359 " At least a priority is required as an argument, or the goto will return a -1,\n"
360 "and the channel and call will be terminated.\n"
361 " If the location that is put into the channel information is bogus, and asterisk cannot\n"
362 "find that location in the dialplan,\n"
363 "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
364 "extension in the current context. If that does not exist, it will try to execute the\n"
365 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
366 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
367 "What this means is that, for example, you specify a context that does not exist, then\n"
368 "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
371 { "GotoIf", pbx_builtin_gotoif,
373 " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
374 "context, extension, and priority in the channel structure based on the evaluation of\n"
375 "the given condition. After this application completes, the\n"
376 "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
377 "The channel will continue at\n"
378 "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
379 "false. The labels are specified with the same syntax as used within the Goto\n"
380 "application. If the label chosen by the condition is omitted, no jump is\n"
381 "performed, and the execution passes to the next instruction.\n"
382 "If the target location is bogus, and does not exist, the execution engine will try \n"
383 "to find and execute the code in the 'i' (invalid)\n"
384 "extension in the current context. If that does not exist, it will try to execute the\n"
385 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
386 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
387 "Remember that this command can set the current context, and if the context specified\n"
388 "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
389 "the channel and call will both be terminated!\n"
392 { "GotoIfTime", pbx_builtin_gotoiftime,
393 "Conditional Goto based on the current time",
394 " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n"
395 "This application will set the context, extension, and priority in the channel structure\n"
396 "if the current time matches the given time specification. Otherwise, nothing is done.\n"
397 "Further information on the time specification can be found in examples\n"
398 "illustrating how to do time-based context includes in the dialplan.\n"
399 "If the target jump location is bogus, the same actions would be taken as for Goto.\n"
402 { "ImportVar", pbx_builtin_importvar,
403 "Import a variable from a channel into a new variable",
404 " ImportVar(newvar=channelname|variable): This application imports a variable\n"
405 "from the specified channel (as opposed to the current one) and stores it as\n"
406 "a variable in the current channel (the channel that is calling this\n"
407 "application). Variables created by this application have the same inheritance\n"
408 "properties as those created with the Set application. See the documentation for\n"
409 "Set for more information.\n"
412 { "Hangup", pbx_builtin_hangup,
413 "Hang up the calling channel",
414 " Hangup([causecode]): This application will hang up the calling channel.\n"
415 "If a causecode is given the channel's hangup cause will be set to the given\n"
419 { "NoOp", pbx_builtin_noop,
421 " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n"
422 "purposes. Any text that is provided as arguments to this application can be\n"
423 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
424 "variables or functions without having any effect."
427 { "Progress", pbx_builtin_progress,
429 " Progress(): This application will request that in-band progress information\n"
430 "be provided to the calling channel.\n"
433 { "ResetCDR", pbx_builtin_resetcdr,
434 "Resets the Call Data Record",
435 " ResetCDR([options]): This application causes the Call Data Record to be\n"
438 " w -- Store the current CDR record before resetting it.\n"
439 " a -- Store any stacked records.\n"
440 " v -- Save CDR variables.\n"
443 { "Ringing", pbx_builtin_ringing,
444 "Indicate ringing tone",
445 " Ringing(): This application will request that the channel indicate a ringing\n"
446 "tone to the user.\n"
449 { "SayAlpha", pbx_builtin_saycharacters,
451 " SayAlpha(string): This application will play the sounds that correspond to\n"
452 "the letters of the given string.\n"
455 { "SayDigits", pbx_builtin_saydigits,
457 " SayDigits(digits): This application will play the sounds that correspond\n"
458 "to the digits of the given number. This will use the language that is currently\n"
459 "set for the channel. See the LANGUAGE function for more information on setting\n"
460 "the language for the channel.\n"
463 { "SayNumber", pbx_builtin_saynumber,
465 " SayNumber(digits[,gender]): This application will play the sounds that\n"
466 "correspond to the given number. Optionally, a gender may be specified.\n"
467 "This will use the language that is currently set for the channel. See the\n"
468 "LANGUAGE function for more information on setting the language for the channel.\n"
471 { "SayPhonetic", pbx_builtin_sayphonetic,
473 " SayPhonetic(string): This application will play the sounds from the phonetic\n"
474 "alphabet that correspond to the letters in the given string.\n"
477 { "Set", pbx_builtin_setvar,
478 "Set channel variable(s) or function value(s)",
479 " Set(name1=value1|name2=value2|..[|options])\n"
480 "This function can be used to set the value of channel variables or dialplan\n"
481 "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
482 "if the variable name is prefixed with _, the variable will be inherited into\n"
483 "channels created from the current channel. If the variable name is prefixed\n"
484 "with __, the variable will be inherited into channels created from the current\n"
485 "channel and all children channels.\n"
487 " g - Set variable globally instead of on the channel\n"
488 " (applies only to variables, not functions)\n"
491 { "SetAMAFlags", pbx_builtin_setamaflags,
493 " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
494 " billing purposes.\n"
497 { "Wait", pbx_builtin_wait,
498 "Waits for some time",
499 " Wait(seconds): This application waits for a specified number of seconds.\n"
500 "Then, dialplan execution will continue at the next priority.\n"
501 " Note that the seconds can be passed with fractions of a second. For example,\n"
502 "'1.5' will ask the application to wait for 1.5 seconds.\n"
505 { "WaitExten", pbx_builtin_waitexten,
506 "Waits for an extension to be entered",
507 " WaitExten([seconds][|options]): This application waits for the user to enter\n"
508 "a new extension for a specified number of seconds.\n"
509 " Note that the seconds can be passed with fractions of a second. For example,\n"
510 "'1.5' will ask the application to wait for 1.5 seconds.\n"
512 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
513 " Optionally, specify the class for music on hold within parenthesis.\n"
516 { "KeepAlive", pbx_builtin_keepalive,
517 "returns AST_PBX_KEEPALIVE value",
518 " KeepAlive(): This application is chiefly meant for internal use with Gosubs.\n"
519 "Please do not run it alone from the dialplan!\n"
524 static struct ast_context *contexts;
525 AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
527 static AST_RWLIST_HEAD_STATIC(apps, ast_app);
529 static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
531 static int stateid = 1;
533 When holding this list's lock, do _not_ do anything that will cause conlock
534 to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
535 function will take the locks in conlock/hints order, so any other
536 paths that require both locks must also take them in that order.
538 static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
539 struct ast_state_cb *statecbs;
542 \note This function is special. It saves the stack so that no matter
543 how many times it is called, it returns to the same place */
544 int pbx_exec(struct ast_channel *c, /*!< Channel */
545 struct ast_app *app, /*!< Application */
546 void *data) /*!< Data for execution */
549 struct ast_module_user *u = NULL;
550 const char *saved_c_appl;
551 const char *saved_c_data;
553 if (c->cdr && !ast_check_hangup(c))
554 ast_cdr_setapp(c->cdr, app->name, data);
556 /* save channel values */
557 saved_c_appl= c->appl;
558 saved_c_data= c->data;
563 u = __ast_module_user_add(app->module, c);
564 res = app->execute(c, data);
565 if (app->module && u)
566 __ast_module_user_remove(app->module, u);
567 /* restore channel values */
568 c->appl = saved_c_appl;
569 c->data = saved_c_data;
574 /*! Go no deeper than this through includes (not counting loops) */
575 #define AST_PBX_MAX_STACK 128
577 /*! \brief Find application handle in linked list
579 struct ast_app *pbx_findapp(const char *app)
583 AST_RWLIST_RDLOCK(&apps);
584 AST_RWLIST_TRAVERSE(&apps, tmp, list) {
585 if (!strcasecmp(tmp->name, app))
588 AST_RWLIST_UNLOCK(&apps);
593 static struct ast_switch *pbx_findswitch(const char *sw)
595 struct ast_switch *asw;
597 AST_RWLIST_RDLOCK(&switches);
598 AST_RWLIST_TRAVERSE(&switches, asw, list) {
599 if (!strcasecmp(asw->name, sw))
602 AST_RWLIST_UNLOCK(&switches);
607 static inline int include_valid(struct ast_include *i)
612 return ast_check_timing(&(i->timing));
615 static void pbx_destroy(struct ast_pbx *p)
621 * Special characters used in patterns:
622 * '_' underscore is the leading character of a pattern.
623 * In other position it is treated as a regular char.
624 * ' ' '-' space and '-' are separator and ignored.
625 * . one or more of any character. Only allowed at the end of
627 * ! zero or more of anything. Also impacts the result of CANMATCH
628 * and MATCHMORE. Only allowed at the end of a pattern.
629 * In the core routine, ! causes a match with a return code of 2.
630 * In turn, depending on the search mode: (XXX check if it is implemented)
631 * - E_MATCH retuns 1 (does match)
632 * - E_MATCHMORE returns 0 (no match)
633 * - E_CANMATCH returns 1 (does match)
635 * / should not appear as it is considered the separator of the CID info.
636 * XXX at the moment we may stop on this char.
638 * X Z N match ranges 0-9, 1-9, 2-9 respectively.
639 * [ denotes the start of a set of character. Everything inside
640 * is considered literally. We can have ranges a-d and individual
641 * characters. A '[' and '-' can be considered literally if they
642 * are just before ']'.
643 * XXX currently there is no way to specify ']' in a range, nor \ is
644 * considered specially.
646 * When we compare a pattern with a specific extension, all characters in the extension
647 * itself are considered literally with the only exception of '-' which is considered
648 * as a separator and thus ignored.
649 * XXX do we want to consider space as a separator as well ?
650 * XXX do we want to consider the separators in non-patterns as well ?
654 * \brief helper functions to sort extensions and patterns in the desired way,
655 * so that more specific patterns appear first.
657 * ext_cmp1 compares individual characters (or sets of), returning
658 * an int where bits 0-7 are the ASCII code of the first char in the set,
659 * while bit 8-15 are the cardinality of the set minus 1.
660 * This way more specific patterns (smaller cardinality) appear first.
661 * Wildcards have a special value, so that we can directly compare them to
662 * sets by subtracting the two values. In particular:
663 * 0x000xx one character, xx
664 * 0x0yyxx yy character set starting with xx
665 * 0x10000 '.' (one or more of anything)
666 * 0x20000 '!' (zero or more of anything)
667 * 0x30000 NUL (end of string)
668 * 0x40000 error in set.
669 * The pointer to the string is advanced according to needs.
671 * 1. the empty set is equivalent to NUL.
672 * 2. given that a full set has always 0 as the first element,
673 * we could encode the special cases as 0xffXX where XX
674 * is 1, 2, 3, 4 as used above.
676 static int ext_cmp1(const char **p)
679 int c, cmin = 0xff, count = 0;
682 /* load, sign extend and advance pointer until we find
685 while ( (c = *(*p)++) && (c == ' ' || c == '-') )
686 ; /* ignore some characters */
688 /* always return unless we have a set of chars */
690 default: /* ordinary character */
691 return 0x0000 | (c & 0xff);
694 return 0x0700 | '2' ;
702 case '.': /* wildcard */
705 case '!': /* earlymatch */
706 return 0x20000; /* less specific than NULL */
708 case '\0': /* empty string */
712 case '[': /* pattern */
715 /* locate end of set */
716 end = strchr(*p, ']');
719 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
720 return 0x40000; /* XXX make this entry go last... */
723 bzero(chars, sizeof(chars)); /* clear all chars in the set */
724 for (; *p < end ; (*p)++) {
725 unsigned char c1, c2; /* first-last char in range */
726 c1 = (unsigned char)((*p)[0]);
727 if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
728 c2 = (unsigned char)((*p)[2]);
729 *p += 2; /* skip a total of 3 chars */
730 } else /* individual character */
734 for (; c1 <= c2; c1++) {
735 uint32_t mask = 1 << (c1 % 32);
736 if ( (chars[ c1 / 32 ] & mask) == 0)
738 chars[ c1 / 32 ] |= mask;
742 return count == 0 ? 0x30000 : (count | cmin);
746 * \brief the full routine to compare extensions in rules.
748 static int ext_cmp(const char *a, const char *b)
750 /* make sure non-patterns come first.
751 * If a is not a pattern, it either comes first or
752 * we use strcmp to compare the strings.
757 return (b[0] == '_') ? -1 : strcmp(a, b);
759 /* Now we know a is a pattern; if b is not, a comes first */
762 #if 0 /* old mode for ext matching */
765 /* ok we need full pattern sorting routine */
766 while (!ret && a && b)
767 ret = ext_cmp1(&a) - ext_cmp1(&b);
771 return (ret > 0) ? 1 : -1;
775 * When looking up extensions, we can have different requests
776 * identified by the 'action' argument, as follows.
777 * Note that the coding is such that the low 4 bits are the
778 * third argument to extension_match_core.
781 E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
782 E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
783 E_MATCH = 0x02, /* extension is an exact match */
784 E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
785 E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
786 E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
790 * Internal function for ast_extension_{match|close}
791 * return 0 on no-match, 1 on match, 2 on early match.
792 * mode is as follows:
793 * E_MATCH success only on exact match
794 * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
795 * E_CANMATCH either of the above.
798 static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
800 mode &= E_MATCH_MASK; /* only consider the relevant bits */
802 if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
805 if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
806 int ld = strlen(data), lp = strlen(pattern);
808 if (lp < ld) /* pattern too short, cannot match */
810 /* depending on the mode, accept full or partial match or both */
812 return !strcmp(pattern, data); /* 1 on match, 0 on fail */
813 if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
814 return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
818 pattern++; /* skip leading _ */
820 * XXX below we stop at '/' which is a separator for the CID info. However we should
821 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
823 while (*data && *pattern && *pattern != '/') {
826 if (*data == '-') { /* skip '-' in data (just a separator) */
830 switch (toupper(*pattern)) {
831 case '[': /* a range */
832 end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
834 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
835 return 0; /* unconditional failure */
837 for (pattern++; pattern != end; pattern++) {
838 if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
839 if (*data >= pattern[0] && *data <= pattern[2])
840 break; /* match found */
842 pattern += 2; /* skip a total of 3 chars */
845 } else if (*data == pattern[0])
846 break; /* match found */
850 pattern = end; /* skip and continue */
853 if (*data < '2' || *data > '9')
857 if (*data < '0' || *data > '9')
861 if (*data < '1' || *data > '9')
864 case '.': /* Must match, even with more digits */
866 case '!': /* Early match */
869 case '-': /* Ignore these in patterns */
870 data--; /* compensate the final data++ */
873 if (*data != *pattern)
879 if (*data) /* data longer than pattern, no match */
882 * match so far, but ran off the end of the data.
883 * Depending on what is next, determine match or not.
885 if (*pattern == '\0' || *pattern == '/') /* exact match */
886 return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
887 else if (*pattern == '!') /* early match */
889 else /* partial match */
890 return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
894 * Wrapper around _extension_match_core() to do performance measurement
895 * using the profiling code.
897 static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
900 static int prof_id = -2; /* marker for 'unallocated' id */
902 prof_id = ast_add_profile("ext_match", 0);
903 ast_mark(prof_id, 1);
904 i = _extension_match_core(pattern, data, mode);
905 ast_mark(prof_id, 0);
909 int ast_extension_match(const char *pattern, const char *data)
911 return extension_match_core(pattern, data, E_MATCH);
914 int ast_extension_close(const char *pattern, const char *data, int needmore)
916 if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
917 ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
918 return extension_match_core(pattern, data, needmore);
921 struct ast_context *ast_context_find(const char *name)
923 struct ast_context *tmp = NULL;
924 ast_rdlock_contexts();
925 while ( (tmp = ast_walk_contexts(tmp)) ) {
926 if (!name || !strcasecmp(name, tmp->name))
929 ast_unlock_contexts();
933 #define STATUS_NO_CONTEXT 1
934 #define STATUS_NO_EXTENSION 2
935 #define STATUS_NO_PRIORITY 3
936 #define STATUS_NO_LABEL 4
937 #define STATUS_SUCCESS 5
939 static int matchcid(const char *cidpattern, const char *callerid)
941 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
942 failing to get a number should count as a match, otherwise not */
944 if (ast_strlen_zero(callerid))
945 return ast_strlen_zero(cidpattern) ? 1 : 0;
947 return ast_extension_match(cidpattern, callerid);
950 /* request and result for pbx_find_extension */
951 struct pbx_find_info {
958 char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
959 int stacklen; /* modified during the search */
960 int status; /* set on return */
961 struct ast_switch *swo; /* set on return */
962 const char *data; /* set on return */
963 const char *foundcontext; /* set on return */
966 static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
967 struct ast_context *bypass, struct pbx_find_info *q,
968 const char *context, const char *exten, int priority,
969 const char *label, const char *callerid, enum ext_match_t action)
972 struct ast_context *tmp;
973 struct ast_exten *e, *eroot;
974 struct ast_include *i;
977 /* Initialize status if appropriate */
978 if (q->stacklen == 0) {
979 q->status = STATUS_NO_CONTEXT;
982 q->foundcontext = NULL;
983 } else if (q->stacklen >= AST_PBX_MAX_STACK) {
984 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
988 /* Check first to see if we've already been checked */
989 for (x = 0; x < q->stacklen; x++) {
990 if (!strcasecmp(q->incstack[x], context))
994 if (bypass) /* bypass means we only look there */
996 else { /* look in contexts */
998 while ((tmp = ast_walk_contexts(tmp)) ) {
999 if (!strcmp(tmp->name, context))
1006 if (q->status < STATUS_NO_EXTENSION)
1007 q->status = STATUS_NO_EXTENSION;
1009 /* scan the list trying to match extension and CID */
1011 while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
1012 int match = extension_match_core(eroot->exten, exten, action);
1013 /* 0 on fail, 1 on match, 2 on earlymatch */
1015 if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
1016 continue; /* keep trying */
1017 if (match == 2 && action == E_MATCHMORE) {
1018 /* We match an extension ending in '!'.
1019 * The decision in this case is final and is NULL (no match).
1023 /* found entry, now look for the right priority */
1024 if (q->status < STATUS_NO_PRIORITY)
1025 q->status = STATUS_NO_PRIORITY;
1027 while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
1028 /* Match label or priority */
1029 if (action == E_FINDLABEL) {
1030 if (q->status < STATUS_NO_LABEL)
1031 q->status = STATUS_NO_LABEL;
1032 if (label && e->label && !strcmp(label, e->label))
1033 break; /* found it */
1034 } else if (e->priority == priority) {
1035 break; /* found it */
1036 } /* else keep searching */
1038 if (e) { /* found a valid match */
1039 q->status = STATUS_SUCCESS;
1040 q->foundcontext = context;
1044 /* Check alternative switches */
1045 AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
1046 struct ast_switch *asw = pbx_findswitch(sw->name);
1047 ast_switch_f *aswf = NULL;
1051 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
1054 /* Substitute variables now */
1056 pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1);
1058 /* equivalent of extension_match_core() at the switch level */
1059 if (action == E_CANMATCH)
1060 aswf = asw->canmatch;
1061 else if (action == E_MATCHMORE)
1062 aswf = asw->matchmore;
1063 else /* action == E_MATCH */
1065 datap = sw->eval ? sw->tmpdata : sw->data;
1066 res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap);
1067 if (res) { /* Got a match */
1070 q->foundcontext = context;
1071 /* XXX keep status = STATUS_NO_CONTEXT ? */
1075 q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
1076 /* Now try any includes we have in this context */
1077 for (i = tmp->includes; i; i = i->next) {
1078 if (include_valid(i)) {
1079 if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action)))
1088 /*! \brief extract offset:length from variable name.
1089 * Returns 1 if there is a offset:length part, which is
1090 * trimmed off (values go into variables)
1092 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
1099 for (; *var; var++) {
1103 } else if (*var == ')') {
1105 } else if (*var == ':' && parens == 0) {
1107 sscanf(var, "%d:%d", offset, length);
1108 return 1; /* offset:length valid */
1114 /*! \brief takes a substring. It is ok to call with value == workspace.
1116 * offset < 0 means start from the end of the string and set the beginning
1117 * to be that many characters back.
1118 * length is the length of the substring. A value less than 0 means to leave
1119 * that many off the end.
1120 * Always return a copy in workspace.
1122 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
1124 char *ret = workspace;
1125 int lr; /* length of the input string after the copy */
1127 ast_copy_string(workspace, value, workspace_len); /* always make a copy */
1129 lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
1131 /* Quick check if no need to do anything */
1132 if (offset == 0 && length >= lr) /* take the whole string */
1135 if (offset < 0) { /* translate negative offset into positive ones */
1136 offset = lr + offset;
1137 if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
1141 /* too large offset result in empty string so we know what to return */
1143 return ret + lr; /* the final '\0' */
1145 ret += offset; /* move to the start position */
1146 if (length >= 0 && length < lr - offset) /* truncate if necessary */
1148 else if (length < 0) {
1149 if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
1150 ret[lr + length - offset] = '\0';
1158 /*! \brief Support for Asterisk built-in variables in the dialplan
1161 - \ref AstVar Channel variables
1162 - \ref AstCauses The HANGUPCAUSE variable
1164 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
1166 const char not_found = '\0';
1168 const char *s; /* the result */
1170 int i, need_substring;
1171 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
1174 places[0] = &c->varshead;
1177 * Make a copy of var because parse_variable_name() modifies the string.
1178 * Then if called directly, we might need to run substring() on the result;
1179 * remember this for later in 'need_substring', 'offset' and 'length'
1181 tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
1182 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
1185 * Look first into predefined variables, then into variable lists.
1186 * Variable 's' points to the result, according to the following rules:
1187 * s == ¬_found (set at the beginning) means that we did not find a
1188 * matching variable and need to look into more places.
1189 * If s != ¬_found, s is a valid result string as follows:
1190 * s = NULL if the variable does not have a value;
1191 * you typically do this when looking for an unset predefined variable.
1192 * s = workspace if the result has been assembled there;
1193 * typically done when the result is built e.g. with an snprintf(),
1194 * so we don't need to do an additional copy.
1195 * s != workspace in case we have a string, that needs to be copied
1196 * (the ast_copy_string is done once for all at the end).
1197 * Typically done when the result is already available in some string.
1199 s = ¬_found; /* default value */
1200 if (c) { /* This group requires a valid channel */
1201 /* Names with common parts are looked up a piece at a time using strncmp. */
1202 if (!strncmp(var, "CALL", 4)) {
1203 if (!strncmp(var + 4, "ING", 3)) {
1204 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
1205 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
1207 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
1208 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
1210 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
1211 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
1213 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
1214 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
1218 } else if (!strcmp(var, "HINT")) {
1219 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
1220 } else if (!strcmp(var, "HINTNAME")) {
1221 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
1222 } else if (!strcmp(var, "EXTEN")) {
1224 } else if (!strcmp(var, "CONTEXT")) {
1226 } else if (!strcmp(var, "PRIORITY")) {
1227 snprintf(workspace, workspacelen, "%d", c->priority);
1229 } else if (!strcmp(var, "CHANNEL")) {
1231 } else if (!strcmp(var, "UNIQUEID")) {
1233 } else if (!strcmp(var, "HANGUPCAUSE")) {
1234 snprintf(workspace, workspacelen, "%d", c->hangupcause);
1238 if (s == ¬_found) { /* look for more */
1239 if (!strcmp(var, "EPOCH")) {
1240 snprintf(workspace, workspacelen, "%u",(int)time(NULL));
1242 } else if (!strcmp(var, "SYSTEMNAME")) {
1243 s = ast_config_AST_SYSTEM_NAME;
1246 /* if not found, look into chanvars or global vars */
1247 for (i = 0; s == ¬_found && i < (sizeof(places) / sizeof(places[0])); i++) {
1248 struct ast_var_t *variables;
1251 if (places[i] == &globals)
1252 ast_rwlock_rdlock(&globalslock);
1253 AST_LIST_TRAVERSE(places[i], variables, entries) {
1254 if (strcasecmp(ast_var_name(variables), var)==0) {
1255 s = ast_var_value(variables);
1259 if (places[i] == &globals)
1260 ast_rwlock_unlock(&globalslock);
1262 if (s == ¬_found || s == NULL)
1266 ast_copy_string(workspace, s, workspacelen);
1269 *ret = substring(*ret, offset, length, workspace, workspacelen);
1273 static int handle_show_functions(int fd, int argc, char *argv[])
1275 struct ast_custom_function *acf;
1279 if (argc == 5 && (!strcmp(argv[3], "like")) ) {
1281 } else if (argc != 3) {
1282 return RESULT_SHOWUSAGE;
1285 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1287 AST_RWLIST_RDLOCK(&acf_root);
1288 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
1289 if (!like || strstr(acf->name, argv[4])) {
1291 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1294 AST_RWLIST_UNLOCK(&acf_root);
1296 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1298 return RESULT_SUCCESS;
1301 static int handle_show_function(int fd, int argc, char *argv[])
1303 struct ast_custom_function *acf;
1304 /* Maximum number of characters added by terminal coloring is 22 */
1305 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1306 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1307 char stxtitle[40], *syntax = NULL;
1308 int synopsis_size, description_size, syntax_size;
1311 return RESULT_SHOWUSAGE;
1313 if (!(acf = ast_custom_function_find(argv[3]))) {
1314 ast_cli(fd, "No function by that name registered.\n");
1315 return RESULT_FAILURE;
1320 synopsis_size = strlen(acf->synopsis) + 23;
1322 synopsis_size = strlen("Not available") + 23;
1323 synopsis = alloca(synopsis_size);
1326 description_size = strlen(acf->desc) + 23;
1328 description_size = strlen("Not available") + 23;
1329 description = alloca(description_size);
1332 syntax_size = strlen(acf->syntax) + 23;
1334 syntax_size = strlen("Not available") + 23;
1335 syntax = alloca(syntax_size);
1337 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1338 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1339 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1340 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1341 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1343 acf->syntax ? acf->syntax : "Not available",
1344 COLOR_CYAN, 0, syntax_size);
1345 term_color(synopsis,
1346 acf->synopsis ? acf->synopsis : "Not available",
1347 COLOR_CYAN, 0, synopsis_size);
1348 term_color(description,
1349 acf->desc ? acf->desc : "Not available",
1350 COLOR_CYAN, 0, description_size);
1352 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1354 return RESULT_SUCCESS;
1357 static char *complete_show_function(const char *line, const char *word, int pos, int state)
1359 struct ast_custom_function *acf;
1362 int wordlen = strlen(word);
1364 /* case-insensitive for convenience in this 'complete' function */
1365 AST_RWLIST_RDLOCK(&acf_root);
1366 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
1367 if (!strncasecmp(word, acf->name, wordlen) && ++which > state) {
1368 ret = ast_strdup(acf->name);
1372 AST_RWLIST_UNLOCK(&acf_root);
1377 struct ast_custom_function *ast_custom_function_find(const char *name)
1379 struct ast_custom_function *acf = NULL;
1381 AST_RWLIST_RDLOCK(&acf_root);
1382 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
1383 if (!strcmp(name, acf->name))
1386 AST_RWLIST_UNLOCK(&acf_root);
1391 int ast_custom_function_unregister(struct ast_custom_function *acf)
1393 struct ast_custom_function *cur;
1398 AST_RWLIST_WRLOCK(&acf_root);
1399 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1401 AST_RWLIST_REMOVE_CURRENT(&acf_root, acflist);
1402 ast_verb(2, "Unregistered custom function %s\n", acf->name);
1406 AST_RWLIST_TRAVERSE_SAFE_END
1407 AST_RWLIST_UNLOCK(&acf_root);
1409 return acf ? 0 : -1;
1412 int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
1414 struct ast_custom_function *cur;
1421 AST_RWLIST_WRLOCK(&acf_root);
1423 AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
1424 if (!strcmp(acf->name, cur->name)) {
1425 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
1426 AST_RWLIST_UNLOCK(&acf_root);
1431 /* Store in alphabetical order */
1432 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1433 if (strcasecmp(acf->name, cur->name) < 0) {
1434 AST_RWLIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist);
1438 AST_RWLIST_TRAVERSE_SAFE_END
1440 AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
1442 AST_RWLIST_UNLOCK(&acf_root);
1444 ast_verb(2, "Registered custom function %s\n", acf->name);
1449 /*! \brief return a pointer to the arguments of the function,
1450 * and terminates the function name with '\\0'
1452 static char *func_args(char *function)
1454 char *args = strchr(function, '(');
1457 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
1461 if ((p = strrchr(args, ')')) )
1464 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
1469 int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
1471 char *copy = ast_strdupa(function);
1472 char *args = func_args(copy);
1473 struct ast_custom_function *acfptr = ast_custom_function_find(copy);
1476 ast_log(LOG_ERROR, "Function %s not registered\n", copy);
1477 else if (!acfptr->read)
1478 ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
1481 struct ast_module_user *u = NULL;
1483 u = __ast_module_user_add(acfptr->mod, chan);
1484 res = acfptr->read(chan, copy, args, workspace, len);
1485 if (acfptr->mod && u)
1486 __ast_module_user_remove(acfptr->mod, u);
1492 int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
1494 char *copy = ast_strdupa(function);
1495 char *args = func_args(copy);
1496 struct ast_custom_function *acfptr = ast_custom_function_find(copy);
1499 ast_log(LOG_ERROR, "Function %s not registered\n", copy);
1500 else if (!acfptr->write)
1501 ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
1504 struct ast_module_user *u = NULL;
1506 u = __ast_module_user_add(acfptr->mod, chan);
1507 res = acfptr->write(chan, copy, args, value);
1508 if (acfptr->mod && u)
1509 __ast_module_user_remove(acfptr->mod, u);
1516 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
1518 /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
1521 const char *tmp, *whereweare;
1522 int length, offset, offset2, isfunction;
1523 char *workspace = NULL;
1524 char *ltmp = NULL, *var = NULL;
1525 char *nextvar, *nextexp, *nextthing;
1527 int pos, brackets, needsub, len;
1530 while (!ast_strlen_zero(whereweare) && count) {
1531 /* Assume we're copying the whole remaining string */
1532 pos = strlen(whereweare);
1535 nextthing = strchr(whereweare, '$');
1537 switch (nextthing[1]) {
1539 nextvar = nextthing;
1540 pos = nextvar - whereweare;
1543 nextexp = nextthing;
1544 pos = nextexp - whereweare;
1550 /* Can't copy more than 'count' bytes */
1554 /* Copy that many bytes */
1555 memcpy(cp2, whereweare, pos);
1563 /* We have a variable. Find the start and end, and determine
1564 if we are going to have to recursively call ourselves on the
1566 vars = vare = nextvar + 2;
1570 /* Find the end of it */
1571 while (brackets && *vare) {
1572 if ((vare[0] == '$') && (vare[1] == '{')) {
1574 } else if (vare[0] == '{') {
1576 } else if (vare[0] == '}') {
1578 } else if ((vare[0] == '$') && (vare[1] == '['))
1583 ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
1584 len = vare - vars - 1;
1586 /* Skip totally over variable string */
1587 whereweare += (len + 3);
1590 var = alloca(VAR_BUF_SIZE);
1592 /* Store variable name (and truncate) */
1593 ast_copy_string(var, vars, len + 1);
1595 /* Substitute if necessary */
1598 ltmp = alloca(VAR_BUF_SIZE);
1600 memset(ltmp, 0, VAR_BUF_SIZE);
1601 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1608 workspace = alloca(VAR_BUF_SIZE);
1610 workspace[0] = '\0';
1612 parse_variable_name(vars, &offset, &offset2, &isfunction);
1614 /* Evaluate function */
1616 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1618 struct varshead old;
1619 struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
1621 memcpy(&old, &c->varshead, sizeof(old));
1622 memcpy(&c->varshead, headp, sizeof(c->varshead));
1623 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1624 /* Don't deallocate the varshead that was passed in */
1625 memcpy(&c->varshead, &old, sizeof(c->varshead));
1626 ast_channel_free(c);
1628 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
1630 ast_debug(1, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
1632 /* Retrieve variable value */
1633 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
1636 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
1638 length = strlen(cp4);
1641 memcpy(cp2, cp4, length);
1645 } else if (nextexp) {
1646 /* We have an expression. Find the start and end, and determine
1647 if we are going to have to recursively call ourselves on the
1649 vars = vare = nextexp + 2;
1653 /* Find the end of it */
1654 while (brackets && *vare) {
1655 if ((vare[0] == '$') && (vare[1] == '[')) {
1659 } else if (vare[0] == '[') {
1661 } else if (vare[0] == ']') {
1663 } else if ((vare[0] == '$') && (vare[1] == '{')) {
1670 ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
1671 len = vare - vars - 1;
1673 /* Skip totally over expression */
1674 whereweare += (len + 3);
1677 var = alloca(VAR_BUF_SIZE);
1679 /* Store variable name (and truncate) */
1680 ast_copy_string(var, vars, len + 1);
1682 /* Substitute if necessary */
1685 ltmp = alloca(VAR_BUF_SIZE);
1687 memset(ltmp, 0, VAR_BUF_SIZE);
1688 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1694 length = ast_expr(vars, cp2, count, c);
1697 ast_debug(1, "Expression result is '%s'\n", cp2);
1706 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
1708 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
1711 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
1713 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
1716 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
1718 memset(passdata, 0, datalen);
1720 /* No variables or expressions in e->data, so why scan it? */
1721 if (e->data && !strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
1722 ast_copy_string(passdata, e->data, datalen);
1726 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
1729 /*! \brief The return value depends on the action:
1731 * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
1732 * and return 0 on failure, -1 on match;
1733 * E_FINDLABEL maps the label to a priority, and returns
1734 * the priority on success, ... XXX
1735 * E_SPAWN, spawn an application,
1736 * and return 0 on success, -1 on failure.
1738 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
1739 const char *context, const char *exten, int priority,
1740 const char *label, const char *callerid, enum ext_match_t action)
1742 struct ast_exten *e;
1743 struct ast_app *app;
1745 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
1746 char passdata[EXT_DATA_SIZE];
1748 int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
1750 ast_rdlock_contexts();
1751 e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
1753 if (matching_action) {
1754 ast_unlock_contexts();
1755 return -1; /* success, we found it */
1756 } else if (action == E_FINDLABEL) { /* map the label to a priority */
1758 ast_unlock_contexts();
1759 return res; /* the priority we were looking for */
1760 } else { /* spawn */
1762 e->cached_app = pbx_findapp(e->app);
1763 app = e->cached_app;
1764 ast_unlock_contexts();
1766 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
1769 if (c->context != context)
1770 ast_copy_string(c->context, context, sizeof(c->context));
1771 if (c->exten != exten)
1772 ast_copy_string(c->exten, exten, sizeof(c->exten));
1773 c->priority = priority;
1774 pbx_substitute_variables(passdata, sizeof(passdata), c, e);
1777 char atmp2[EXT_DATA_SIZE+100];
1778 ast_debug(1, "Launching '%s'\n", app->name);
1779 snprintf(atmp, sizeof(atmp), "STACK-%s-%s-%d", context, exten, priority);
1780 snprintf(atmp2, sizeof(atmp2), "%s(\"%s\", \"%s\") %s",
1781 app->name, c->name, passdata, "in new stack");
1782 pbx_builtin_setvar_helper(c, atmp, atmp2);
1784 if (option_verbose > 2) {
1785 char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
1786 ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
1787 exten, context, priority,
1788 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
1789 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
1790 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
1793 manager_event(EVENT_FLAG_CALL, "Newexten",
1798 "Application: %s\r\n"
1801 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
1802 return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
1804 } else if (q.swo) { /* not found here, but in another switch */
1805 ast_unlock_contexts();
1806 if (matching_action)
1810 ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
1813 return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
1815 } else { /* not found anywhere, see what happened */
1816 ast_unlock_contexts();
1818 case STATUS_NO_CONTEXT:
1819 if (!matching_action)
1820 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
1822 case STATUS_NO_EXTENSION:
1823 if (!matching_action)
1824 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
1826 case STATUS_NO_PRIORITY:
1827 if (!matching_action)
1828 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
1830 case STATUS_NO_LABEL:
1832 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
1835 ast_debug(1, "Shouldn't happen!\n");
1838 return (matching_action) ? 0 : -1;
1842 /*! \brief ast_hint_extension: Find hint for given extension in context */
1843 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
1845 struct ast_exten *e;
1846 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
1848 ast_rdlock_contexts();
1849 e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
1850 ast_unlock_contexts();
1855 /*! \brief ast_extensions_state2: Check state of extension by using hints */
1856 static int ast_extension_state2(struct ast_exten *e)
1858 char hint[AST_MAX_EXTENSION];
1860 int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
1861 int busy = 0, inuse = 0, ring = 0;
1866 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
1868 rest = hint; /* One or more devices separated with a & character */
1869 while ( (cur = strsep(&rest, "&")) ) {
1870 int res = ast_device_state(cur);
1872 case AST_DEVICE_NOT_INUSE:
1877 case AST_DEVICE_INUSE:
1883 case AST_DEVICE_RINGING:
1889 case AST_DEVICE_RINGINUSE:
1896 case AST_DEVICE_ONHOLD:
1900 case AST_DEVICE_BUSY:
1906 case AST_DEVICE_UNAVAILABLE:
1907 case AST_DEVICE_INVALID:
1921 return AST_EXTENSION_RINGING;
1923 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
1925 return AST_EXTENSION_INUSE;
1927 return AST_EXTENSION_NOT_INUSE;
1929 return AST_EXTENSION_ONHOLD;
1931 return AST_EXTENSION_BUSY;
1933 return AST_EXTENSION_UNAVAILABLE;
1935 return AST_EXTENSION_INUSE;
1937 return AST_EXTENSION_NOT_INUSE;
1940 /*! \brief ast_extension_state2str: Return extension_state as string */
1941 const char *ast_extension_state2str(int extension_state)
1945 for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
1946 if (extension_states[i].extension_state == extension_state)
1947 return extension_states[i].text;
1952 /*! \brief ast_extension_state: Check extension state for an extension by using hint */
1953 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
1955 struct ast_exten *e;
1957 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
1959 return -1; /* No hint, return -1 */
1961 return ast_extension_state2(e); /* Check all devices in the hint */
1964 static void handle_statechange(const char *device)
1966 struct ast_hint *hint;
1968 AST_RWLIST_RDLOCK(&hints);
1970 AST_RWLIST_TRAVERSE(&hints, hint, list) {
1971 struct ast_state_cb *cblist;
1972 char buf[AST_MAX_EXTENSION];
1977 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
1978 while ( (cur = strsep(&parse, "&")) ) {
1979 if (!strcasecmp(cur, device))
1985 /* Get device state for this hint */
1986 state = ast_extension_state2(hint->exten);
1988 if ((state == -1) || (state == hint->laststate))
1991 /* Device state changed since last check - notify the watchers */
1993 /* For general callbacks */
1994 for (cblist = statecbs; cblist; cblist = cblist->next)
1995 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
1997 /* For extension callbacks */
1998 for (cblist = hint->callbacks; cblist; cblist = cblist->next)
1999 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2001 hint->laststate = state; /* record we saw the change */
2004 AST_RWLIST_UNLOCK(&hints);
2007 static int statechange_queue(const char *dev)
2009 struct statechange *sc;
2011 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
2014 strcpy(sc->dev, dev);
2016 ast_mutex_lock(&device_state.lock);
2017 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
2018 ast_cond_signal(&device_state.cond);
2019 ast_mutex_unlock(&device_state.lock);
2024 static void *device_state_thread(void *data)
2026 struct statechange *sc;
2028 while (!device_state.stop) {
2029 ast_mutex_lock(&device_state.lock);
2030 while (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
2031 ast_cond_wait(&device_state.cond, &device_state.lock);
2032 /* Check to see if we were woken up to see the request to stop */
2033 if (device_state.stop) {
2034 ast_mutex_unlock(&device_state.lock);
2038 ast_mutex_unlock(&device_state.lock);
2040 handle_statechange(sc->dev);
2048 /*! \brief ast_extension_state_add: Add watcher for extension states */
2049 int ast_extension_state_add(const char *context, const char *exten,
2050 ast_state_cb_type callback, void *data)
2052 struct ast_hint *hint;
2053 struct ast_state_cb *cblist;
2054 struct ast_exten *e;
2056 /* If there's no context and extension: add callback to statecbs list */
2057 if (!context && !exten) {
2058 AST_RWLIST_WRLOCK(&hints);
2060 for (cblist = statecbs; cblist; cblist = cblist->next) {
2061 if (cblist->callback == callback) {
2062 cblist->data = data;
2063 AST_RWLIST_UNLOCK(&hints);
2068 /* Now insert the callback */
2069 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2070 AST_RWLIST_UNLOCK(&hints);
2074 cblist->callback = callback;
2075 cblist->data = data;
2077 cblist->next = statecbs;
2080 AST_RWLIST_UNLOCK(&hints);
2084 if (!context || !exten)
2087 /* This callback type is for only one hint, so get the hint */
2088 e = ast_hint_extension(NULL, context, exten);
2093 /* Find the hint in the list of hints */
2094 AST_RWLIST_WRLOCK(&hints);
2096 AST_RWLIST_TRAVERSE(&hints, hint, list) {
2097 if (hint->exten == e)
2102 /* We have no hint, sorry */
2103 AST_RWLIST_UNLOCK(&hints);
2107 /* Now insert the callback in the callback list */
2108 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2109 AST_RWLIST_UNLOCK(&hints);
2112 cblist->id = stateid++; /* Unique ID for this callback */
2113 cblist->callback = callback; /* Pointer to callback routine */
2114 cblist->data = data; /* Data for the callback */
2116 cblist->next = hint->callbacks;
2117 hint->callbacks = cblist;
2119 AST_RWLIST_UNLOCK(&hints);
2123 /*! \brief ast_extension_state_del: Remove a watcher from the callback list */
2124 int ast_extension_state_del(int id, ast_state_cb_type callback)
2126 struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
2129 if (!id && !callback)
2132 AST_RWLIST_WRLOCK(&hints);
2134 if (!id) { /* id == 0 is a callback without extension */
2135 for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
2136 if ((*p_cur)->callback == callback)
2139 } else { /* callback with extension, find the callback based on ID */
2140 struct ast_hint *hint;
2141 AST_RWLIST_TRAVERSE(&hints, hint, list) {
2142 for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
2143 if ((*p_cur)->id == id)
2146 if (*p_cur) /* found in the inner loop */
2150 if (p_cur && *p_cur) {
2151 struct ast_state_cb *cur = *p_cur;
2156 AST_RWLIST_UNLOCK(&hints);
2160 /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
2161 static int ast_add_hint(struct ast_exten *e)
2163 struct ast_hint *hint;
2168 AST_RWLIST_WRLOCK(&hints);
2170 /* Search if hint exists, do nothing */
2171 AST_RWLIST_TRAVERSE(&hints, hint, list) {
2172 if (hint->exten == e) {
2173 AST_RWLIST_UNLOCK(&hints);
2174 ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2179 ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2181 if (!(hint = ast_calloc(1, sizeof(*hint)))) {
2182 AST_RWLIST_UNLOCK(&hints);
2185 /* Initialize and insert new item at the top */
2187 hint->laststate = ast_extension_state2(e);
2188 AST_RWLIST_INSERT_HEAD(&hints, hint, list);
2190 AST_RWLIST_UNLOCK(&hints);
2194 /*! \brief ast_change_hint: Change hint for an extension */
2195 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
2197 struct ast_hint *hint;
2200 AST_RWLIST_WRLOCK(&hints);
2201 AST_RWLIST_TRAVERSE(&hints, hint, list) {
2202 if (hint->exten == oe) {
2208 AST_RWLIST_UNLOCK(&hints);
2213 /*! \brief ast_remove_hint: Remove hint from extension */
2214 static int ast_remove_hint(struct ast_exten *e)
2216 /* Cleanup the Notifys if hint is removed */
2217 struct ast_hint *hint;
2218 struct ast_state_cb *cblist, *cbprev;
2224 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
2225 if (hint->exten == e) {
2227 cblist = hint->callbacks;
2229 /* Notify with -1 and remove all callbacks */
2231 cblist = cblist->next;
2232 cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
2235 hint->callbacks = NULL;
2236 AST_RWLIST_REMOVE_CURRENT(&hints, list);
2242 AST_RWLIST_TRAVERSE_SAFE_END
2248 /*! \brief ast_get_hint: Get hint for channel */
2249 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
2251 struct ast_exten *e = ast_hint_extension(c, context, exten);
2255 ast_copy_string(hint, ast_get_extension_app(e), hintsize);
2257 const char *tmp = ast_get_extension_app_data(e);
2259 ast_copy_string(name, tmp, namesize);
2266 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2268 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH);
2271 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
2273 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL);
2276 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
2278 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
2281 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2283 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH);
2286 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2288 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE);
2291 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2293 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN);
2296 /* helper function to set extension and priority */
2297 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
2299 ast_copy_string(c->exten, exten, sizeof(c->exten));
2304 * \brief collect digits from the channel into the buffer,
2305 * return -1 on error, 0 on timeout or done.
2307 static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
2311 buf[pos] = '\0'; /* make sure it is properly terminated */
2312 while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
2313 /* As long as we're willing to wait, and as long as it's not defined,
2314 keep reading digits until we can't possibly get a right answer anymore. */
2315 digit = ast_waitfordigit(c, waittime * 1000);
2316 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2319 if (!digit) /* No entry */
2321 if (digit < 0) /* Error, maybe a hangup */
2323 if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
2327 waittime = c->pbx->dtimeout;
2333 static int __ast_pbx_run(struct ast_channel *c)
2335 int found = 0; /* set if we find at least one match */
2338 int error = 0; /* set an error conditions */
2340 /* A little initial setup here */
2342 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
2343 /* XXX and now what ? */
2346 if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
2350 c->cdr = ast_cdr_alloc();
2352 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
2356 ast_cdr_init(c->cdr, c);
2359 /* Set reasonable defaults */
2360 c->pbx->rtimeout = 10;
2361 c->pbx->dtimeout = 5;
2363 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
2364 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
2366 /* Start by trying whatever the channel is set to */
2367 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2368 /* If not successful fall back to 's' */
2369 ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
2370 /* XXX the original code used the existing priority in the call to
2371 * ast_exists_extension(), and reset it to 1 afterwards.
2372 * I believe the correct thing is to set it to 1 immediately.
2374 set_ext_pri(c, "s", 1);
2375 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2376 /* JK02: And finally back to default if everything else failed */
2377 ast_verb(2, "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
2378 ast_copy_string(c->context, "default", sizeof(c->context));
2381 if (c->cdr && ast_tvzero(c->cdr->start))
2382 ast_cdr_start(c->cdr);
2384 char dst_exten[256]; /* buffer to accumulate digits */
2385 int pos = 0; /* XXX should check bounds */
2388 /* loop on priorities in this context/exten */
2389 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2391 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2392 /* Something bad happened, or a hangup has been requested. */
2393 if (strchr("0123456789ABCDEF*#", res)) {
2394 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
2396 dst_exten[pos++] = digit = res;
2397 dst_exten[pos] = '\0';
2400 if (res == AST_PBX_KEEPALIVE) {
2401 ast_debug(1, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2402 ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2406 ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2407 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2408 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2410 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2411 /* atimeout, nothing bad */
2419 if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
2420 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
2421 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
2422 c->whentohangup = 0;
2423 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
2424 } else if (c->_softhangup) {
2425 ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
2426 c->exten, c->priority);
2431 } /* end while - from here on we can use 'break' to go out */
2435 /* XXX we get here on non-existing extension or a keypress or hangup ? */
2437 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
2438 /* If there is no match at priority 1, it is not a valid extension anymore.
2439 * Try to continue at "i", 1 or exit if the latter does not exist.
2441 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2442 ast_verb(3, "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
2443 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
2444 set_ext_pri(c, "i", 1);
2446 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
2447 c->name, c->exten, c->context);
2448 error = 1; /* we know what to do with it */
2451 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2452 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
2454 } else { /* keypress received, get more digits for a full extension */
2457 waittime = c->pbx->dtimeout;
2458 else if (!autofallthrough)
2459 waittime = c->pbx->rtimeout;
2461 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
2464 ast_verb(3, "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
2465 if (!strcasecmp(status, "CONGESTION"))
2466 res = pbx_builtin_congestion(c, "10");
2467 else if (!strcasecmp(status, "CHANUNAVAIL"))
2468 res = pbx_builtin_congestion(c, "10");
2469 else if (!strcasecmp(status, "BUSY"))
2470 res = pbx_builtin_busy(c, "10");
2471 error = 1; /* XXX disable message */
2472 break; /* exit from the 'for' loop */
2475 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
2477 if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
2478 set_ext_pri(c, dst_exten, 1);
2480 /* No such extension */
2481 if (!ast_strlen_zero(dst_exten)) {
2482 /* An invalid extension */
2483 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2484 ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
2485 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
2486 set_ext_pri(c, "i", 1);
2488 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
2489 found = 1; /* XXX disable message */
2493 /* A simple timeout */
2494 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
2495 ast_verb(3, "Timeout on %s\n", c->name);
2496 set_ext_pri(c, "t", 1);
2498 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
2499 found = 1; /* XXX disable message */
2505 ast_verb(2, "CDR updated on %s\n",c->name);
2510 if (!found && !error)
2511 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
2512 if (res != AST_PBX_KEEPALIVE)
2513 ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
2514 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
2515 if (c->cdr && ast_opt_end_cdr_before_h_exten)
2516 ast_cdr_end(c->cdr);
2517 set_ext_pri(c, "h", 1);
2518 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2519 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2520 /* Something bad happened, or a hangup has been requested. */
2521 ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2522 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2528 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
2530 pbx_destroy(c->pbx);
2532 if (res != AST_PBX_KEEPALIVE)
2537 /* Returns 0 on success, non-zero if a configured limit (maxcalls, maxload, minmemfree) was reached */
2538 static int increase_call_count(const struct ast_channel *c)
2542 #if defined(HAVE_SYSINFO)
2544 struct sysinfo sys_info;
2547 ast_mutex_lock(&maxcalllock);
2548 if (option_maxcalls) {
2549 if (countcalls >= option_maxcalls) {
2550 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
2554 if (option_maxload) {
2555 getloadavg(&curloadavg, 1);
2556 if (curloadavg >= option_maxload) {
2557 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
2561 #if defined(HAVE_SYSINFO)
2562 if (option_minmemfree) {
2563 if (!sysinfo(&sys_info)) {
2564 /* make sure that the free system memory is above the configured low watermark
2565 * convert the amount of freeram from mem_units to MB */
2566 curfreemem = sys_info.freeram / sys_info.mem_unit;
2567 curfreemem /= 1024*1024;
2568 if (curfreemem < option_minmemfree) {
2569 ast_log(LOG_WARNING, "Available system memory (~%ldMB) is below the configured low watermark (%ldMB)\n", curfreemem, option_minmemfree);
2578 ast_mutex_unlock(&maxcalllock);
2583 static void decrease_call_count(void)
2585 ast_mutex_lock(&maxcalllock);
2588 ast_mutex_unlock(&maxcalllock);
2591 static void destroy_exten(struct ast_exten *e)
2593 if (e->priority == PRIORITY_HINT)
2601 static void *pbx_thread(void *data)
2603 /* Oh joyeous kernel, we're a new thread, with nothing to do but
2604 answer this channel and get it going.
2607 The launcher of this function _MUST_ increment 'countcalls'
2608 before invoking the function; it will be decremented when the
2609 PBX has finished running on the channel
2611 struct ast_channel *c = data;
2614 decrease_call_count();
2621 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
2626 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
2627 return AST_PBX_FAILED;
2630 if (increase_call_count(c))
2631 return AST_PBX_CALL_LIMIT;
2633 /* Start a new thread, and get something handling this channel. */
2634 if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
2635 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
2636 return AST_PBX_FAILED;
2639 return AST_PBX_SUCCESS;
2642 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
2644 enum ast_pbx_result res = AST_PBX_SUCCESS;
2646 if (increase_call_count(c))
2647 return AST_PBX_CALL_LIMIT;
2649 res = __ast_pbx_run(c);
2650 decrease_call_count();
2655 int ast_active_calls(void)
2660 int pbx_set_autofallthrough(int newval)
2662 int oldval = autofallthrough;
2663 autofallthrough = newval;
2667 /* lookup for a context with a given name,
2668 * return with conlock held if found, NULL if not found
2670 static struct ast_context *find_context_locked(const char *context)
2672 struct ast_context *c = NULL;
2674 ast_rdlock_contexts();
2675 while ( (c = ast_walk_contexts(c)) ) {
2676 if (!strcmp(ast_get_context_name(c), context))
2679 ast_unlock_contexts();
2685 * This function locks contexts list by &conlist, search for the right context
2686 * structure, leave context list locked and call ast_context_remove_include2
2687 * which removes include, unlock contexts list and return ...
2689 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
2692 struct ast_context *c = find_context_locked(context);
2695 /* found, remove include from this context ... */
2696 ret = ast_context_remove_include2(c, include, registrar);
2697 ast_unlock_contexts();
2703 * When we call this function, &conlock lock must be locked, because when
2704 * we giving *con argument, some process can remove/change this context
2705 * and after that there can be segfault.
2707 * This function locks given context, removes include, unlock context and
2710 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
2712 struct ast_include *i, *pi = NULL;
2715 ast_wrlock_context(con);
2717 /* find our include */
2718 for (i = con->includes; i; pi = i, i = i->next) {
2719 if (!strcmp(i->name, include) &&
2720 (!registrar || !strcmp(i->registrar, registrar))) {
2721 /* remove from list */
2725 con->includes = i->next;
2726 /* free include and return */
2733 ast_unlock_context(con);
2739 * \note This function locks contexts list by &conlist, search for the rigt context
2740 * structure, leave context list locked and call ast_context_remove_switch2
2741 * which removes switch, unlock contexts list and return ...
2743 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
2745 int ret = -1; /* default error return */
2746 struct ast_context *c = find_context_locked(context);
2749 /* remove switch from this context ... */
2750 ret = ast_context_remove_switch2(c, sw, data, registrar);
2751 ast_unlock_contexts();
2757 * \brief This function locks given context, removes switch, unlock context and
2759 * \note When we call this function, &conlock lock must be locked, because when
2760 * we giving *con argument, some process can remove/change this context
2761 * and after that there can be segfault.
2764 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
2769 ast_wrlock_context(con);
2772 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
2773 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
2774 (!registrar || !strcmp(i->registrar, registrar))) {
2775 /* found, remove from list */
2776 AST_LIST_REMOVE_CURRENT(&con->alts, list);
2777 ast_free(i); /* free switch and return */
2782 AST_LIST_TRAVERSE_SAFE_END
2784 ast_unlock_context(con);
2790 * \note This functions lock contexts list, search for the right context,
2791 * call ast_context_remove_extension2, unlock contexts list and return.
2792 * In this function we are using
2794 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
2796 int ret = -1; /* default error return */
2797 struct ast_context *c = find_context_locked(context);
2799 if (c) { /* ... remove extension ... */
2800 ret = ast_context_remove_extension2(c, extension, priority, registrar);
2801 ast_unlock_contexts();
2807 * \brief This functionc locks given context, search for the right extension and
2808 * fires out all peer in this extensions with given priority. If priority
2809 * is set to 0, all peers are removed. After that, unlock context and
2811 * \note When do you want to call this function, make sure that &conlock is locked,
2812 * because some process can handle with your *con context before you lock
2816 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
2818 struct ast_exten *exten, *prev_exten = NULL;
2819 struct ast_exten *peer;
2821 ast_wrlock_context(con);
2823 /* scan the extension list to find matching extension-registrar */
2824 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
2825 if (!strcmp(exten->exten, extension) &&
2826 (!registrar || !strcmp(exten->registrar, registrar)))
2830 /* we can't find right extension */
2831 ast_unlock_context(con);
2835 /* should we free all peers in this extension? (priority == 0)? */
2836 if (priority == 0) {
2837 /* remove this extension from context list */
2839 prev_exten->next = exten->next;
2841 con->root = exten->next;
2843 /* fire out all peers */
2844 while ( (peer = exten) ) {
2845 exten = peer->peer; /* prepare for next entry */
2846 destroy_exten(peer);
2849 /* scan the priority list to remove extension with exten->priority == priority */
2850 struct ast_exten *previous_peer = NULL;
2852 for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
2853 if (peer->priority == priority &&
2854 (!registrar || !strcmp(peer->registrar, registrar) ))
2855 break; /* found our priority */
2857 if (!peer) { /* not found */
2858 ast_unlock_context(con);
2861 /* we are first priority extension? */
2862 if (!previous_peer) {
2864 * We are first in the priority chain, so must update the extension chain.
2865 * The next node is either the next priority or the next extension
2867 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
2869 if (!prev_exten) /* change the root... */
2870 con->root = next_node;
2872 prev_exten->next = next_node; /* unlink */
2873 if (peer->peer) /* XXX update the new head of the pri list */
2874 peer->peer->next = peer->next;
2875 } else { /* easy, we are not first priority in extension */
2876 previous_peer->peer = peer->peer;
2879 /* now, free whole priority extension */
2880 destroy_exten(peer);
2881 /* XXX should we return -1 ? */
2883 ast_unlock_context(con);
2889 * \note This function locks contexts list by &conlist, searches for the right context
2890 * structure, and locks the macrolock mutex in that context.
2891 * macrolock is used to limit a macro to be executed by one call at a time.
2893 int ast_context_lockmacro(const char *context)
2895 struct ast_context *c = NULL;
2898 ast_rdlock_contexts();
2900 while ((c = ast_walk_contexts(c))) {
2901 if (!strcmp(ast_get_context_name(c), context)) {
2907 ast_unlock_contexts();
2909 /* if we found context, lock macrolock */
2911 ret = ast_mutex_lock(&c->macrolock);
2917 * \note This function locks contexts list by &conlist, searches for the right context
2918 * structure, and unlocks the macrolock mutex in that context.
2919 * macrolock is used to limit a macro to be executed by one call at a time.
2921 int ast_context_unlockmacro(const char *context)
2923 struct ast_context *c = NULL;
2926 ast_rdlock_contexts();
2928 while ((c = ast_walk_contexts(c))) {
2929 if (!strcmp(ast_get_context_name(c), context)) {
2935 ast_unlock_contexts();
2937 /* if we found context, unlock macrolock */
2939 ret = ast_mutex_unlock(&c->macrolock);
2944 /*! \brief Dynamically register a new dial plan application */
2945 int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description, void *mod)
2947 struct ast_app *tmp, *cur = NULL;
2951 AST_RWLIST_WRLOCK(&apps);
2952 AST_RWLIST_TRAVERSE(&apps, tmp, list) {
2953 if (!(res = strcasecmp(app, tmp->name))) {
2954 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
2955 AST_RWLIST_UNLOCK(&apps);
2961 length = sizeof(*tmp) + strlen(app) + 1;
2963 if (!(tmp = ast_calloc(1, length))) {
2964 AST_RWLIST_UNLOCK(&apps);
2968 strcpy(tmp->name, app);
2969 tmp->execute = execute;
2970 tmp->synopsis = synopsis;
2971 tmp->description = description;
2974 /* Store in alphabetical order */
2975 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
2976 if (strcasecmp(tmp->name, cur->name) < 0) {
2977 AST_RWLIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
2981 AST_RWLIST_TRAVERSE_SAFE_END
2983 AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
2985 ast_verb(2, "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
2987 AST_RWLIST_UNLOCK(&apps);
2993 * Append to the list. We don't have a tail pointer because we need
2994 * to scan the list anyways to check for duplicates during insertion.
2996 int ast_register_switch(struct ast_switch *sw)
2998 struct ast_switch *tmp;
3000 AST_RWLIST_WRLOCK(&switches);
3001 AST_RWLIST_TRAVERSE(&switches, tmp, list) {
3002 if (!strcasecmp(tmp->name, sw->name)) {
3003 AST_RWLIST_UNLOCK(&switches);
3004 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
3008 AST_RWLIST_INSERT_TAIL(&switches, sw, list);
3009 AST_RWLIST_UNLOCK(&switches);
3014 void ast_unregister_switch(struct ast_switch *sw)
3016 AST_RWLIST_WRLOCK(&switches);
3017 AST_RWLIST_REMOVE(&switches, sw, list);
3018 AST_RWLIST_UNLOCK(&switches);
3022 * Help for CLI commands ...
3024 static char show_applications_help[] =
3025 "Usage: core show applications [{like|describing} <text>]\n"
3026 " List applications which are currently available.\n"
3027 " If 'like', <text> will be a substring of the app name\n"
3028 " If 'describing', <text> will be a substring of the description\n";
3030 static char show_functions_help[] =
3031 "Usage: core show functions [like <text>]\n"
3032 " List builtin functions, optionally only those matching a given string\n";
3034 static char show_switches_help[] =
3035 "Usage: core show switches\n"
3036 " List registered switches\n";
3038 static char show_hints_help[] =
3039 "Usage: core show hints\n"
3040 " List registered hints\n";
3042 static char show_globals_help[] =
3043 "Usage: core show globals\n"
3044 " List current global dialplan variables and their values\n";
3046 static char show_application_help[] =
3047 "Usage: core show application <application> [<application> [<application> [...]]]\n"
3048 " Describes a particular application.\n";
3050 static char show_function_help[] =
3051 "Usage: core show function <function>\n"
3052 " Describe a particular dialplan function.\n";
3054 static char show_dialplan_help[] =
3055 "Usage: core show dialplan [exten@][context]\n"
3058 static char set_global_help[] =
3059 "Usage: core set global <name> <value>\n"
3060 " Set global dialplan variable <name> to <value>\n";
3064 * \brief 'show application' CLI command implementation functions ...
3068 * There is a possibility to show informations about more than one
3069 * application at one time. You can type 'show application Dial Echo' and
3070 * you will see informations about these two applications ...
3072 static char *complete_show_application(const char *line, const char *word, int pos, int state)
3077 int wordlen = strlen(word);
3079 /* return the n-th [partial] matching entry */
3080 AST_RWLIST_RDLOCK(&apps);
3081 AST_RWLIST_TRAVERSE(&apps, a, list) {
3082 if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
3083 ret = ast_strdup(a->name);
3087 AST_RWLIST_UNLOCK(&apps);
3092 static int handle_show_application(int fd, int argc, char *argv[])
3095 int app, no_registered_app = 1;
3098 return RESULT_SHOWUSAGE;
3100 /* ... go through all applications ... */
3101 AST_RWLIST_RDLOCK(&apps);
3102 AST_RWLIST_TRAVERSE(&apps, a, list) {
3103 /* ... compare this application name with all arguments given
3104 * to 'show application' command ... */
3105 for (app = 3; app < argc; app++) {
3106 if (!strcasecmp(a->name, argv[app])) {
3107 /* Maximum number of characters added by terminal coloring is 22 */
3108 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3109 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3110 int synopsis_size, description_size;
3112 no_registered_app = 0;
3115 synopsis_size = strlen(a->synopsis) + 23;
3117 synopsis_size = strlen("Not available") + 23;
3118 synopsis = alloca(synopsis_size);
3121 description_size = strlen(a->description) + 23;
3123 description_size = strlen("Not available") + 23;
3124 description = alloca(description_size);
3126 if (synopsis && description) {
3127 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3128 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3129 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3130 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3131 term_color(synopsis,
3132 a->synopsis ? a->synopsis : "Not available",
3133 COLOR_CYAN, 0, synopsis_size);
3134 term_color(description,
3135 a->description ? a->description : "Not available",
3136 COLOR_CYAN, 0, description_size);
3138 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);