[Asterisk-Users] app_valetparking.c
Paul Zimm
pbzinc at dejazzd.com
Mon Jun 20 08:51:26 MST 2005
Try this
> Since www.bkw.org seems not to exist anymore (getting response from
> some hosting provider), does anyone happend to have a copy of
> app_valetparking.c from www.bkw.org - the one that should work with *
> stable 1.0.X ? If so please contact me.
>
> One that can be downloaded from www.loligo.com dosn't compile with
> 1.0.X, and SuperValletParking (www.asterlink.com/svp/) seems to be for
> * HEAD (1.1.X), so it wont do me any good.
>
-------------- next part --------------
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Routines implementing call valetparking
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster at linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/lock.h>
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/options.h>
#include <asterisk/module.h>
#include <asterisk/translate.h>
#include <asterisk/say.h>
#include <asterisk/callerid.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/parking.h>
#include <asterisk/musiconhold.h>
#include <asterisk/config.h>
#include <asterisk/cli.h>
#include <asterisk/app.h>
#include <asterisk/manager.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <pthread.h>
#define DEFAULT_VALETPARK_TIME 45000
static char *valetparking = "ValetParking";
static char *valetparkedcall = "ValetParkCall";
static char *valetunparkedcall = "ValetUnparkCall";
static char *valetparklist = "ValetParkList";
/* No more than 45 seconds valetparked before you do something with them */
static int valetparkingtime = DEFAULT_VALETPARK_TIME;
/* First available extension for valetparking */
static int valetparking_start = 1;
/* Last available extension for valetparking */
static int valetparking_stop = 10000;
static char *vpsynopsis = "Valet Parking";
static char *vpcsynopsis = "Valet Park Call";
static char *vupsynopsis = "Valet UnPark Call";
static char *vlsynopsis = "ValetParkList";
static char *vpdesc =
"ValetParking(<exten>|<lotname>|<timeout>[|<return_ext>][|<return_pri>][|<return_context>])\n"
"Auto-Sense Valet Parking: if <exten> is not occupied, park it, if it is already parked, bridge to it.\n\n";
static char *vpcdesc =
"ValetParkCall(<exten>|<lotname>|<timeout>[|<return_ext>][|<return_pri>][|<return_context>])\n"
"Park Call at <exten> in <lotname> until someone calls ValetUnparkCall on the same <exten> + <lotname>\n"
"set <exten> to 'auto' to auto-choose the slot.\n\n";
static char *vupdesc =
"ValetUnparkCall(<exten>|<lotname>)\n"
"Un-Park the call at <exten> in lot <lotname> use 'fifo' or 'filo' for auto-ordered Un-Park.\n\n";
static char *vldesc =
"ValetParkList(<lotname>)\n"
"Audibly list the slot number of all the calls in <lotname> press * to unpark it.\n\n";
struct valetparkeduser {
struct ast_channel *chan;
struct timeval start;
int valetparkingnum;
/* Where to go if our valetparking time expires */
char context[AST_MAX_EXTENSION];
char exten[AST_MAX_EXTENSION];
char lotname[AST_MAX_EXTENSION];
int priority;
int valetparkingtime;
struct valetparkeduser *next;
};
static struct valetparkeduser *valetparkinglot;
static ast_mutex_t valetparking_lock = AST_MUTEX_INITIALIZER;
static pthread_t valetparking_thread;
STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
static int valetparking_count(void)
{
struct valetparkeduser *cur;
int x=0;
ast_mutex_lock(&valetparking_lock);
for(cur = valetparkinglot;cur;cur = cur->next)
x++;
ast_mutex_unlock(&valetparking_lock);
return x;
}
static int valetparking_say(struct ast_channel *chan,char *lotname)
{
struct valetparkeduser *cur;
int x=0,y=0,res=0;
int list[1024];
if(!lotname)
return 0;
ast_mutex_lock(&valetparking_lock);
for(cur = valetparkinglot;cur;cur = cur->next)
if(cur->lotname && !strcmp(lotname,cur->lotname))
list[y++] = cur->valetparkingnum;
ast_mutex_unlock(&valetparking_lock);
for(x=0;x<y;x++) {
ast_say_digits(chan,list[x], "", chan->language);
res = ast_waitfordigit(chan,1500);
if(res != 0) {
res = list[x];
break;
}
}
return res;
}
static int ast_pop_valetparking_top(char *lotname)
{
struct valetparkeduser *cur;
ast_mutex_lock(&valetparking_lock);
for(cur = valetparkinglot;cur;cur = cur->next)
if(cur->lotname && !strcmp(lotname,cur->lotname))
break;
ast_mutex_unlock(&valetparking_lock);
return cur ? cur->valetparkingnum : 0;
}
static int ast_pop_valetparking_bot(char *lotname)
{
struct valetparkeduser *cur,*last=NULL;
ast_mutex_lock(&valetparking_lock);
for(cur = valetparkinglot;cur;cur = cur->next) {
if(cur->lotname && !strcmp(lotname,cur->lotname)) {
last = cur;
}
}
ast_mutex_unlock(&valetparking_lock);
return last ? last->valetparkingnum : 0;
}
static int ast_is_valetparked(char *exten,char *lotname)
{
struct valetparkeduser *cur;
int ext=0;
int ret = 0;
ext = atoi(exten);
if(! ext > 0) {
return ret;
}
ast_mutex_lock(&valetparking_lock);
cur = valetparkinglot;
while(cur) {
if (cur->valetparkingnum == ext && lotname && cur->lotname && !strcmp(lotname,cur->lotname)) {
ret = 1;
break;
}
cur = cur->next;
}
ast_mutex_unlock(&valetparking_lock);
return ret;
}
static int ast_valetpark_call(struct ast_channel *chan, int timeout, int *extout,char *lotname)
{
/* We put the user in the valetparking list, then wake up the valetparking thread to be sure it looks
after these channels too */
struct valetparkeduser *pu, *cur;
int x;
x = *extout;
pu = malloc(sizeof(struct valetparkeduser));
if (pu) {
memset(pu,0,sizeof(struct valetparkeduser));
ast_mutex_lock(&valetparking_lock);
if(lotname) {
strncpy(pu->lotname,lotname,sizeof(pu->lotname));
if(chan->exten)
strncpy(pu->exten,chan->exten,sizeof(pu->exten)-1);
if(chan->context)
strncpy(pu->context,chan->context,sizeof(pu->context)-1);
pu->priority = chan->priority;
x = *extout;
if(x == -1) {
for (x=valetparking_start;x<=valetparking_stop;x++) {
for(cur = valetparkinglot;cur;cur=cur->next) {
if (cur->valetparkingnum == x && cur->lotname && !strcmp(cur->lotname,lotname))
break;
}
if (!cur)
break;
}
}
}
if (x <= valetparking_stop) {
chan->appl = "Valet Parked Call";
chan->data = NULL;
pu->chan = chan;
/* Start music on hold */
ast_moh_start(pu->chan, NULL);
gettimeofday(&pu->start, NULL);
pu->valetparkingnum = x;
if (timeout >= 0)
pu->valetparkingtime = timeout;
else
pu->valetparkingtime = valetparkingtime;
*extout = x;
/* Remember what had been dialed, so that if the valetparking
expires, we try to come back to the same place */
if (strlen(chan->macrocontext))
strncpy(pu->context, chan->macrocontext, sizeof(pu->context)-1);
else
strncpy(pu->context, chan->context, sizeof(pu->context)-1);
if (strlen(chan->macroexten))
strncpy(pu->exten, chan->macroexten, sizeof(pu->exten)-1);
else
strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1);
if (chan->macropriority)
pu->priority = chan->macropriority;
else
pu->priority = chan->priority;
pu->next = valetparkinglot;
valetparkinglot = pu;
ast_mutex_unlock(&valetparking_lock);
/* Wake up the (presumably select()ing) thread */
pthread_kill(valetparking_thread, SIGURG);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Valet Parked %s on slot %d\n", pu->chan->name, pu->valetparkingnum);
manager_event(EVENT_FLAG_CALL, "VirtualValetparkedCall",
"Exten: %d\r\n"
"Channel: %s\r\n"
"LotName: %s\r\n"
"Timeout: %ld\r\n"
"CallerID: %s\r\n"
,pu->valetparkingnum, pu->chan->name, lotname
,(long)pu->start.tv_sec + (long)(pu->valetparkingtime/1000) - (long)time(NULL)
,(pu->chan->callerid ? pu->chan->callerid : "")
);
return 0;
} else {
ast_log(LOG_WARNING, "No more valetparking spaces\n");
free(pu);
ast_mutex_unlock(&valetparking_lock);
return -1;
}
} else {
ast_log(LOG_WARNING, "Out of memory\n");
return -1;
}
return 0;
}
static int ast_masq_valetpark_call(struct ast_channel *rchan,int timeout, int *extout,char *lotname)
{
struct ast_channel *chan;
struct ast_frame *f;
/* Make a new, fake channel that we'll use to masquerade in the real one */
chan = ast_channel_alloc(0);
if (chan) {
/* Let us keep track of the channel name */
snprintf(chan->name, sizeof (chan->name), "ValetParked/%s",rchan->name);
/* Make formats okay */
chan->readformat = rchan->readformat;
chan->writeformat = rchan->writeformat;
ast_channel_masquerade(chan, rchan);
/* Setup the extensions and such */
strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
chan->priority = rchan->priority;
/* Make the masq execute */
f = ast_read(chan);
if (f)
ast_frfree(f);
ast_valetpark_call(chan, timeout, extout,lotname);
} else {
ast_log(LOG_WARNING, "Unable to create Valet Parked channel\n");
return -1;
}
return 0;
}
static void *do_valetparking_thread(void *ignore)
{
int ms, tms, max;
struct valetparkeduser *pu, *pl, *pt = NULL;
struct timeval tv;
struct ast_frame *f;
int x;
int gc=0;
fd_set rfds, efds;
fd_set nrfds, nefds;
FD_ZERO(&rfds);
FD_ZERO(&efds);
for (;;) {
ms = -1;
max = -1;
ast_mutex_lock(&valetparking_lock);
pl = NULL;
pu = valetparkinglot;
gettimeofday(&tv, NULL);
FD_ZERO(&nrfds);
FD_ZERO(&nefds);
while(pu) {
tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
if(gc < 5 && !pu->chan->generator) {
gc++;
ast_moh_start(pu->chan,NULL);
}
if(pu->valetparkingtime > 0 && tms > pu->valetparkingtime) {
/* They've been waiting too long, send them back to where they came. Theoretically they
should have their original extensions and such, but we copy to be on the safe side */
strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1);
strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1);
pu->chan->priority = pu->priority;
/* Stop music on hold */
ast_moh_stop(pu->chan);
/* Start up the PBX, or hang them up */
if (ast_pbx_start(pu->chan)) {
ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
ast_hangup(pu->chan);
}
/* And take them out of the valetparking lot */
if (pl)
pl->next = pu->next;
else
valetparkinglot = pu->next;
pt = pu;
pu = pu->next;
free(pt);
} else {
for (x=0;x<AST_MAX_FDS;x++) {
if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
if (FD_ISSET(pu->chan->fds[x], &efds))
pu->chan->exception = 1;
pu->chan->fdno = x;
/* See if they need servicing */
f = ast_read(pu->chan);
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
/* There's a problem, hang them up*/
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being Valet Parked\n", pu->chan->name);
ast_hangup(pu->chan);
/* And take them out of the valetparking lot */
if (pl)
pl->next = pu->next;
else
valetparkinglot = pu->next;
pt = pu;
pu = pu->next;
free(pt);
break;
} else {
/* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
ast_frfree(f);
goto std; /* XXX Ick: jumping into an else statement??? XXX */
}
}
}
if (x >= AST_MAX_FDS) {
std: for (x=0;x<AST_MAX_FDS;x++) {
/* Keep this one for next one */
if (pu->chan->fds[x] > -1) {
FD_SET(pu->chan->fds[x], &nrfds);
FD_SET(pu->chan->fds[x], &nefds);
if (pu->chan->fds[x] > max)
max = pu->chan->fds[x];
}
}
/* Keep track of our longest wait */
if ((tms < ms) || (ms < 0))
ms = tms;
pl = pu;
pu = pu->next;
}
}
}
ast_mutex_unlock(&valetparking_lock);
rfds = nrfds;
efds = nefds;
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
/* Wait for something to happen */
ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
pthread_testcancel();
}
return NULL; /* Never reached */
}
static int ast_valetparking(struct ast_channel *chan, void *data)
{
struct localuser *u;
char *appname;
char buf[512],*exten,*lotname,*to;
struct ast_app *app;
int res=0;
if (!data) {
ast_log(LOG_WARNING, "ValetParking requires an argument (extension number)\n");
return -1;
}
exten=lotname=to=NULL;
strncpy(buf,data,512);
exten = buf;
if((lotname=strchr(exten,'|'))) {
*lotname = '\0';
*lotname++;
if((to=strchr(lotname,'|'))) {
*to = '\0';
*to++;
}
}
if(exten[0] >= 97) {
ast_log(LOG_WARNING, "ValetParking requires a numeric extension.\n");
return -1;
}
appname = ast_is_valetparked(exten,lotname) ? "ValetParkCall" : "ValetUnparkCall";
app = pbx_findapp(appname);
LOCAL_USER_ADD(u);
if(app) {
res = pbx_exec(chan,app,data,1);
} else {
ast_log(LOG_WARNING, "Error: Can't find app %s\n",appname);
res = -1;
}
LOCAL_USER_REMOVE(u);
return res;
}
static int valetpark_call(struct ast_channel *chan, void *data)
{
struct localuser *u;
int timeout = DEFAULT_VALETPARK_TIME;
int ext = 0,res = 0;
char buf[512],*exten,*lotname,*to,*findme,*context,*priority=NULL,tmp[80];
if (!data) {
ast_log(LOG_WARNING, "ValetParkCall requires an argument (extension number)\n");
return -1;
}
exten=lotname=to=findme=context=NULL;
strncpy(buf,data,512);
exten = buf;
if((lotname=strchr(exten,'|'))) {
*lotname = '\0';
*lotname++;
if((to=strchr(lotname,'|'))) {
*to = '\0';
*to++;
timeout = atoi(to) * 1000;
if((findme=strchr(to,'|'))) {
*findme = '\0';
*findme++;
if((priority=strchr(findme,'|'))) {
*priority = '\0';
*priority++;
if((context=strchr(priority,'|'))) {
*context = '\0';
*context++;
}
}
}
}
}
if(!lotname) {
ast_log(LOG_WARNING,"Please specify a lotname in the dialplan.");
return -1;
}
if(ast_is_valetparked(exten,lotname)) {
ast_log(LOG_WARNING,"Call is already Valet Parked Here [%s]\n",exten);
return -1;
}
LOCAL_USER_ADD(u);
ast_answer(chan);
if(exten && lotname) {
if(!strcmp(exten,"auto"))
ext = -1;
else if(!strcmp(exten,"query")) {
ast_waitfor(chan,-1);
memset(&tmp,0,80);
ast_streamfile(chan, "vm-extension", chan->language);
res = ast_waitstream(chan, AST_DIGIT_ANY);
if(res)
return -1;
ast_app_getdata(chan,"vm-then-pound",tmp,80,5000);
if(tmp[0])
ext = atoi(tmp);
} else {
ext = atoi(exten);
}
if(ext == 0)
ext = -1;
if(findme)
strncpy(chan->exten,findme,sizeof(chan->exten)-1);
if (context)
strncpy(chan->context, context, sizeof(chan->context)-1);
if(priority) {
chan->priority = atoi(priority);
if(!chan->priority)
chan->priority = 1;
}
ast_masq_valetpark_call(chan,timeout,&ext,lotname);
}
LOCAL_USER_REMOVE(u);
return 1;
}
static int valetpark_list(struct ast_channel *chan, void *data)
{
struct localuser *u;
int res=0;
struct ast_app *app;
char buf[512];
if(!data) {
ast_log(LOG_WARNING,"Parameter 'lotname' is required.\n");
return -1;
}
LOCAL_USER_ADD(u);
res = valetparking_say(chan,data);
if(res > 0) {
app = pbx_findapp("ValetUnparkCall");
if(app) {
snprintf(buf,512,"%d|%s",res,(char *)data);
res = pbx_exec(chan,app,buf,1);
}
}
LOCAL_USER_REMOVE(u);
return 1;
}
static int valetunpark_call(struct ast_channel *chan, void *data)
{
int res=0;
struct localuser *u;
struct ast_channel *peer=NULL;
struct valetparkeduser *pu, *pl=NULL;
int valetpark=-1;
int dres;
struct ast_bridge_config config;
char buf[512],tmp[80],*exten,*lotname;
if (!data) {
ast_log(LOG_WARNING, "Valetpark requires an argument (extension number)\n");
return -1;
}
exten=lotname=NULL;
strncpy(buf,data,512);
exten = buf;
if((lotname=strchr(exten,'|'))) {
*lotname = '\0';
*lotname++;
}
if(!lotname) {
ast_log(LOG_WARNING,"Please specify a lotname in the dialplan.");
return -1;
}
LOCAL_USER_ADD(u);
ast_answer(chan);
if(exten) {
if(!strcmp(exten,"fifo")) {
valetpark = ast_pop_valetparking_top(lotname);
}
else if(!strcmp(exten,"filo")) {
valetpark = ast_pop_valetparking_bot(lotname);
}
else if(!strcmp(exten,"query")) {
ast_waitfor(chan,-1);
memset(&tmp,0,80);
ast_streamfile(chan, "vm-extension", chan->language);
res = ast_waitstream(chan, AST_DIGIT_ANY);
if(res)
return -1;
ast_app_getdata(chan,"vm-then-pound",tmp,80,5000);
if(tmp[0])
valetpark = atoi(tmp);
}
else {
valetpark = atoi(exten);
}
if(valetpark == 0) {
ast_log(LOG_WARNING, "Nobody Valet Parked in %s",lotname);
LOCAL_USER_REMOVE(u);
return -1;
}
}
else
valetpark = atoi((char *)data);
ast_mutex_lock(&valetparking_lock);
pu = valetparkinglot;
while(pu) {
if ((lotname && pu->valetparkingnum == valetpark && pu->lotname && !strcmp(pu->lotname,lotname))
|| (! lotname && pu->valetparkingnum == valetpark)) {
if (pl)
pl->next = pu->next;
else
valetparkinglot = pu->next;
break;
}
pl = pu;
pu = pu->next;
}
ast_mutex_unlock(&valetparking_lock);
if (pu) {
peer = pu->chan;
free(pu);
}
/* JK02: it helps to answer the channel if not already up */
if (chan->_state != AST_STATE_UP) {
ast_answer(chan);
}
if (peer) {
ast_moh_stop(peer);
res = ast_channel_make_compatible(chan, peer);
if (res < 0) {
ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
ast_hangup(peer);
LOCAL_USER_REMOVE(u);
return -1;
}
/* This runs sorta backwards, since we give the incoming channel control, as if it
were the person called. */
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to Valet Parked call %d in lot %s\n", chan->name, valetpark,lotname);
memset(&config,0,sizeof(struct ast_bridge_config));
config.allowredirect_in = 1;
config.allowredirect_out = 1;
config.allowdisconnect = 0;
config.timelimit = 0;
config.play_warning = 0;
config.warning_freq = 0;
config.warning_sound=NULL;
res = ast_bridge_call(chan,peer,&config);
/* Simulate the PBX hanging up */
if (res != AST_PBX_NO_HANGUP_PEER)
ast_hangup(peer);
LOCAL_USER_REMOVE(u);
return res;
} else {
/* XXX Play a message XXX */
dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
if (!dres) {
dres = ast_waitstream(chan, "");
} else {
ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
res = 0;
}
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to non-existant Valet Parked call %d\n", chan->name, valetpark);
res = -1;
}
LOCAL_USER_REMOVE(u);
return res;
}
static int handle_valetparkedcalls(int fd, int argc, char *argv[])
{
struct valetparkeduser *cur;
ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s %-6s %-15s\n", "Num", "Channel"
, "Context", "Extension", "Pri", "Elapsed","Timeout","LotName");
ast_mutex_lock(&valetparking_lock);
cur=valetparkinglot;
while(cur) {
ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds %6lds %-15s\n"
,cur->valetparkingnum, cur->chan->name, cur->context, cur->exten
,cur->priority,(time(NULL) - cur->start.tv_sec),cur->valetparkingtime ? (cur->start.tv_sec + (cur->valetparkingtime/1000) - time(NULL)) : 0,cur->lotname);
cur = cur->next;
}
ast_mutex_unlock(&valetparking_lock);
return RESULT_SUCCESS;
}
static char showvaletparked_help[] =
"Usage: show valetparkedcalls\n"
" Lists currently Valet Parked calls.\n";
static struct ast_cli_entry showvaletparked =
{ { "show", "valetparkedcalls", NULL }, handle_valetparkedcalls, "Lists valetparked calls", showvaletparked_help };
/* Dump lot status */
static int manager_valetparking_status( struct mansession *s, struct message *m )
{
struct valetparkeduser *cur;
astman_send_ack(s, m, "Valet Parked calls will follow");
ast_mutex_lock(&valetparking_lock);
cur=valetparkinglot;
while(cur) {
ast_cli(s->fd, "Event: ValetParkedCall\r\n"
"Exten: %d\r\n"
"Channel: %s\r\n"
"Timeout: %ld\r\n"
"CallerID: %s\r\n"
"\r\n"
,cur->valetparkingnum, cur->chan->name
,(long)cur->start.tv_sec + (long)(cur->valetparkingtime/1000) - (long)time(NULL)
,(cur->chan->callerid ? cur->chan->callerid : "")
);
cur = cur->next;
}
ast_mutex_unlock(&valetparking_lock);
return RESULT_SUCCESS;
}
int load_module(void)
{
int res;
ast_cli_register(&showvaletparked);
valetparkingtime = DEFAULT_VALETPARK_TIME;
pthread_create(&valetparking_thread, NULL, do_valetparking_thread, NULL);
res = ast_register_application(valetunparkedcall, valetunpark_call, vupsynopsis, vupdesc);
res = ast_register_application(valetparkedcall, valetpark_call, vpcsynopsis, vpcdesc);
res = ast_register_application(valetparking, ast_valetparking, vpsynopsis, vpdesc);
res = ast_register_application(valetparklist,valetpark_list, vlsynopsis, vldesc);
if (!res) {
ast_manager_register( "ValetparkedCalls", 0, manager_valetparking_status, "List valetparked calls" );
}
return res;
}
int unload_module(void)
{
STANDARD_HANGUP_LOCALUSERS;
if (!ast_mutex_lock(&valetparking_lock)) {
if (valetparking_thread && (valetparking_thread != AST_PTHREADT_STOP)) {
pthread_cancel(valetparking_thread);
pthread_kill(valetparking_thread, SIGURG);
pthread_join(valetparking_thread, NULL);
}
valetparking_thread = AST_PTHREADT_STOP;
ast_mutex_unlock(&valetparking_lock);
} else {
ast_log(LOG_WARNING, "Unable to lock the valet\n");
return -1;
}
ast_manager_unregister( "ValetparkedCalls" );
ast_cli_unregister(&showvaletparked);
ast_unregister_application(valetunparkedcall);
ast_unregister_application(valetparkedcall);
ast_unregister_application(valetparking);
ast_unregister_application(valetparklist);
return 0;
}
char *description(void)
{
return "Valet Parking Application";
}
int usecount(void)
{
int res;
STANDARD_USECOUNT(res);
res += valetparking_count();
return res;
}
char *key()
{
return ASTERISK_GPL_KEY;
}
More information about the asterisk-users
mailing list