diff options
-rwxr-xr-x | channel.c | 6 | ||||
-rwxr-xr-x | channels/Makefile | 2 | ||||
-rwxr-xr-x | channels/chan_features.c | 520 | ||||
-rwxr-xr-x | channels/chan_local.c | 3 | ||||
-rwxr-xr-x | cli.c | 62 | ||||
-rwxr-xr-x | frame.c | 6 | ||||
-rwxr-xr-x | pbx/pbx_dundi.c | 22 |
7 files changed, 598 insertions, 23 deletions
@@ -59,6 +59,8 @@ static int shutting_down = 0; static int uniqueint = 0; +unsigned long global_fin = 0, global_fout = 0; + /* XXX Lock appropriately in more functions XXX */ struct chanlist { @@ -334,8 +336,8 @@ struct ast_channel *ast_channel_alloc(int needqueue) tmp->streamid = -1; tmp->appl = NULL; tmp->data = NULL; - tmp->fin = 0; - tmp->fout = 0; + tmp->fin = global_fin; + tmp->fout = global_fout; snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long)time(NULL), uniqueint++); headp=&tmp->varshead; ast_mutex_init(&tmp->lock); diff --git a/channels/Makefile b/channels/Makefile index c91be45c8..9810cec95 100755 --- a/channels/Makefile +++ b/channels/Makefile @@ -21,7 +21,7 @@ CHANNEL_LIBS=chan_modem.so chan_sip.so \ chan_modem_aopen.so \ chan_modem_bestdata.so chan_modem_i4l.so \ chan_agent.so chan_mgcp.so chan_iax2.so \ - chan_local.so chan_skinny.so + chan_local.so chan_skinny.so chan_features.so ifeq (${OSARCH},OpenBSD) CFLAGS+=-I/usr/local/include diff --git a/channels/chan_features.c b/channels/chan_features.c new file mode 100755 index 000000000..7c823e786 --- /dev/null +++ b/channels/chan_features.c @@ -0,0 +1,520 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * feature Proxy Channel + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <stdio.h> +#include <string.h> +#include <asterisk/lock.h> +#include <asterisk/channel.h> +#include <asterisk/channel_pvt.h> +#include <asterisk/config.h> +#include <asterisk/logger.h> +#include <asterisk/module.h> +#include <asterisk/pbx.h> +#include <asterisk/options.h> +#include <asterisk/lock.h> +#include <asterisk/sched.h> +#include <asterisk/io.h> +#include <asterisk/rtp.h> +#include <asterisk/acl.h> +#include <asterisk/callerid.h> +#include <asterisk/file.h> +#include <asterisk/cli.h> +#include <asterisk/app.h> +#include <asterisk/musiconhold.h> +#include <asterisk/manager.h> +#include <sys/socket.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <sys/signal.h> + + +static char *desc = "Feature Proxy Channel"; +static char *type = "Feature"; +static char *tdesc = "Feature Proxy Channel Driver"; + +static int capability = -1; + +static int usecnt =0; +AST_MUTEX_DEFINE_STATIC(usecnt_lock); + +#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0) + +/* Protect the interface list (of feature_pvt's) */ +AST_MUTEX_DEFINE_STATIC(featurelock); + +struct feature_sub { + struct ast_channel *owner; + int inthreeway; + int pfd; +}; + +static struct feature_pvt { + ast_mutex_t lock; /* Channel private lock */ + char tech[AST_MAX_EXTENSION]; /* Technology to abstract */ + char dest[AST_MAX_EXTENSION]; /* Destination to abstract */ + struct ast_channel *subchan; + struct feature_sub subs[3]; /* Subs */ + struct ast_channel *owner; /* Current Master Channel */ + struct feature_pvt *next; /* Next entity */ +} *features = NULL; + +#define SUB_REAL 0 /* Active call */ +#define SUB_CALLWAIT 1 /* Call-Waiting call on hold */ +#define SUB_THREEWAY 2 /* Three-way call */ + +static inline void init_sub(struct feature_sub *sub) +{ + sub->inthreeway = 0; + sub->pfd = -1; +} + +static inline int indexof(struct feature_pvt *p, struct ast_channel *owner, int nullok) +{ + int x; + if (!owner) { + ast_log(LOG_WARNING, "indexof called on NULL owner??\n"); + return -1; + } + for (x=0;x<3;x++) { + if (owner == p->subs[x].owner) + return x; + } + return -1; +} + +static void wakeup_sub(struct feature_pvt *p, int a) +{ + struct ast_frame null = { AST_FRAME_NULL, }; + for (;;) { + if (p->subs[a].owner) { + if (ast_mutex_trylock(&p->subs[a].owner->lock)) { + ast_mutex_unlock(&p->lock); + usleep(1); + ast_mutex_lock(&p->lock); + } else { + ast_queue_frame(p->subs[a].owner, &null); + ast_mutex_unlock(&p->subs[a].owner->lock); + break; + } + } else + break; + } +} + +static void swap_subs(struct feature_pvt *p, int a, int b) +{ + int x; + int tinthreeway; + struct ast_channel *towner; + + ast_log(LOG_DEBUG, "Swapping %d and %d\n", a, b); + + towner = p->subs[a].owner; + tinthreeway = p->subs[a].inthreeway; + + p->subs[a].owner = p->subs[b].owner; + p->subs[a].inthreeway = p->subs[b].inthreeway; + + p->subs[b].owner = towner; + p->subs[b].inthreeway = tinthreeway; + + if (p->subs[a].owner) { + for (x=0;x<AST_MAX_FDS;x++) { + if (a) + p->subs[a].owner->fds[x] = -1; + else + p->subs[a].owner->fds[x] = p->subchan->fds[x]; + } + } + if (p->subs[b].owner) { + for (x=0;x<AST_MAX_FDS;x++) + p->subs[b].owner->fds[x] = p->subchan->fds[x]; + } + wakeup_sub(p, a); + wakeup_sub(p, b); +} + +static int features_answer(struct ast_channel *ast) +{ + struct feature_pvt *p = ast->pvt->pvt; + int res = -1; + int x; + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) + res = ast_answer(p->subchan); + ast_mutex_unlock(&p->lock); + return res; +} + +static struct ast_frame *features_read(struct ast_channel *ast) +{ + static struct ast_frame null_frame = { AST_FRAME_NULL, }; + struct feature_pvt *p = ast->pvt->pvt; + struct ast_frame *f; + int x; + + f = &null_frame; + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) + f = ast_read(p->subchan); + ast_mutex_unlock(&p->lock); + return f; +} + +static int features_write(struct ast_channel *ast, struct ast_frame *f) +{ + struct feature_pvt *p = ast->pvt->pvt; + int res = -1; + int x; + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) + res = ast_write(p->subchan, f); + ast_mutex_unlock(&p->lock); + return res; +} + +static int features_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + struct feature_pvt *p = newchan->pvt->pvt; + int x; + ast_mutex_lock(&p->lock); + if (p->owner == oldchan) + p->owner = newchan; + for (x=0;x<3;x++) { + if (p->subs[x].owner == oldchan) + p->subs[x].owner = newchan; + } + ast_mutex_unlock(&p->lock); + return 0; +} + +static int features_indicate(struct ast_channel *ast, int condition) +{ + struct feature_pvt *p = ast->pvt->pvt; + int res = -1; + int x; + /* Queue up a frame representing the indication as a control frame */ + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) + res = ast_indicate(p->subchan, condition); + ast_mutex_unlock(&p->lock); + return res; +} + +static int features_digit(struct ast_channel *ast, char digit) +{ + struct feature_pvt *p = ast->pvt->pvt; + int res = -1; + int x; + /* Queue up a frame representing the indication as a control frame */ + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) + res = ast_senddigit(p->subchan, digit); + ast_mutex_unlock(&p->lock); + return res; +} + +static int features_call(struct ast_channel *ast, char *dest, int timeout) +{ + struct feature_pvt *p = ast->pvt->pvt; + int res = -1; + int x; + + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) { + if (p->owner->cid.cid_num) + p->subchan->cid.cid_num = strdup(p->owner->cid.cid_num); + else + p->subchan->cid.cid_num = NULL; + + if (p->owner->cid.cid_name) + p->subchan->cid.cid_name = strdup(p->owner->cid.cid_name); + else + p->subchan->cid.cid_name = NULL; + + if (p->owner->cid.cid_rdnis) + p->subchan->cid.cid_rdnis = strdup(p->owner->cid.cid_rdnis); + else + p->subchan->cid.cid_rdnis = NULL; + + if (p->owner->cid.cid_ani) + p->subchan->cid.cid_ani = strdup(p->owner->cid.cid_ani); + else + p->subchan->cid.cid_ani = NULL; + + strncpy(p->subchan->language, p->owner->language, sizeof(p->subchan->language) - 1); + strncpy(p->subchan->accountcode, p->owner->accountcode, sizeof(p->subchan->accountcode) - 1); + p->subchan->cdrflags = p->owner->cdrflags; + } else + ast_log(LOG_NOTICE, "Uhm yah, not quite there with the call waiting...\n"); + ast_mutex_unlock(&p->lock); + return res; +} + +static int features_hangup(struct ast_channel *ast) +{ + struct feature_pvt *p = ast->pvt->pvt; + struct feature_pvt *cur, *prev=NULL; + int x; + + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (x > -1) { + p->subs[x].owner = NULL; + /* XXX Re-arrange, unconference, etc XXX */ + } + ast->pvt->pvt = NULL; + + + if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) { + ast_mutex_unlock(&p->lock); + /* Remove from list */ + ast_mutex_lock(&featurelock); + cur = features; + while(cur) { + if (cur == p) { + if (prev) + prev->next = cur->next; + else + features = cur->next; + break; + } + prev = cur; + cur = cur->next; + } + ast_mutex_unlock(&featurelock); + ast_mutex_lock(&p->lock); + /* And destroy */ + if (p->subchan) + ast_hangup(p->subchan); + ast_mutex_unlock(&p->lock); + ast_mutex_destroy(&p->lock); + free(p); + return 0; + } + ast_mutex_unlock(&p->lock); + return 0; +} + +static struct feature_pvt *features_alloc(char *data, int format) +{ + struct feature_pvt *tmp; + char *dest=NULL; + char *tech; + int x; + int status; + struct ast_channel *chan; + + tech = ast_strdupa(data); + if (tech) { + dest = strchr(tech, '/'); + if (dest) { + *dest = '\0'; + dest++; + } + } + if (!tech || !dest) { + ast_log(LOG_NOTICE, "Format for feature channel is Feature/Tech/Dest ('%s' not valid)!\n", + data); + return NULL; + } + ast_mutex_lock(&featurelock); + tmp = features; + while(tmp) { + if (!strcasecmp(tmp->tech, tech) && !strcmp(tmp->dest, dest)) + break; + tmp = tmp->next; + } + ast_mutex_unlock(&featurelock); + if (!tmp) { + chan = ast_request(tech, format, dest, &status); + if (!chan) { + ast_log(LOG_NOTICE, "Unable to allocate subchannel '%s/%s'\n", tech, dest); + return NULL; + } + tmp = malloc(sizeof(struct feature_pvt)); + if (tmp) { + memset(tmp, 0, sizeof(struct feature_pvt)); + for (x=0;x<3;x++) + init_sub(tmp->subs + x); + ast_mutex_init(&tmp->lock); + strncpy(tmp->tech, tech, sizeof(tmp->tech) - 1); + strncpy(tmp->dest, dest, sizeof(tmp->dest) - 1); + tmp->subchan = chan; + ast_mutex_lock(&featurelock); + tmp->next = features; + features = tmp; + ast_mutex_unlock(&featurelock); + } + } + return tmp; +} + +static struct ast_channel *features_new(struct feature_pvt *p, int state, int index) +{ + struct ast_channel *tmp; + int x,y; + if (!p->subchan) { + ast_log(LOG_WARNING, "Called upon channel with no subchan:(\n"); + return NULL; + } + if (!p->subs[index].owner) { + ast_log(LOG_WARNING, "Called to put index %d already there!\n", index); + return NULL; + } + tmp = ast_channel_alloc(1); + if (!tmp) + return NULL; + if (tmp) { + for (x=1;x<4;x++) { + snprintf(tmp->name, sizeof(tmp->name), "Feature/%s/%s-%d", p->tech, p->dest, x); + for (y=0;y<3;y++) { + if (p->subs[x].owner && !strcasecmp(p->subs[x].owner->name, tmp->name)) + break; + } + if (y < 3) + break; + } + tmp->type = type; + ast_setstate(tmp, state); + tmp->writeformat = p->subchan->writeformat;; + tmp->pvt->rawwriteformat = p->subchan->pvt->rawwriteformat; + tmp->readformat = p->subchan->readformat; + tmp->pvt->rawreadformat = p->subchan->pvt->rawreadformat; + tmp->pvt->pvt = p; + tmp->pvt->send_digit = features_digit; + tmp->pvt->call = features_call; + tmp->pvt->hangup = features_hangup; + tmp->pvt->answer = features_answer; + tmp->pvt->read = features_read; + tmp->pvt->write = features_write; + tmp->pvt->exception = features_read; + tmp->pvt->indicate = features_indicate; + tmp->pvt->fixup = features_fixup; + p->subs[index].owner = tmp; + ast_mutex_lock(&usecnt_lock); + usecnt++; + ast_mutex_unlock(&usecnt_lock); + ast_update_use_count(); + } else + ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); + return tmp; +} + + +static struct ast_channel *features_request(const char *type, int format, void *data, int *cause) +{ + struct feature_pvt *p; + struct ast_channel *chan = NULL; + p = features_alloc(data, format); + if (!p->subs[SUB_REAL].owner) + chan = features_new(p, AST_STATE_DOWN, SUB_REAL); + return chan; +} + +static int features_show(int fd, int argc, char **argv) +{ + struct feature_pvt *p; + + if (argc != 3) + return RESULT_SHOWUSAGE; + ast_mutex_lock(&featurelock); + p = features; + while(p) { + ast_mutex_lock(&p->lock); + ast_cli(fd, "%s -- %s/%s\n", p->owner ? p->owner->name : "<unowned>", p->tech, p->dest); + ast_mutex_unlock(&p->lock); + p = p->next; + } + if (!features) + ast_cli(fd, "No feature channels in use\n"); + ast_mutex_unlock(&featurelock); + return RESULT_SUCCESS; +} + +static char show_features_usage[] = +"Usage: feature show channels\n" +" Provides summary information on feature channels.\n"; + +static struct ast_cli_entry cli_show_features = { + { "feature", "show", "channels", NULL }, features_show, + "Show status of feature channels", show_features_usage, NULL }; + +int load_module() +{ + /* Make sure we can register our sip channel type */ + if (ast_channel_register(type, tdesc, capability, features_request)) { + ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); + return -1; + } + ast_cli_register(&cli_show_features); + return 0; +} + +int reload() +{ + return 0; +} + +int unload_module() +{ + struct feature_pvt *p; + /* First, take us out of the channel loop */ + ast_cli_unregister(&cli_show_features); + ast_channel_unregister(type); + if (!ast_mutex_lock(&featurelock)) { + /* Hangup all interfaces if they have an owner */ + p = features; + while(p) { + if (p->owner) + ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); + p = p->next; + } + features = NULL; + ast_mutex_unlock(&featurelock); + } else { + ast_log(LOG_WARNING, "Unable to lock the monitor\n"); + return -1; + } + return 0; +} + +int usecount() +{ + int res; + ast_mutex_lock(&usecnt_lock); + res = usecnt; + ast_mutex_unlock(&usecnt_lock); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} + +char *description() +{ + return desc; +} + diff --git a/channels/chan_local.c b/channels/chan_local.c index 661ab403e..fc5c74fea 100755 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -335,6 +335,9 @@ static int local_hangup(struct ast_channel *ast) cur = cur->next; } ast_mutex_unlock(&locallock); + /* Grab / release lock just in case */ + ast_mutex_lock(&p->lock); + ast_mutex_unlock(&p->lock); /* And destroy */ if (!glaredetect) { ast_mutex_destroy(&p->lock); @@ -37,6 +37,8 @@ #define VERSION_INFO "Asterisk " ASTERISK_VERSION " built by " BUILD_USER "@" BUILD_HOSTNAME \ " on a " BUILD_MACHINE " running " BUILD_OS +extern unsigned long global_fin, global_fout; + void ast_cli(int fd, char *fmt, ...) { char *stuff; @@ -548,47 +550,73 @@ static int handle_commandcomplete(int fd, int argc, char *argv[]) static int handle_debugchan(int fd, int argc, char *argv[]) { struct ast_channel *c=NULL; + int is_all; if (argc != 3) return RESULT_SHOWUSAGE; + + is_all = !strcasecmp("all", argv[2]); + if (is_all) { + global_fin |= 0x80000000; + global_fout |= 0x80000000; + } c = ast_channel_walk_locked(NULL); while(c) { - if (!strcasecmp(c->name, argv[2])) { - c->fin |= 0x80000000; - c->fout |= 0x80000000; - break; + if (is_all || !strcasecmp(c->name, argv[2])) { + if (!(c->fin & 0x80000000) || !(c->fout & 0x80000000)) { + c->fin |= 0x80000000; + c->fout |= 0x80000000; + ast_cli(fd, "Debugging enabled on channel %s\n", c->name); + } + if (!is_all) + break; } ast_mutex_unlock(&c->lock); c = ast_channel_walk_locked(c); } - if (c) { - ast_cli(fd, "Debugging enabled on channel %s\n", c->name); - ast_mutex_unlock(&c->lock); + if (!is_all) { + if (c) + ast_mutex_unlock(&c->lock); + else + ast_cli(fd, "No such channel %s\n", argv[2]); } else - ast_cli(fd, "No such channel %s\n", argv[2]); + ast_cli(fd, "Debugging on new channels is enabled\n"); return RESULT_SUCCESS; } static int handle_nodebugchan(int fd, int argc, char *argv[]) { struct ast_channel *c=NULL; + int is_all; if (argc != 4) return RESULT_SHOWUSAGE; + is_all = !strcasecmp("all", argv[3]); + if (is_all) { + global_fin &= ~0x80000000; + global_fout &= ~0x80000000; + } c = ast_channel_walk_locked(NULL); while(c) { - if (!strcasecmp(c->name, argv[3])) { - c->fin &= 0x7fffffff; - c->fout &= 0x7fffffff; - break; + if (is_all || !strcasecmp(c->name, argv[3])) { + if ((c->fin & 0x80000000) || (c->fout & 0x80000000)) { + c->fin &= 0x7fffffff; + c->fout &= 0x7fffffff; + ast_cli(fd, "Debugging disabled on channel %s\n", c->name); + } + if (!is_all) + break; } ast_mutex_unlock(&c->lock); c = ast_channel_walk_locked(c); } - if (c) { - ast_cli(fd, "Debugging disabled on channel %s\n", c->name); - ast_mutex_unlock(&c->lock); - } else - ast_cli(fd, "No such channel %s\n", argv[2]); + if (!is_all) { + if (c) + ast_mutex_unlock(&c->lock); + else + ast_cli(fd, "No such channel %s\n", argv[3]); + } + else + ast_cli(fd, "Debugging on new channels is disabled\n"); return RESULT_SUCCESS; } @@ -607,7 +607,7 @@ void ast_frame_dump(char *name, struct ast_frame *f, char *prefix) char subclass[40] = "Unknown Subclass"; char csub[80]; char moreinfo[40] = ""; - char cn[40]; + char cn[60]; char cp[40]; char cmn[40]; if (name) @@ -672,9 +672,13 @@ void ast_frame_dump(char *name, struct ast_frame *f, char *prefix) case AST_CONTROL_RADIO_UNKEY: strcpy(subclass, "Unkey Radio"); break; + case -1: + strcpy(subclass, "Stop generators"); + break; default: snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass); } + break; case AST_FRAME_NULL: strcpy(ftype, "Null Frame"); strcpy(subclass, "N/A"); diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c index 0c8b86555..7aa4efb2c 100755 --- a/pbx/pbx_dundi.c +++ b/pbx/pbx_dundi.c @@ -3824,10 +3824,14 @@ int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_e static int dundi_lookup_exec(struct ast_channel *chan, void *data) { char *tmp; - char *context; + char *context = NULL; char *opts; - int res = -1; + int res = 0; + int results = 0; + int x; + int bypass = 0; struct localuser *u; + struct dundi_result dr[MAX_RESULTS]; if (!data || !strlen(data)) { ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n"); @@ -3853,6 +3857,20 @@ static int dundi_lookup_exec(struct ast_channel *chan, void *data) opts = ""; } + results = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass); + if (results > 0) { + sort_results(dr, results); + for (x=0;x<results;x++) { + if (dr[x].flags & DUNDI_FLAG_EXISTS) { + pbx_builtin_setvar_helper(chan, "DUNDTECH", dr[x].tech); + pbx_builtin_setvar_helper(chan, "DUNDDEST", dr[x].dest); + break; + } + } + } else { + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) + chan->priority += 100; + } LOCAL_USER_REMOVE(u); return res; } |