All pastes #381 Raw Edit

appvaletparking.c

public ini v1 · immutable
#381 ·published 2004-08-01 02:06 UTC
rendered paste body
/* * Asterisk -- A telephony toolkit for Linux. * * Routines implementing call valetparking *  * 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 <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/features.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 45000static 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;AST_MUTEX_DEFINE_STATIC(valetparking_lock);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_in = 0;		config.allowdisconnect_out = 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;}