Newer
Older
/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
static int ast_add_hint(struct ast_exten *e)
{
Russell Bryant
committed
struct ast_hint *hint;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
/* Search if hint exists, do nothing */
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == e) {
AST_LIST_UNLOCK(&hints);
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
Russell Bryant
committed
if (!(hint = ast_calloc(1, sizeof(*hint)))) {
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/* Initialize and insert new item at the top */
Russell Bryant
committed
hint->exten = e;
hint->laststate = ast_extension_state2(e);
AST_LIST_INSERT_HEAD(&hints, hint, list);
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/*! \brief ast_change_hint: Change hint for an extension */
static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
Russell Bryant
committed
struct ast_hint *hint;
int res = -1;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
AST_LIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == oe) {
hint->exten = ne;
res = 0;
break;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
Russell Bryant
committed
return res;
/*! \brief ast_remove_hint: Remove hint from extension */
static int ast_remove_hint(struct ast_exten *e)
/* Cleanup the Notifys if hint is removed */
Russell Bryant
committed
struct ast_hint *hint;
struct ast_state_cb *cblist, *cbprev;
Russell Bryant
committed
int res = -1;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
if (hint->exten == e) {
Russell Bryant
committed
cblist = hint->callbacks;
while (cblist) {
/* Notify with -1 and remove all callbacks */
Russell Bryant
committed
cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
Russell Bryant
committed
hint->callbacks = NULL;
AST_LIST_REMOVE_CURRENT(&hints, list);
free(hint);
res = 0;
Russell Bryant
committed
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK(&hints);
Russell Bryant
committed
return res;
/*! \brief ast_get_hint: Get hint for channel */
int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
struct ast_exten *e = ast_hint_extension(c, context, exten);
ast_copy_string(hint, ast_get_extension_app(e), hintsize);
const char *tmp = ast_get_extension_app_data(e);
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH);
int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL);
int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH);
int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE);
int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN);
/* helper function to set extension and priority */
static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
{
ast_copy_string(c->exten, exten, sizeof(c->exten));
c->priority = pri;
}
/*!
* \brief collect digits from the channel into the buffer,
* return -1 on error, 0 on timeout or done.
*/
static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
Kevin P. Fleming
committed
int digit;
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
buf[pos] = '\0'; /* make sure it is properly terminated */
while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
/* As long as we're willing to wait, and as long as it's not defined,
keep reading digits until we can't possibly get a right answer anymore. */
digit = ast_waitfordigit(c, waittime * 1000);
if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
c->_softhangup = 0;
} else {
if (!digit) /* No entry */
break;
if (digit < 0) /* Error, maybe a hangup */
return -1;
if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
buf[pos++] = digit;
buf[pos] = '\0';
}
waittime = c->pbx->dtimeout;
}
}
return 0;
}
static int __ast_pbx_run(struct ast_channel *c)
{
int found = 0; /* set if we find at least one match */
int res = 0;
Kevin P. Fleming
committed
int autoloopflag;
int error = 0; /* set an error conditions */
ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
/* XXX and now what ? */
free(c->pbx);
}
Russell Bryant
committed
if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
c->cdr = ast_cdr_alloc();
if (!c->cdr) {
ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
free(c->pbx);
return -1;
}
ast_cdr_init(c->cdr, c);
}
}
/* Set reasonable defaults */
c->pbx->rtimeout = 10;
c->pbx->dtimeout = 5;
autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
Kevin P. Fleming
committed
ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
/* Start by trying whatever the channel is set to */
if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
Kevin P. Fleming
committed
/* If not successful fall back to 's' */
if (option_verbose > 1)
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);
/* XXX the original code used the existing priority in the call to
* ast_exists_extension(), and reset it to 1 afterwards.
* I believe the correct thing is to set it to 1 immediately.
*/
set_ext_pri(c, "s", 1);
if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
/* JK02: And finally back to default if everything else failed */
if (option_verbose > 1)
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);
ast_copy_string(c->context, "default", sizeof(c->context));
if (c->cdr && ast_tvzero(c->cdr->start))
char dst_exten[256]; /* buffer to accumulate digits */
int pos = 0; /* XXX should check bounds */
int digit = 0;
/* loop on priorities in this context/exten */
while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
/* Something bad happened, or a hangup has been requested. */
if (strchr("0123456789ABCDEF*#", res)) {
ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
dst_exten[pos++] = digit = res;
dst_exten[pos] = '\0';
if (res == AST_PBX_KEEPALIVE) {
if (option_debug)
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
}
if (option_debug)
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
c->_softhangup =0;
} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
/* atimeout, nothing bad */
} else {
if (c->cdr)
ast_cdr_update(c);
if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
/* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
c->whentohangup = 0;
ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
} /* end while - from here on we can use 'break' to go out */
if (error)
break;
/* XXX we get here on non-existing extension or a keypress or hangup ? */
if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
/* If there is no match at priority 1, it is not a valid extension anymore.
* Try to continue at "i", 1 or exit if the latter does not exist.
*/
if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
set_ext_pri(c, "i", 1);
} else {
ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
c->name, c->exten, c->context);
error = 1; /* we know what to do with it */
break;
} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
/* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
c->_softhangup = 0;
} else { /* keypress received, get more digits for a full extension */
int waittime = 0;
if (!waittime) {
const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
if (!status)
status = "UNKNOWN";
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
if (!strcasecmp(status, "CONGESTION"))
res = pbx_builtin_congestion(c, "10");
else if (!strcasecmp(status, "CHANUNAVAIL"))
res = pbx_builtin_congestion(c, "10");
else if (!strcasecmp(status, "BUSY"))
res = pbx_builtin_busy(c, "10");
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
error = 1; /* XXX disable message */
break; /* exit from the 'for' loop */
}
if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
break;
if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
set_ext_pri(c, dst_exten, 1);
else {
/* No such extension */
if (!ast_strlen_zero(dst_exten)) {
/* An invalid extension */
if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
set_ext_pri(c, "i", 1);
} else {
ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
found = 1; /* XXX disable message */
break;
}
} else {
/* A simple timeout */
if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
set_ext_pri(c, "t", 1);
} else {
ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
found = 1; /* XXX disable message */
break;
}
}
}
if (c->cdr) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
ast_cdr_update(c);
ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
if (c->cdr && ast_opt_end_cdr_before_h_exten)
ast_cdr_end(c->cdr);
set_ext_pri(c, "h", 1);
while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
/* Something bad happened, or a hangup has been requested. */
if (option_debug)
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
break;
}
c->priority++;
}
}
Kevin P. Fleming
committed
ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
if (res != AST_PBX_KEEPALIVE)
ast_hangup(c);
Kevin P. Fleming
committed
/* Returns 0 on success, non-zero if call limit was reached */
static int increase_call_count(const struct ast_channel *c)
{
int failed = 0;
double curloadavg;
Kevin P. Fleming
committed
ast_mutex_lock(&maxcalllock);
if (option_maxcalls) {
if (countcalls >= option_maxcalls) {
ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
failed = -1;
}
}
if (option_maxload) {
getloadavg(&curloadavg, 1);
if (curloadavg >= option_maxload) {
ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
failed = -1;
}
}
Kevin P. Fleming
committed
if (!failed)
Kevin P. Fleming
committed
ast_mutex_unlock(&maxcalllock);
return failed;
}
static void decrease_call_count(void)
{
ast_mutex_lock(&maxcalllock);
if (countcalls > 0)
countcalls--;
ast_mutex_unlock(&maxcalllock);
}
static void destroy_exten(struct ast_exten *e)
{
if (e->priority == PRIORITY_HINT)
ast_remove_hint(e);
if (e->datad)
e->datad(e->data);
free(e);
}
static void *pbx_thread(void *data)
{
/* Oh joyeous kernel, we're a new thread, with nothing to do but
answer this channel and get it going.
*/
Kevin P. Fleming
committed
/* NOTE:
The launcher of this function _MUST_ increment 'countcalls'
before invoking the function; it will be decremented when the
PBX has finished running on the channel
*/
Kevin P. Fleming
committed
__ast_pbx_run(c);
decrease_call_count();
Kevin P. Fleming
committed
Kevin P. Fleming
committed
enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
Kevin P. Fleming
committed
if (!c) {
ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
Kevin P. Fleming
committed
return AST_PBX_FAILED;
Kevin P. Fleming
committed
if (increase_call_count(c))
return AST_PBX_CALL_LIMIT;
/* Start a new thread, and get something handling this channel. */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
ast_log(LOG_WARNING, "Failed to create new channel thread\n");
Kevin P. Fleming
committed
return AST_PBX_FAILED;
Kevin P. Fleming
committed
return AST_PBX_SUCCESS;
Kevin P. Fleming
committed
enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
Kevin P. Fleming
committed
enum ast_pbx_result res = AST_PBX_SUCCESS;
if (increase_call_count(c))
return AST_PBX_CALL_LIMIT;
res = __ast_pbx_run(c);
decrease_call_count();
return res;
}
int ast_active_calls(void)
{
return countcalls;
}
int oldval = autofallthrough;
autofallthrough = newval;
/* lookup for a context with a given name,
* return with conlock held if found, NULL if not found
*/
static struct ast_context *find_context_locked(const char *context)
{
struct ast_context *c = NULL;
ast_lock_contexts();
while ( (c = ast_walk_contexts(c)) ) {
if (!strcmp(ast_get_context_name(c), context))
return c;
}
ast_unlock_contexts();
return NULL;
}
* This function locks contexts list by &conlist, search for the right context
* structure, leave context list locked and call ast_context_remove_include2
* which removes include, unlock contexts list and return ...
*/
int ast_context_remove_include(const char *context, const char *include, const char *registrar)
int ret = -1;
struct ast_context *c = find_context_locked(context);
if (c) {
/* found, remove include from this context ... */
ret = ast_context_remove_include2(c, include, registrar);
ast_unlock_contexts();
}
/*
* When we call this function, &conlock lock must be locked, because when
* we giving *con argument, some process can remove/change this context
* and after that there can be segfault.
*
* This function locks given context, removes include, unlock context and
* return.
*/
int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
ast_mutex_lock(&con->lock);
(!registrar || !strcmp(i->registrar, registrar))) {
/* remove from list */
if (pi)
pi->next = i->next;
else
con->includes = i->next;
/* free include and return */
free(i);
ast_mutex_unlock(&con->lock);
/*!
* \note This function locks contexts list by &conlist, search for the rigt context
* structure, leave context list locked and call ast_context_remove_switch2
* which removes switch, unlock contexts list and return ...
*/
int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
struct ast_context *c = find_context_locked(context);
ret = ast_context_remove_switch2(c, sw, data, registrar);
ast_unlock_contexts();
/*!
* \brief This function locks given context, removes switch, unlock context and
* return.
* \note When we call this function, &conlock lock must be locked, because when
* we giving *con argument, some process can remove/change this context
* and after that there can be segfault.
*
*/
int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
struct ast_sw *i;
ast_mutex_lock(&con->lock);
/* walk switches */
AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
Mark Spencer
committed
(!registrar || !strcmp(i->registrar, registrar))) {
/* found, remove from list */
AST_LIST_REMOVE_CURRENT(&con->alts, list);
free(i); /* free switch and return */
ret = 0;
break;
AST_LIST_TRAVERSE_SAFE_END
ast_mutex_unlock(&con->lock);
* \note This functions lock contexts list, search for the right context,
* call ast_context_remove_extension2, unlock contexts list and return.
* In this function we are using
*/
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
struct ast_context *c = find_context_locked(context);
if (c) { /* ... remove extension ... */
ret = ast_context_remove_extension2(c, extension, priority, registrar);
ast_unlock_contexts();
/*!
* \brief This functionc locks given context, search for the right extension and
* fires out all peer in this extensions with given priority. If priority
* is set to 0, all peers are removed. After that, unlock context and
* return.
* \note When do you want to call this function, make sure that &conlock is locked,
* because some process can handle with your *con context before you lock
* it.
*
int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
{
struct ast_exten *exten, *prev_exten = NULL;
ast_mutex_lock(&con->lock);
/* scan the extension list to find matching extension-registrar */
for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
(!registrar || !strcmp(exten->registrar, registrar)))
break;
}
if (!exten) {
/* we can't find right extension */
ast_mutex_unlock(&con->lock);
return -1;
}
/* should we free all peers in this extension? (priority == 0)? */
if (priority == 0) {
/* remove this extension from context list */
if (prev_exten)
prev_exten->next = exten->next;
else
con->root = exten->next;
/* fire out all peers */
while ( (peer = exten) ) {
exten = peer->peer; /* prepare for next entry */
destroy_exten(peer);
}
} else {
/* scan the priority list to remove extension with exten->priority == priority */
struct ast_exten *previous_peer = NULL;
for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
if (peer->priority == priority &&
(!registrar || !strcmp(peer->registrar, registrar) ))
break; /* found our priority */
}
if (!peer) { /* not found */
ast_mutex_unlock(&con->lock);
return -1;
}
/* we are first priority extension? */
if (!previous_peer) {
/*
* We are first in the priority chain, so must update the extension chain.
* The next node is either the next priority or the next extension
*/
struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
if (!prev_exten) /* change the root... */
con->root = next_node;
else
prev_exten->next = next_node; /* unlink */
if (peer->peer) /* XXX update the new head of the pri list */
peer->peer->next = peer->next;
} else { /* easy, we are not first priority in extension */
previous_peer->peer = peer->peer;
/* now, free whole priority extension */
destroy_exten(peer);
/* XXX should we return -1 ? */
ast_mutex_unlock(&con->lock);
/*! \brief Dynamically register a new dial plan application */
int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
struct ast_app *tmp, *cur = NULL;
AST_LIST_LOCK(&apps);
AST_LIST_TRAVERSE(&apps, tmp, list) {
if (!strcasecmp(app, tmp->name)) {
ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
AST_LIST_UNLOCK(&apps);
length = sizeof(*tmp) + strlen(app) + 1;
Russell Bryant
committed
if (!(tmp = ast_calloc(1, length))) {
AST_LIST_UNLOCK(&apps);
strcpy(tmp->name, app);
tmp->execute = execute;
tmp->synopsis = synopsis;
tmp->description = description;
AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
if (strcasecmp(tmp->name, cur->name) < 0) {
AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_INSERT_TAIL(&apps, tmp, list);
ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
AST_LIST_UNLOCK(&apps);
/*
* Append to the list. We don't have a tail pointer because we need
* to scan the list anyways to check for duplicates during insertion.
*/
struct ast_switch *tmp;
AST_LIST_LOCK(&switches);
AST_LIST_TRAVERSE(&switches, tmp, list) {
if (!strcasecmp(tmp->name, sw->name)) {
AST_LIST_UNLOCK(&switches);
ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
return -1;
}
AST_LIST_INSERT_TAIL(&switches, sw, list);
AST_LIST_UNLOCK(&switches);
void ast_unregister_switch(struct ast_switch *sw)
AST_LIST_LOCK(&switches);
AST_LIST_REMOVE(&switches, sw, list);
AST_LIST_UNLOCK(&switches);
"Usage: show application <application> [<application> [<application> [...]]]\n"
" Describes a particular application.\n";
static char show_functions_help[] =
"Usage: show functions [like <text>]\n"
" List builtin functions, optionally only those matching a given string\n";
static char show_function_help[] =
"Usage: show function <function>\n"
" Describe a particular dialplan function.\n";
"Usage: show applications [{like|describing} <text>]\n"
" List applications which are currently available.\n"
" If 'like', <text> will be a substring of the app name\n"
" If 'describing', <text> will be a substring of the description\n";
static char show_dialplan_help[] =
"Usage: show dialplan [exten@][context]\n"
" Show dialplan\n";
"Usage: show switches\n"
" Show registered switches\n";
"Usage: show hints\n"
" Show registered hints\n";
"Usage: show globals\n"
" Show current global dialplan variables and their values\n";
"Usage: set global <name> <value>\n"
" Set global dialplan variable <name> to <value>\n";
/*
* IMPLEMENTATION OF CLI FUNCTIONS IS IN THE SAME ORDER AS COMMANDS HELPS
*
*/
/*
* \brief 'show application' CLI command implementation functions ...
*/
/*
* There is a possibility to show informations about more than one
* application at one time. You can type 'show application Dial Echo' and
* you will see informations about these two applications ...
*/
Russell Bryant
committed
static char *complete_show_application(const char *line, const char *word, int pos, int state)
char *ret = NULL;
int wordlen = strlen(word);
Russell Bryant
committed
/* return the n-th [partial] matching entry */
AST_LIST_LOCK(&apps);
AST_LIST_TRAVERSE(&apps, a, list) {
Joshua Colp
committed
if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
Russell Bryant
committed
ret = strdup(a->name);
Joshua Colp
committed
break;
}
AST_LIST_UNLOCK(&apps);
}
static int handle_show_application(int fd, int argc, char *argv[])
{
if (argc < 3)
return RESULT_SHOWUSAGE;
AST_LIST_LOCK(&apps);
AST_LIST_TRAVERSE(&apps, a, list) {
/* ... compare this application name with all arguments given
* to 'show application' command ... */
for (app = 2; app < argc; app++) {
if (!strcasecmp(a->name, argv[app])) {
/* Maximum number of characters added by terminal coloring is 22 */
char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
int synopsis_size, description_size;
if (a->synopsis)
synopsis_size = strlen(a->synopsis) + 23;
else
synopsis_size = strlen("Not available") + 23;
synopsis = alloca(synopsis_size);
if (a->description)
description_size = strlen(a->description) + 23;
else
description_size = strlen("Not available") + 23;
description = alloca(description_size);
if (synopsis && description) {
snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
term_color(synopsis,
a->synopsis ? a->synopsis : "Not available",
COLOR_CYAN, 0, synopsis_size);
term_color(description,
a->description ? a->description : "Not available",
COLOR_CYAN, 0, description_size);
ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
} else {
/* ... one of our applications, show info ...*/
ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
"[Synopsis]\n %s\n\n"
"[Description]\n%s\n",
a->name,
a->synopsis ? a->synopsis : "Not available",
a->description ? a->description : "Not available");
AST_LIST_UNLOCK(&apps);
/* we found at least one app? no? */
if (no_registered_app) {
ast_cli(fd, "Your application(s) is (are) not registered\n");
return RESULT_FAILURE;
}
/*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
static int handle_show_hints(int fd, int argc, char *argv[])
{
struct ast_hint *hint;
int num = 0;
Kevin P. Fleming
committed
int watchers;
struct ast_state_cb *watcher;
Russell Bryant
committed
if (AST_LIST_EMPTY(&hints)) {
ast_cli(fd, "There are no registered dialplan hints\n");
return RESULT_SUCCESS;
}
/* ... we have hints ... */
ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
AST_LIST_LOCK(&hints);
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
Kevin P. Fleming
committed
watchers = 0;
for (watcher = hint->callbacks; watcher; watcher = watcher->next)
watchers++;
ast_cli(fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
ast_get_extension_name(hint->exten),
ast_get_context_name(ast_get_extension_context(hint->exten)),
ast_get_extension_app(hint->exten),
num++;
}
ast_cli(fd, "----------------\n");
ast_cli(fd, "- %d hints registered\n", num);
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
static int handle_show_switches(int fd, int argc, char *argv[])
AST_LIST_LOCK(&switches);
if (AST_LIST_EMPTY(&switches)) {
AST_LIST_UNLOCK(&switches);
ast_cli(fd, "There are no registered alternative switches\n");
return RESULT_SUCCESS;
}
ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
AST_LIST_TRAVERSE(&switches, sw, list)
ast_cli(fd, "%s: %s\n", sw->name, sw->description);
AST_LIST_UNLOCK(&switches);