/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer <markster@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. *//*! \file * * \brief Channel Management * * \author Mark Spencer <markster@digium.com> */#include"asterisk.h"ASTERISK_FILE_VERSION(__FILE__,"$Revision$")#include"asterisk/_private.h"#include<sys/time.h>#include<signal.h>#include<math.h>#include"asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */#include"asterisk/pbx.h"#include"asterisk/frame.h"#include"asterisk/mod_format.h"#include"asterisk/sched.h"#include"asterisk/channel.h"#include"asterisk/musiconhold.h"#include"asterisk/say.h"#include"asterisk/file.h"#include"asterisk/cli.h"#include"asterisk/translate.h"#include"asterisk/manager.h"#include"asterisk/cel.h"#include"asterisk/chanvars.h"#include"asterisk/linkedlists.h"#include"asterisk/indications.h"#include"asterisk/monitor.h"#include"asterisk/causes.h"#include"asterisk/callerid.h"#include"asterisk/utils.h"#include"asterisk/lock.h"#include"asterisk/app.h"#include"asterisk/transcap.h"#include"asterisk/devicestate.h"#include"asterisk/sha1.h"#include"asterisk/threadstorage.h"#include"asterisk/slinfactory.h"#include"asterisk/audiohook.h"#include"asterisk/framehook.h"#include"asterisk/timing.h"#include"asterisk/autochan.h"#include"asterisk/stringfields.h"#include"asterisk/global_datastores.h"#include"asterisk/data.h"#ifdef HAVE_EPOLL#include<sys/epoll.h>#endif#if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)#if defined(HAVE_PRI)#include"libpri.h"#endif /* defined(HAVE_PRI) */#endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */structast_epoll_data{structast_channel*chan;intwhich;};/* uncomment if you have problems with 'monitoring' synchronized files */#if 0#define MONITOR_CONSTANT_DELAY#define MONITOR_DELAY 150 * 8 /*!< 150 ms of MONITORING DELAY */#endif/*! \brief Prevent new channel allocation if shutting down. */staticintshutting_down;staticintuniqueint;unsignedlongglobal_fin,global_fout;AST_THREADSTORAGE(state2str_threadbuf);#define STATE2STR_BUFSIZE 32/*! Default amount of time to use when emulating a digit as a begin and end * 100ms */#define AST_DEFAULT_EMULATE_DTMF_DURATION 100/*! Minimum allowed digit length - 80ms */#define AST_MIN_DTMF_DURATION 80/*! Minimum amount of time between the end of the last digit and the beginning * of a new one - 45ms */#define AST_MIN_DTMF_GAP 45/*! \brief List of channel drivers */structchanlist{conststructast_channel_tech*tech;AST_LIST_ENTRY(chanlist)list;};#ifdef CHANNEL_TRACE/*! \brief Structure to hold channel context backtrace data */structast_chan_trace_data{intenabled;AST_LIST_HEAD_NOLOCK(,ast_chan_trace)trace;};/*! \brief Structure to save contexts where an ast_chan has been into */structast_chan_trace{charcontext[AST_MAX_CONTEXT];charexten[AST_MAX_EXTENSION];intpriority;AST_LIST_ENTRY(ast_chan_trace)entry;};#endif/*! \brief the list of registered channel types */staticAST_RWLIST_HEAD_STATIC(backends,chanlist);#ifdef LOW_MEMORY#define NUM_CHANNEL_BUCKETS 61#else#define NUM_CHANNEL_BUCKETS 1567#endif#if 0 /* XXX AstData: ast_callerid no longer exists. (Equivalent code not readily apparent.) */#define DATA_EXPORT_CALLERID(MEMBER) \ MEMBER(ast_callerid, cid_dnid, AST_DATA_STRING) \ MEMBER(ast_callerid, cid_num, AST_DATA_STRING) \ MEMBER(ast_callerid, cid_name, AST_DATA_STRING) \ MEMBER(ast_callerid, cid_ani, AST_DATA_STRING) \ MEMBER(ast_callerid, cid_pres, AST_DATA_INTEGER) \ MEMBER(ast_callerid, cid_ani2, AST_DATA_INTEGER) \ MEMBER(ast_callerid, cid_tag, AST_DATA_STRING)AST_DATA_STRUCTURE(ast_callerid, DATA_EXPORT_CALLERID);#endif#define DATA_EXPORT_CHANNEL(MEMBER) \ MEMBER(ast_channel, blockproc, AST_DATA_STRING) \ MEMBER(ast_channel, appl, AST_DATA_STRING) \ MEMBER(ast_channel, data, AST_DATA_STRING) \ MEMBER(ast_channel, name, AST_DATA_STRING) \ MEMBER(ast_channel, language, AST_DATA_STRING) \ MEMBER(ast_channel, musicclass, AST_DATA_STRING) \ MEMBER(ast_channel, accountcode, AST_DATA_STRING) \ MEMBER(ast_channel, peeraccount, AST_DATA_STRING) \ MEMBER(ast_channel, userfield, AST_DATA_STRING) \ MEMBER(ast_channel, call_forward, AST_DATA_STRING) \ MEMBER(ast_channel, uniqueid, AST_DATA_STRING) \ MEMBER(ast_channel, linkedid, AST_DATA_STRING) \ MEMBER(ast_channel, parkinglot, AST_DATA_STRING) \ MEMBER(ast_channel, hangupsource, AST_DATA_STRING) \ MEMBER(ast_channel, dialcontext, AST_DATA_STRING) \ MEMBER(ast_channel, rings, AST_DATA_INTEGER) \ MEMBER(ast_channel, priority, AST_DATA_INTEGER) \ MEMBER(ast_channel, macropriority, AST_DATA_INTEGER) \ MEMBER(ast_channel, adsicpe, AST_DATA_INTEGER) \ MEMBER(ast_channel, fin, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, fout, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, emulate_dtmf_duration, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, visible_indication, AST_DATA_INTEGER) \ MEMBER(ast_channel, context, AST_DATA_STRING) \ MEMBER(ast_channel, exten, AST_DATA_STRING) \ MEMBER(ast_channel, macrocontext, AST_DATA_STRING) \ MEMBER(ast_channel, macroexten, AST_DATA_STRING)AST_DATA_STRUCTURE(ast_channel,DATA_EXPORT_CHANNEL);/*! \brief All active channels on the system */staticstructao2_container*channels;/*! \brief map AST_CAUSE's to readable string representations * * \ref causes.h*/staticconststruct{intcause;constchar*name;constchar*desc;}causes[]={{AST_CAUSE_UNALLOCATED,"UNALLOCATED","Unallocated (unassigned) number"},{AST_CAUSE_NO_ROUTE_TRANSIT_NET,"NO_ROUTE_TRANSIT_NET","No route to specified transmit network"},{AST_CAUSE_NO_ROUTE_DESTINATION,"NO_ROUTE_DESTINATION","No route to destination"},{AST_CAUSE_CHANNEL_UNACCEPTABLE,"CHANNEL_UNACCEPTABLE","Channel unacceptable"},{AST_CAUSE_CALL_AWARDED_DELIVERED,"CALL_AWARDED_DELIVERED","Call awarded and being delivered in an established channel"},{AST_CAUSE_NORMAL_CLEARING,"NORMAL_CLEARING","Normal Clearing"},{AST_CAUSE_USER_BUSY,"USER_BUSY","User busy"},{AST_CAUSE_NO_USER_RESPONSE,"NO_USER_RESPONSE","No user responding"},{AST_CAUSE_NO_ANSWER,"NO_ANSWER","User alerting, no answer"},{AST_CAUSE_CALL_REJECTED,"CALL_REJECTED","Call Rejected"},{AST_CAUSE_NUMBER_CHANGED,"NUMBER_CHANGED","Number changed"},{AST_CAUSE_DESTINATION_OUT_OF_ORDER,"DESTINATION_OUT_OF_ORDER","Destination out of order"},{AST_CAUSE_INVALID_NUMBER_FORMAT,"INVALID_NUMBER_FORMAT","Invalid number format"},{AST_CAUSE_FACILITY_REJECTED,"FACILITY_REJECTED","Facility rejected"},{AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY,"RESPONSE_TO_STATUS_ENQUIRY","Response to STATus ENQuiry"},{AST_CAUSE_NORMAL_UNSPECIFIED,"NORMAL_UNSPECIFIED","Normal, unspecified"},{AST_CAUSE_NORMAL_CIRCUIT_CONGESTION,"NORMAL_CIRCUIT_CONGESTION","Circuit/channel congestion"},{AST_CAUSE_NETWORK_OUT_OF_ORDER,"NETWORK_OUT_OF_ORDER","Network out of order"},{AST_CAUSE_NORMAL_TEMPORARY_FAILURE,"NORMAL_TEMPORARY_FAILURE","Temporary failure"},{AST_CAUSE_SWITCH_CONGESTION,"SWITCH_CONGESTION","Switching equipment congestion"},{AST_CAUSE_ACCESS_INFO_DISCARDED,"ACCESS_INFO_DISCARDED","Access information discarded"},{AST_CAUSE_REQUESTED_CHAN_UNAVAIL,"REQUESTED_CHAN_UNAVAIL","Requested channel not available"},{AST_CAUSE_PRE_EMPTED,"PRE_EMPTED","Pre-empted"},{AST_CAUSE_FACILITY_NOT_SUBSCRIBED,"FACILITY_NOT_SUBSCRIBED","Facility not subscribed"},{AST_CAUSE_OUTGOING_CALL_BARRED,"OUTGOING_CALL_BARRED","Outgoing call barred"},{AST_CAUSE_INCOMING_CALL_BARRED,"INCOMING_CALL_BARRED","Incoming call barred"},{AST_CAUSE_BEARERCAPABILITY_NOTAUTH,"BEARERCAPABILITY_NOTAUTH","Bearer capability not authorized"},{AST_CAUSE_BEARERCAPABILITY_NOTAVAIL,"BEARERCAPABILITY_NOTAVAIL","Bearer capability not available"},{AST_CAUSE_BEARERCAPABILITY_NOTIMPL,"BEARERCAPABILITY_NOTIMPL","Bearer capability not implemented"},{AST_CAUSE_CHAN_NOT_IMPLEMENTED,"CHAN_NOT_IMPLEMENTED","Channel not implemented"},{AST_CAUSE_FACILITY_NOT_IMPLEMENTED,"FACILITY_NOT_IMPLEMENTED","Facility not implemented"},{AST_CAUSE_INVALID_CALL_REFERENCE,"INVALID_CALL_REFERENCE","Invalid call reference value"},{AST_CAUSE_INCOMPATIBLE_DESTINATION,"INCOMPATIBLE_DESTINATION","Incompatible destination"},{AST_CAUSE_INVALID_MSG_UNSPECIFIED,"INVALID_MSG_UNSPECIFIED","Invalid message unspecified"},{AST_CAUSE_MANDATORY_IE_MISSING,"MANDATORY_IE_MISSING","Mandatory information element is missing"},{AST_CAUSE_MESSAGE_TYPE_NONEXIST,"MESSAGE_TYPE_NONEXIST","Message type nonexist."},{AST_CAUSE_WRONG_MESSAGE,"WRONG_MESSAGE","Wrong message"},{AST_CAUSE_IE_NONEXIST,"IE_NONEXIST","Info. element nonexist or not implemented"},{AST_CAUSE_INVALID_IE_CONTENTS,"INVALID_IE_CONTENTS","Invalid information element contents"},{AST_CAUSE_WRONG_CALL_STATE,"WRONG_CALL_STATE","Message not compatible with call state"},{AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE,"RECOVERY_ON_TIMER_EXPIRE","Recover on timer expiry"},{AST_CAUSE_MANDATORY_IE_LENGTH_ERROR,"MANDATORY_IE_LENGTH_ERROR","Mandatory IE length error"},{AST_CAUSE_PROTOCOL_ERROR,"PROTOCOL_ERROR","Protocol error, unspecified"},{AST_CAUSE_INTERWORKING,"INTERWORKING","Interworking, unspecified"},};structast_variable*ast_channeltype_list(void){structchanlist*cl;structast_variable*var=NULL,*prev=NULL;AST_RWLIST_RDLOCK(&backends);AST_RWLIST_TRAVERSE(&backends,cl,list){if(prev){if((prev->next=ast_variable_new(cl->tech->type,cl->tech->description,"")))prev=prev->next;}else{var=ast_variable_new(cl->tech->type,cl->tech->description,"");prev=var;}}AST_RWLIST_UNLOCK(&backends);returnvar;}staticvoidchannel_data_add_flags(structast_data*tree,structast_channel*chan){ast_data_add_bool(tree,"DEFER_DTMF",ast_test_flag(chan,AST_FLAG_DEFER_DTMF));ast_data_add_bool(tree,"WRITE_INT",ast_test_flag(chan,AST_FLAG_WRITE_INT));ast_data_add_bool(tree,"BLOCKING",ast_test_flag(chan,AST_FLAG_BLOCKING));ast_data_add_bool(tree,"ZOMBIE",ast_test_flag(chan,AST_FLAG_ZOMBIE));ast_data_add_bool(tree,"EXCEPTION",ast_test_flag(chan,AST_FLAG_EXCEPTION));ast_data_add_bool(tree,"MOH",ast_test_flag(chan,AST_FLAG_MOH));ast_data_add_bool(tree,"SPYING",ast_test_flag(chan,AST_FLAG_SPYING));ast_data_add_bool(tree,"NBRIDGE",ast_test_flag(chan,AST_FLAG_NBRIDGE));ast_data_add_bool(tree,"IN_AUTOLOOP",ast_test_flag(chan,AST_FLAG_IN_AUTOLOOP));ast_data_add_bool(tree,"OUTGOING",ast_test_flag(chan,AST_FLAG_OUTGOING));ast_data_add_bool(tree,"IN_DTMF",ast_test_flag(chan,AST_FLAG_IN_DTMF));ast_data_add_bool(tree,"EMULATE_DTMF",ast_test_flag(chan,AST_FLAG_EMULATE_DTMF));ast_data_add_bool(tree,"END_DTMF_ONLY",ast_test_flag(chan,AST_FLAG_END_DTMF_ONLY));ast_data_add_bool(tree,"ANSWERED_ELSEWHERE",ast_test_flag(chan,AST_FLAG_ANSWERED_ELSEWHERE));ast_data_add_bool(tree,"MASQ_NOSTREAM",ast_test_flag(chan,AST_FLAG_MASQ_NOSTREAM));ast_data_add_bool(tree,"BRIDGE_HANGUP_RUN",ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_RUN));ast_data_add_bool(tree,"BRIDGE_HANGUP_DONT",ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT));ast_data_add_bool(tree,"DISABLE_WORKAROUNDS",ast_test_flag(chan,AST_FLAG_DISABLE_WORKAROUNDS));}#if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)staticconstchar*party_number_ton2str(intton){#if defined(HAVE_PRI)switch((ton>>4)&0x07){casePRI_TON_INTERNATIONAL:return"International";casePRI_TON_NATIONAL:return"National";casePRI_TON_NET_SPECIFIC:return"Network Specific";casePRI_TON_SUBSCRIBER:return"Subscriber";casePRI_TON_ABBREVIATED:return"Abbreviated";casePRI_TON_RESERVED:return"Reserved";casePRI_TON_UNKNOWN:default:break;}#endif /* defined(HAVE_PRI) */return"Unknown";}#endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */#if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)staticconstchar*party_number_plan2str(intplan){#if defined(HAVE_PRI)switch(plan&0x0F){default:casePRI_NPI_UNKNOWN:break;casePRI_NPI_E163_E164:return"Public (E.163/E.164)";casePRI_NPI_X121:return"Data (X.121)";casePRI_NPI_F69:return"Telex (F.69)";casePRI_NPI_NATIONAL:return"National Standard";casePRI_NPI_PRIVATE:return"Private";casePRI_NPI_RESERVED:return"Reserved";}#endif /* defined(HAVE_PRI) */return"Unknown";}#endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */intast_channel_data_add_structure(structast_data*tree,structast_channel*chan,intadd_bridged){structast_channel*bc;structast_data*data_bridged;structast_data*data_cdr;structast_data*data_flags;structast_data*data_zones;structast_data*enum_node;structast_data*data_softhangup;#if 0 /* XXX AstData: ast_callerid no longer exists. (Equivalent code not readily apparent.) */ struct ast_data *data_callerid; char value_str[100];#endifif(!tree){return-1;}ast_data_add_structure(ast_channel,tree,chan);if(add_bridged){bc=ast_bridged_channel(chan);if(bc){data_bridged=ast_data_add_node(tree,"bridged");if(!data_bridged){return-1;}ast_channel_data_add_structure(data_bridged,bc,0);}}ast_data_add_codec(tree,"oldwriteformat",&chan->oldwriteformat);ast_data_add_codec(tree,"readformat",&chan->readformat);ast_data_add_codec(tree,"writeformat",&chan->writeformat);ast_data_add_codec(tree,"rawreadformat",&chan->rawreadformat);ast_data_add_codec(tree,"rawwriteformat",&chan->rawwriteformat);ast_data_add_codecs(tree,"nativeformats",chan->nativeformats);/* state */enum_node=ast_data_add_node(tree,"state");if(!enum_node){return-1;}ast_data_add_str(enum_node,"text",ast_state2str(chan->_state));ast_data_add_int(enum_node,"value",chan->_state);/* hangupcause */enum_node=ast_data_add_node(tree,"hangupcause");if(!enum_node){return-1;}ast_data_add_str(enum_node,"text",ast_cause2str(chan->hangupcause));ast_data_add_int(enum_node,"value",chan->hangupcause);/* amaflags */enum_node=ast_data_add_node(tree,"amaflags");if(!enum_node){return-1;}ast_data_add_str(enum_node,"text",ast_cdr_flags2str(chan->amaflags));ast_data_add_int(enum_node,"value",chan->amaflags);/* transfercapability */enum_node=ast_data_add_node(tree,"transfercapability");if(!enum_node){return-1;}ast_data_add_str(enum_node,"text",ast_transfercapability2str(chan->transfercapability));ast_data_add_int(enum_node,"value",chan->transfercapability);/* _softphangup */data_softhangup=ast_data_add_node(tree,"softhangup");if(!data_softhangup){return-1;}ast_data_add_bool(data_softhangup,"dev",chan->_softhangup&AST_SOFTHANGUP_DEV);ast_data_add_bool(data_softhangup,"asyncgoto",chan->_softhangup&AST_SOFTHANGUP_ASYNCGOTO);ast_data_add_bool(data_softhangup,"shutdown",chan->_softhangup&AST_SOFTHANGUP_SHUTDOWN);ast_data_add_bool(data_softhangup,"timeout",chan->_softhangup&AST_SOFTHANGUP_TIMEOUT);ast_data_add_bool(data_softhangup,"appunload",chan->_softhangup&AST_SOFTHANGUP_APPUNLOAD);ast_data_add_bool(data_softhangup,"explicit",chan->_softhangup&AST_SOFTHANGUP_EXPLICIT);ast_data_add_bool(data_softhangup,"unbridge",chan->_softhangup&AST_SOFTHANGUP_UNBRIDGE);/* channel flags */data_flags=ast_data_add_node(tree,"flags");if(!data_flags){return-1;}channel_data_add_flags(data_flags,chan);ast_data_add_uint(tree,"timetohangup",chan->whentohangup.tv_sec);#if 0 /* XXX AstData: ast_callerid no longer exists. (Equivalent code not readily apparent.) */ /* callerid */ data_callerid = ast_data_add_node(tree, "callerid"); if (!data_callerid) { return -1; } ast_data_add_structure(ast_callerid, data_callerid, &(chan->cid)); /* insert the callerid ton */ enum_node = ast_data_add_node(data_callerid, "cid_ton"); if (!enum_node) { return -1; } ast_data_add_int(enum_node, "value", chan->cid.cid_ton); snprintf(value_str, sizeof(value_str), "TON: %s/Plan: %s", party_number_ton2str(chan->cid.cid_ton), party_number_plan2str(chan->cid.cid_ton)); ast_data_add_str(enum_node, "text", value_str);#endif/* tone zone */if(chan->zone){data_zones=ast_data_add_node(tree,"zone");if(!data_zones){return-1;}ast_tone_zone_data_add_structure(data_zones,chan->zone);}/* insert cdr */data_cdr=ast_data_add_node(tree,"cdr");if(!data_cdr){return-1;}ast_cdr_data_add_structure(data_cdr,chan->cdr,1);return0;}intast_channel_data_cmp_structure(conststructast_data_search*tree,structast_channel*chan,constchar*structure_name){returnast_data_search_cmp_structure(tree,ast_channel,chan,structure_name);}/*! \brief Show channel types - CLI command */staticchar*handle_cli_core_show_channeltypes(structast_cli_entry*e,intcmd,structast_cli_args*a){#define FORMAT "%-10.10s %-40.40s %-12.12s %-12.12s %-12.12s\n"structchanlist*cl;intcount_chan=0;switch(cmd){caseCLI_INIT:e->command="core show channeltypes";e->usage="Usage: core show channeltypes\n"" Lists available channel types registered in your\n"" Asterisk server.\n";returnNULL;caseCLI_GENERATE:returnNULL;}if(a->argc!=3)returnCLI_SHOWUSAGE;ast_cli(a->fd,FORMAT,"Type","Description","Devicestate","Indications","Transfer");ast_cli(a->fd,FORMAT,"----------","-----------","-----------","-----------","--------");AST_RWLIST_RDLOCK(&backends);AST_RWLIST_TRAVERSE(&backends,cl,list){ast_cli(a->fd,FORMAT,cl->tech->type,cl->tech->description,(cl->tech->devicestate)?"yes":"no",(cl->tech->indicate)?"yes":"no",(cl->tech->transfer)?"yes":"no");count_chan++;}AST_RWLIST_UNLOCK(&backends);ast_cli(a->fd,"----------\n%d channel drivers registered.\n",count_chan);returnCLI_SUCCESS;#undef FORMAT}staticchar*complete_channeltypes(structast_cli_args*a){structchanlist*cl;intwhich=0;intwordlen;char*ret=NULL;if(a->pos!=3)returnNULL;wordlen=strlen(a->word);AST_RWLIST_RDLOCK(&backends);AST_RWLIST_TRAVERSE(&backends,cl,list){if(!strncasecmp(a->word,cl->tech->type,wordlen)&&++which>a->n){ret=ast_strdup(cl->tech->type);break;}}AST_RWLIST_UNLOCK(&backends);returnret;}/*! \brief Show details about a channel driver - CLI command */staticchar*handle_cli_core_show_channeltype(structast_cli_entry*e,intcmd,structast_cli_args*a){structchanlist*cl=NULL;charbuf[512];switch(cmd){caseCLI_INIT:e->command="core show channeltype";e->usage="Usage: core show channeltype <name>\n"" Show details about the specified channel type, <name>.\n";returnNULL;caseCLI_GENERATE:returncomplete_channeltypes(a);}if(a->argc!=4)returnCLI_SHOWUSAGE;AST_RWLIST_RDLOCK(&backends);AST_RWLIST_TRAVERSE(&backends,cl,list){if(!strncasecmp(cl->tech->type,a->argv[3],strlen(cl->tech->type)))break;}if(!cl){ast_cli(a->fd,"\n%s is not a registered channel driver.\n",a->argv[3]);AST_RWLIST_UNLOCK(&backends);returnCLI_FAILURE;}ast_cli(a->fd,"-- Info about channel driver: %s --\n"" Device State: %s\n"" Indication: %s\n"" Transfer : %s\n"" Capabilities: %s\n"" Digit Begin: %s\n"" Digit End: %s\n"" Send HTML : %s\n"" Image Support: %s\n"" Text Support: %s\n",cl->tech->type,(cl->tech->devicestate)?"yes":"no",(cl->tech->indicate)?"yes":"no",(cl->tech->transfer)?"yes":"no",ast_getformatname_multiple(buf,sizeof(buf),cl->tech->capabilities),(cl->tech->send_digit_begin)?"yes":"no",(cl->tech->send_digit_end)?"yes":"no",(cl->tech->send_html)?"yes":"no",(cl->tech->send_image)?"yes":"no",(cl->tech->send_text)?"yes":"no");AST_RWLIST_UNLOCK(&backends);returnCLI_SUCCESS;}staticstructast_cli_entrycli_channel[]={AST_CLI_DEFINE(handle_cli_core_show_channeltypes,"List available channel types"),AST_CLI_DEFINE(handle_cli_core_show_channeltype,"Give more details on that channel type")};staticstructast_frame*kill_read(structast_channel*chan){/* Hangup channel. */returnNULL;}staticstructast_frame*kill_exception(structast_channel*chan){/* Hangup channel. */returnNULL;}staticintkill_write(structast_channel*chan,structast_frame*frame){/* Hangup channel. */return-1;}staticintkill_fixup(structast_channel*oldchan,structast_channel*newchan){/* No problem fixing up the channel. */return0;}staticintkill_hangup(structast_channel*chan){chan->tech_pvt=NULL;return0;}/*! * \brief Kill the channel channel driver technology descriptor. * * \details * The purpose of this channel technology is to encourage the * channel to hangup as quickly as possible. * * \note Used by DTMF atxfer and zombie channels. */conststructast_channel_techast_kill_tech={.type="Kill",.description="Kill channel (should not see this)",.read=kill_read,.exception=kill_exception,.write=kill_write,.fixup=kill_fixup,.hangup=kill_hangup,};#ifdef CHANNEL_TRACE/*! \brief Destructor for the channel trace datastore */staticvoidast_chan_trace_destroy_cb(void*data){structast_chan_trace*trace;structast_chan_trace_data*traced=data;while((trace=AST_LIST_REMOVE_HEAD(&traced->trace,entry))){ast_free(trace);}ast_free(traced);}/*! \brief Datastore to put the linked list of ast_chan_trace and trace status */staticconststructast_datastore_infoast_chan_trace_datastore_info={.type="ChanTrace",.destroy=ast_chan_trace_destroy_cb};/*! \brief Put the channel backtrace in a string */intast_channel_trace_serialize(structast_channel*chan,structast_str**buf){inttotal=0;structast_chan_trace*trace;structast_chan_trace_data*traced;structast_datastore*store;ast_channel_lock(chan);store=ast_channel_datastore_find(chan,&ast_chan_trace_datastore_info,NULL);if(!store){ast_channel_unlock(chan);returntotal;}traced=store->data;ast_str_reset(*buf);AST_LIST_TRAVERSE(&traced->trace,trace,entry){if(ast_str_append(buf,0,"[%d] => %s, %s, %d\n",total,trace->context,trace->exten,trace->priority)<0){ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");total=-1;break;}total++;}ast_channel_unlock(chan);returntotal;}/* !\brief Whether or not context tracing is enabled */intast_channel_trace_is_enabled(structast_channel*chan){structast_datastore*store=ast_channel_datastore_find(chan,&ast_chan_trace_datastore_info,NULL);if(!store)return0;return((structast_chan_trace_data*)store->data)->enabled;}/*! \brief Update the context backtrace data if tracing is enabled */staticintast_channel_trace_data_update(structast_channel*chan,structast_chan_trace_data*traced){structast_chan_trace*trace;if(!traced->enabled)return0;/* If the last saved context does not match the current one OR we have not saved any context so far, then save the current context */if((!AST_LIST_EMPTY(&traced->trace)&&strcasecmp(AST_LIST_FIRST(&traced->trace)->context,chan->context))||(AST_LIST_EMPTY(&traced->trace))){/* Just do some debug logging */if(AST_LIST_EMPTY(&traced->trace))ast_debug(1,"Setting initial trace context to %s\n",chan->context);elseast_debug(1,"Changing trace context from %s to %s\n",AST_LIST_FIRST(&traced->trace)->context,chan->context);/* alloc or bail out */trace=ast_malloc(sizeof(*trace));if(!trace)return-1;/* save the current location and store it in the trace list */ast_copy_string(trace->context,chan->context,sizeof(trace->context));ast_copy_string(trace->exten,chan->exten,sizeof(trace->exten));trace->priority=chan->priority;AST_LIST_INSERT_HEAD(&traced->trace,trace,entry);}return0;}/*! \brief Update the context backtrace if tracing is enabled */intast_channel_trace_update(structast_channel*chan){structast_datastore*store=ast_channel_datastore_find(chan,&ast_chan_trace_datastore_info,NULL);if(!store)return0;returnast_channel_trace_data_update(chan,store->data);}/*! \brief Enable context tracing in the channel */intast_channel_trace_enable(structast_channel*chan){structast_datastore*store=ast_channel_datastore_find(chan,&ast_chan_trace_datastore_info,NULL);structast_chan_trace_data*traced;if(!store){store=ast_datastore_alloc(&ast_chan_trace_datastore_info,"ChanTrace");if(!store)return-1;traced=ast_calloc(1,sizeof(*traced));if(!traced){ast_datastore_free(store);return-1;}store->data=traced;AST_LIST_HEAD_INIT_NOLOCK(&traced->trace);ast_channel_datastore_add(chan,store);}((structast_chan_trace_data*)store->data)->enabled=1;ast_channel_trace_data_update(chan,store->data);return0;}/*! \brief Disable context tracing in the channel */intast_channel_trace_disable(structast_channel*chan){structast_datastore*store=ast_channel_datastore_find(chan,&ast_chan_trace_datastore_info,NULL);if(!store)return0;((structast_chan_trace_data*)store->data)->enabled=0;return0;}#endif /* CHANNEL_TRACE *//*! \brief Checks to see if a channel is needing hang up */intast_check_hangup(structast_channel*chan){if(chan->_softhangup)/* yes if soft hangup flag set */return1;if(ast_tvzero(chan->whentohangup))/* no if no hangup scheduled */return0;if(ast_tvdiff_ms(chan->whentohangup,ast_tvnow())>0)/* no if hangup time has not come yet. */return0;ast_debug(4,"Hangup time has come: %"PRIi64"\n",ast_tvdiff_ms(chan->whentohangup,ast_tvnow()));chan->_softhangup|=AST_SOFTHANGUP_TIMEOUT;/* record event */return1;}intast_check_hangup_locked(structast_channel*chan){intres;ast_channel_lock(chan);res=ast_check_hangup(chan);ast_channel_unlock(chan);returnres;}staticintast_channel_softhangup_cb(void*obj,void*arg,intflags){structast_channel*chan=obj;ast_softhangup(chan,AST_SOFTHANGUP_SHUTDOWN);return0;}voidast_begin_shutdown(inthangup){shutting_down=1;if(hangup){ao2_callback(channels,OBJ_NODATA|OBJ_MULTIPLE,ast_channel_softhangup_cb,NULL);}}/*! \brief returns number of active/allocated channels */intast_active_channels(void){returnchannels?ao2_container_count(channels):0;}/*! \brief Cancel a shutdown in progress */voidast_cancel_shutdown(void){shutting_down=0;}/*! \brief Returns non-zero if Asterisk is being shut down */intast_shutting_down(void){returnshutting_down;}/*! \brief Set when to hangup channel */voidast_channel_setwhentohangup_tv(structast_channel*chan,structtimevaloffset){chan->whentohangup=ast_tvzero(offset)?offset:ast_tvadd(offset,ast_tvnow());ast_queue_frame(chan,&ast_null_frame);return;}voidast_channel_setwhentohangup(structast_channel*chan,time_toffset){structtimevalwhen={offset,};ast_channel_setwhentohangup_tv(chan,when);}/*! \brief Compare a offset with when to hangup channel */intast_channel_cmpwhentohangup_tv(structast_channel*chan,structtimevaloffset){structtimevalwhentohangup;if(ast_tvzero(chan->whentohangup))returnast_tvzero(offset)?0:-1;if(ast_tvzero(offset))return1;whentohangup=ast_tvadd(offset,ast_tvnow());returnast_tvdiff_ms(whentohangup,chan->whentohangup);}intast_channel_cmpwhentohangup(structast_channel*chan,time_toffset){structtimevalwhen={offset,};returnast_channel_cmpwhentohangup_tv(chan,when);}/*! \brief Register a new telephony channel in Asterisk */intast_channel_register(conststructast_channel_tech*tech){structchanlist*chan;AST_RWLIST_WRLOCK(&backends);AST_RWLIST_TRAVERSE(&backends,chan,list){if(!strcasecmp(tech->type,chan->tech->type)){ast_log(LOG_WARNING,"Already have a handler for type '%s'\n",tech->type);AST_RWLIST_UNLOCK(&backends);return-1;}}if(!(chan=ast_calloc(1,sizeof(*chan)))){AST_RWLIST_UNLOCK(&backends);return-1;}chan->tech=tech;AST_RWLIST_INSERT_HEAD(&backends,chan,list);ast_debug(1,"Registered handler for '%s' (%s)\n",chan->tech->type,chan->tech->description);ast_verb(2,"Registered channel type '%s' (%s)\n",chan->tech->type,chan->tech->description);AST_RWLIST_UNLOCK(&backends);return0;}/*! \brief Unregister channel driver */voidast_channel_unregister(conststructast_channel_tech*tech){structchanlist*chan;ast_debug(1,"Unregistering channel type '%s'\n",tech->type);AST_RWLIST_WRLOCK(&backends);AST_RWLIST_TRAVERSE_SAFE_BEGIN(&backends,chan,list){if(chan->tech==tech){AST_LIST_REMOVE_CURRENT(list);ast_free(chan);ast_verb(2,"Unregistered channel type '%s'\n",tech->type);break;}}AST_LIST_TRAVERSE_SAFE_END;AST_RWLIST_UNLOCK(&backends);}/*! \brief Get handle to channel driver based on name */conststructast_channel_tech*ast_get_channel_tech(constchar*name){structchanlist*chanls;conststructast_channel_tech*ret=NULL;AST_RWLIST_RDLOCK(&backends);AST_RWLIST_TRAVERSE(&backends,chanls,list){if(!strcasecmp(name,chanls->tech->type)){ret=chanls->tech;break;}}AST_RWLIST_UNLOCK(&backends);returnret;}/*! \brief Gives the string form of a given hangup cause */constchar*ast_cause2str(intcause){intx;for(x=0;x<ARRAY_LEN(causes);x++){if(causes[x].cause==cause)returncauses[x].desc;}return"Unknown";}/*! \brief Convert a symbolic hangup cause to number */intast_str2cause(constchar*name){intx;for(x=0;x<ARRAY_LEN(causes);x++)if(!strncasecmp(causes[x].name,name,strlen(causes[x].name)))returncauses[x].cause;return-1;}/*! \brief Gives the string form of a given channel state. \note This function is not reentrant. */constchar*ast_state2str(enumast_channel_statestate){char*buf;switch(state){caseAST_STATE_DOWN:return"Down";caseAST_STATE_RESERVED:return"Rsrvd";caseAST_STATE_OFFHOOK:return"OffHook";caseAST_STATE_DIALING:return"Dialing";caseAST_STATE_RING:return"Ring";caseAST_STATE_RINGING:return"Ringing";caseAST_STATE_UP:return"Up";caseAST_STATE_BUSY:return"Busy";caseAST_STATE_DIALING_OFFHOOK:return"Dialing Offhook";caseAST_STATE_PRERING:return"Pre-ring";default:if(!(buf=ast_threadstorage_get(&state2str_threadbuf,STATE2STR_BUFSIZE)))return"Unknown";snprintf(buf,STATE2STR_BUFSIZE,"Unknown (%d)",state);returnbuf;}}/*! \brief Gives the string form of a given transfer capability */char*ast_transfercapability2str(inttransfercapability){switch(transfercapability){caseAST_TRANS_CAP_SPEECH:return"SPEECH";caseAST_TRANS_CAP_DIGITAL:return"DIGITAL";caseAST_TRANS_CAP_RESTRICTED_DIGITAL:return"RESTRICTED_DIGITAL";caseAST_TRANS_CAP_3_1K_AUDIO:return"3K1AUDIO";caseAST_TRANS_CAP_DIGITAL_W_TONES:return"DIGITAL_W_TONES";caseAST_TRANS_CAP_VIDEO:return"VIDEO";default:return"UNKNOWN";}}/*! \brief Pick the best audio codec */structast_format*ast_best_codec(structast_format_cap*cap,structast_format*result){/* This just our opinion, expressed in code. We are asked to choose the best codec to use, given no information */staticconstenumast_format_idprefs[]={/*! Okay, ulaw is used by all telephony equipment, so start with it */AST_FORMAT_ULAW,/*! Unless of course, you're a silly European, so then prefer ALAW */AST_FORMAT_ALAW,AST_FORMAT_G719,AST_FORMAT_SIREN14,AST_FORMAT_SIREN7,AST_FORMAT_TESTLAW,/*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */AST_FORMAT_G722,/*! Okay, well, signed linear is easy to translate into other stuff */AST_FORMAT_SLINEAR192,AST_FORMAT_SLINEAR96,AST_FORMAT_SLINEAR48,AST_FORMAT_SLINEAR44,AST_FORMAT_SLINEAR32,AST_FORMAT_SLINEAR24,AST_FORMAT_SLINEAR16,AST_FORMAT_SLINEAR12,AST_FORMAT_SLINEAR,/*! G.726 is standard ADPCM, in RFC3551 packing order */AST_FORMAT_G726,/*! G.726 is standard ADPCM, in AAL2 packing order */AST_FORMAT_G726_AAL2,/*! ADPCM has great sound quality and is still pretty easy to translate */AST_FORMAT_ADPCM,/*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to translate and sounds pretty good */AST_FORMAT_GSM,/*! iLBC is not too bad */AST_FORMAT_ILBC,/*! Speex is free, but computationally more expensive than GSM */AST_FORMAT_SPEEX32,AST_FORMAT_SPEEX16,AST_FORMAT_SPEEX,/*! SILK is pretty awesome. */AST_FORMAT_SILK,/*! CELT supports crazy high sample rates */AST_FORMAT_CELT,/*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough to use it */AST_FORMAT_LPC10,/*! G.729a is faster than 723 and slightly less expensive */AST_FORMAT_G729A,/*! Down to G.723.1 which is proprietary but at least designed for voice */AST_FORMAT_G723_1,};charbuf[512];intx;/* Find the first preferred codec in the format given */for(x=0;x<ARRAY_LEN(prefs);x++){if(ast_format_cap_best_byid(cap,prefs[x],result)){returnresult;}}ast_format_clear(result);ast_log(LOG_WARNING,"Don't know any of %s formats\n",ast_getformatname_multiple(buf,sizeof(buf),cap));returnNULL;}staticconststructast_channel_technull_tech={.type="NULL",.description="Null channel (should not see this)",};staticvoidast_channel_destructor(void*obj);staticvoidast_dummy_channel_destructor(void*obj);/*! \brief Create a new channel structure */staticstructast_channel*attribute_malloc__attribute__((format(printf,13,0)))__ast_channel_alloc_ap(intneedqueue,intstate,constchar*cid_num,constchar*cid_name,constchar*acctcode,constchar*exten,constchar*context,constchar*linkedid,constintamaflag,constchar*file,intline,constchar*function,constchar*name_fmt,va_listap){structast_channel*tmp;intx;intflags;structvarshead*headp;char*tech="",*tech2=NULL;/* If shutting down, don't allocate any new channels */if(shutting_down){ast_log(LOG_WARNING,"Channel allocation failed: Refusing due to active shutdown\n");returnNULL;}#if defined(REF_DEBUG)tmp=__ao2_alloc_debug(sizeof(*tmp),ast_channel_destructor,"",file,line,function,1);#elif defined(__AST_DEBUG_MALLOC)tmp=__ao2_alloc_debug(sizeof(*tmp),ast_channel_destructor,"",file,line,function,0);#elsetmp=ao2_alloc(sizeof(*tmp),ast_channel_destructor);#endifif(!tmp){/* Channel structure allocation failure. */returnNULL;}if(!(tmp->nativeformats=ast_format_cap_alloc())){ao2_ref(tmp,-1);/* format capabilities structure allocation failure */returnNULL;}/* * Init file descriptors to unopened state so * the destructor can know not to close them. */tmp->timingfd=-1;for(x=0;x<ARRAY_LEN(tmp->alertpipe);++x){tmp->alertpipe[x]=-1;}for(x=0;x<ARRAY_LEN(tmp->fds);++x){tmp->fds[x]=-1;}#ifdef HAVE_EPOLLtmp->epfd=epoll_create(25);#endifif(!(tmp->sched=ast_sched_context_create())){ast_log(LOG_WARNING,"Channel allocation failed: Unable to create schedule context\n");returnast_channel_unref(tmp);}ast_party_dialed_init(&tmp->dialed);ast_party_caller_init(&tmp->caller);ast_party_connected_line_init(&tmp->connected);ast_party_redirecting_init(&tmp->redirecting);if(cid_name){tmp->caller.id.name.valid=1;tmp->caller.id.name.str=ast_strdup(cid_name);if(!tmp->caller.id.name.str){returnast_channel_unref(tmp);}}if(cid_num){tmp->caller.id.number.valid=1;tmp->caller.id.number.str=ast_strdup(cid_num);if(!tmp->caller.id.number.str){returnast_channel_unref(tmp);}}if((tmp->timer=ast_timer_open())){if(strcmp(ast_timer_get_name(tmp->timer),"timerfd")){needqueue=0;}tmp->timingfd=ast_timer_fd(tmp->timer);}if(needqueue){if(pipe(tmp->alertpipe)){ast_log(LOG_WARNING,"Channel allocation failed: Can't create alert pipe! Try increasing max file descriptors with ulimit -n\n");returnast_channel_unref(tmp);}else{flags=fcntl(tmp->alertpipe[0],F_GETFL);if(fcntl(tmp->alertpipe[0],F_SETFL,flags|O_NONBLOCK)<0){ast_log(LOG_WARNING,"Channel allocation failed: Unable to set alertpipe nonblocking! (%d: %s)\n",errno,strerror(errno));returnast_channel_unref(tmp);}flags=fcntl(tmp->alertpipe[1],F_GETFL);if(fcntl(tmp->alertpipe[1],F_SETFL,flags|O_NONBLOCK)<0){ast_log(LOG_WARNING,"Channel allocation failed: Unable to set alertpipe nonblocking! (%d: %s)\n",errno,strerror(errno));returnast_channel_unref(tmp);}}}/* * This is the last place the channel constructor can fail. * * The destructor takes advantage of this fact to ensure that the * AST_CEL_CHANNEL_END is not posted if we have not posted the * AST_CEL_CHANNEL_START yet. */if((ast_string_field_init(tmp,128))){returnast_channel_unref(tmp);}/* Always watch the alertpipe */ast_channel_set_fd(tmp,AST_ALERT_FD,tmp->alertpipe[0]);/* And timing pipe */ast_channel_set_fd(tmp,AST_TIMING_FD,tmp->timingfd);/* Initial state */tmp->_state=state;tmp->streamid=-1;tmp->fin=global_fin;tmp->fout=global_fout;if(ast_strlen_zero(ast_config_AST_SYSTEM_NAME)){ast_string_field_build(tmp,uniqueid,"%li.%d",(long)time(NULL),ast_atomic_fetchadd_int(&uniqueint,1));}else{ast_string_field_build(tmp,uniqueid,"%s-%li.%d",ast_config_AST_SYSTEM_NAME,(long)time(NULL),ast_atomic_fetchadd_int(&uniqueint,1));}if(!ast_strlen_zero(linkedid)){ast_string_field_set(tmp,linkedid,linkedid);}else{ast_string_field_set(tmp,linkedid,tmp->uniqueid);}if(!ast_strlen_zero(name_fmt)){char*slash,*slash2;/* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call. * And they all use slightly different formats for their name string. * This means, to set the name here, we have to accept variable args, and call the string_field_build from here. * This means, that the stringfields must have a routine that takes the va_lists directly, and * uses them to build the string, instead of forming the va_lists internally from the vararg ... list. * This new function was written so this can be accomplished. */ast_string_field_build_va(tmp,name,name_fmt,ap);tech=ast_strdupa(tmp->name);if((slash=strchr(tech,'/'))){if((slash2=strchr(slash+1,'/'))){tech2=slash+1;*slash2='\0';}*slash='\0';}}else{/* * Start the string with '-' so it becomes an empty string * in the destructor. */ast_string_field_set(tmp,name,"-**Unknown**");}/* Reminder for the future: under what conditions do we NOT want to track cdrs on channels? *//* These 4 variables need to be set up for the cdr_init() to work right */if(amaflag)tmp->amaflags=amaflag;elsetmp->amaflags=ast_default_amaflags;if(!ast_strlen_zero(acctcode))ast_string_field_set(tmp,accountcode,acctcode);elseast_string_field_set(tmp,accountcode,ast_default_accountcode);if(!ast_strlen_zero(context))ast_copy_string(tmp->context,context,sizeof(tmp->context));elsestrcpy(tmp->context,"default");if(!ast_strlen_zero(exten))ast_copy_string(tmp->exten,exten,sizeof(tmp->exten));elsestrcpy(tmp->exten,"s");tmp->priority=1;tmp->cdr=ast_cdr_alloc();ast_cdr_init(tmp->cdr,tmp);ast_cdr_start(tmp->cdr);ast_cel_report_event(tmp,AST_CEL_CHANNEL_START,NULL,NULL,NULL);headp=&tmp->varshead;AST_LIST_HEAD_INIT_NOLOCK(headp);AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);AST_LIST_HEAD_INIT_NOLOCK(&tmp->autochans);ast_string_field_set(tmp,language,defaultlanguage);tmp->tech=&null_tech;ao2_link(channels,tmp);/* * And now, since the channel structure is built, and has its name, let's * call the manager event generator with this Newchannel event. This is the * proper and correct place to make this call, but you sure do have to pass * a lot of data into this func to do it here! */if(ast_get_channel_tech(tech)||(tech2&&ast_get_channel_tech(tech2))){ast_manager_event(tmp,EVENT_FLAG_CALL,"Newchannel","Channel: %s\r\n""ChannelState: %d\r\n""ChannelStateDesc: %s\r\n""CallerIDNum: %s\r\n""CallerIDName: %s\r\n""AccountCode: %s\r\n""Exten: %s\r\n""Context: %s\r\n""Uniqueid: %s\r\n",tmp->name,state,ast_state2str(state),S_OR(cid_num,""),S_OR(cid_name,""),tmp->accountcode,S_OR(exten,""),S_OR(context,""),tmp->uniqueid);}returntmp;}structast_channel*__ast_channel_alloc(intneedqueue,intstate,constchar*cid_num,constchar*cid_name,constchar*acctcode,constchar*exten,constchar*context,constchar*linkedid,constintamaflag,constchar*file,intline,constchar*function,constchar*name_fmt,...){va_listap;structast_channel*result;va_start(ap,name_fmt);result=__ast_channel_alloc_ap(needqueue,state,cid_num,cid_name,acctcode,exten,context,linkedid,amaflag,file,line,function,name_fmt,ap);va_end(ap);returnresult;}/* only do the minimum amount of work needed here to make a channel * structure that can be used to expand channel vars */#if defined(REF_DEBUG) || defined(__AST_DEBUG_MALLOC)structast_channel*__ast_dummy_channel_alloc(constchar*file,intline,constchar*function)#elsestructast_channel*ast_dummy_channel_alloc(void)#endif{structast_channel*tmp;structvarshead*headp;#if defined(REF_DEBUG)tmp=__ao2_alloc_debug(sizeof(*tmp),ast_dummy_channel_destructor,"dummy channel",file,line,function,1);#elif defined(__AST_DEBUG_MALLOC)tmp=__ao2_alloc_debug(sizeof(*tmp),ast_dummy_channel_destructor,"dummy channel",file,line,function,0);#elsetmp=ao2_alloc(sizeof(*tmp),ast_dummy_channel_destructor);#endifif(!tmp){/* Dummy channel structure allocation failure. */returnNULL;}if((ast_string_field_init(tmp,128))){returnast_channel_unref(tmp);}headp=&tmp->varshead;AST_LIST_HEAD_INIT_NOLOCK(headp);returntmp;}staticint__ast_queue_frame(structast_channel*chan,structast_frame*fin,inthead,structast_frame*after){structast_frame*f;structast_frame*cur;intblah=1;unsignedintnew_frames=0;unsignedintnew_voice_frames=0;unsignedintqueued_frames=0;unsignedintqueued_voice_frames=0;AST_LIST_HEAD_NOLOCK(,ast_frame)frames;ast_channel_lock(chan);/* * Check the last frame on the queue if we are queuing the new * frames after it. */cur=AST_LIST_LAST(&chan->readq);if(cur&&cur->frametype==AST_FRAME_CONTROL&&!head&&(!after||after==cur)){switch(cur->subclass.integer){caseAST_CONTROL_END_OF_Q:if(fin->frametype==AST_FRAME_CONTROL&&fin->subclass.integer==AST_CONTROL_HANGUP){/* * Destroy the end-of-Q marker frame so we can queue the hangup * frame in its place. */AST_LIST_REMOVE(&chan->readq,cur,frame_list);ast_frfree(cur);/* * This has degenerated to a normal queue append anyway. Since * we just destroyed the last frame in the queue we must make * sure that "after" is NULL or bad things will happen. */after=NULL;break;}/* Fall through */caseAST_CONTROL_HANGUP:/* Don't queue anything. */ast_channel_unlock(chan);return0;default:break;}}/* Build copies of all the new frames and count them */AST_LIST_HEAD_INIT_NOLOCK(&frames);for(cur=fin;cur;cur=AST_LIST_NEXT(cur,frame_list)){if(!(f=ast_frdup(cur))){if(AST_LIST_FIRST(&frames)){ast_frfree(AST_LIST_FIRST(&frames));}ast_channel_unlock(chan);return-1;}AST_LIST_INSERT_TAIL(&frames,f,frame_list);new_frames++;if(f->frametype==AST_FRAME_VOICE){new_voice_frames++;}}/* Count how many frames exist on the queue */AST_LIST_TRAVERSE(&chan->readq,cur,frame_list){queued_frames++;if(cur->frametype==AST_FRAME_VOICE){queued_voice_frames++;}}if((queued_frames+new_frames>128||queued_voice_frames+new_voice_frames>96)){intcount=0;ast_log(LOG_WARNING,"Exceptionally long %squeue length queuing to %s\n",queued_frames+new_frames>128?"":"voice ",chan->name);AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->readq,cur,frame_list){/* Save the most recent frame */if(!AST_LIST_NEXT(cur,frame_list)){break;}elseif(cur->frametype==AST_FRAME_VOICE||cur->frametype==AST_FRAME_VIDEO||cur->frametype==AST_FRAME_NULL){if(++count>64){break;}AST_LIST_REMOVE_CURRENT(frame_list);ast_frfree(cur);}}AST_LIST_TRAVERSE_SAFE_END;}if(after){AST_LIST_INSERT_LIST_AFTER(&chan->readq,&frames,after,frame_list);}else{if(head){AST_LIST_APPEND_LIST(&frames,&chan->readq,frame_list);AST_LIST_HEAD_INIT_NOLOCK(&chan->readq);}AST_LIST_APPEND_LIST(&chan->readq,&frames,frame_list);}if(chan->alertpipe[1]>-1){if(write(chan->alertpipe[1],&blah,new_frames*sizeof(blah))!=(new_frames*sizeof(blah))){ast_log(LOG_WARNING,"Unable to write to alert pipe on %s (qlen = %d): %s!\n",chan->name,queued_frames,strerror(errno));}}elseif(chan->timingfd>-1){ast_timer_enable_continuous(chan->timer);}elseif(ast_test_flag(chan,AST_FLAG_BLOCKING)){pthread_kill(chan->blocker,SIGURG);}ast_channel_unlock(chan);return0;}intast_queue_frame(structast_channel*chan,structast_frame*fin){return__ast_queue_frame(chan,fin,0,NULL);}intast_queue_frame_head(structast_channel*chan,structast_frame*fin){return__ast_queue_frame(chan,fin,1,NULL);}/*! \brief Queue a hangup frame for channel */intast_queue_hangup(structast_channel*chan){structast_framef={AST_FRAME_CONTROL,.subclass.integer=AST_CONTROL_HANGUP};/* Yeah, let's not change a lock-critical value without locking */if(!ast_channel_trylock(chan)){chan->_softhangup|=AST_SOFTHANGUP_DEV;manager_event(EVENT_FLAG_CALL,"HangupRequest","Channel: %s\r\n""Uniqueid: %s\r\n",chan->name,chan->uniqueid);ast_channel_unlock(chan);}returnast_queue_frame(chan,&f);}/*! \brief Queue a hangup frame for channel */intast_queue_hangup_with_cause(structast_channel*chan,intcause){structast_framef={AST_FRAME_CONTROL,.subclass.integer=AST_CONTROL_HANGUP};if(cause>=0)f.data.uint32=cause;/* Yeah, let's not change a lock-critical value without locking */if(!ast_channel_trylock(chan)){chan->_softhangup|=AST_SOFTHANGUP_DEV;if(cause<0)f.data.uint32=chan->hangupcause;manager_event(EVENT_FLAG_CALL,"HangupRequest","Channel: %s\r\n""Uniqueid: %s\r\n""Cause: %d\r\n",chan->name,chan->uniqueid,cause);ast_channel_unlock(chan);}returnast_queue_frame(chan,&f);}/*! \brief Queue a control frame */intast_queue_control(structast_channel*chan,enumast_control_frame_typecontrol){structast_framef={AST_FRAME_CONTROL,.subclass.integer=control};returnast_queue_frame(chan,&f);}/*! \brief Queue a control frame with payload */intast_queue_control_data(structast_channel*chan,enumast_control_frame_typecontrol,constvoid*data,size_tdatalen){structast_framef={AST_FRAME_CONTROL,.subclass.integer=control,.data.ptr=(void*)data,.datalen=datalen};returnast_queue_frame(chan,&f);}/*! \brief Set defer DTMF flag on channel */intast_channel_defer_dtmf(structast_channel*chan){intpre=0;if(chan){pre=ast_test_flag(chan,AST_FLAG_DEFER_DTMF);ast_set_flag(chan,AST_FLAG_DEFER_DTMF);}returnpre;}/*! \brief Unset defer DTMF flag on channel */voidast_channel_undefer_dtmf(structast_channel*chan){if(chan)ast_clear_flag(chan,AST_FLAG_DEFER_DTMF);}structast_channel*ast_channel_callback(ao2_callback_data_fn*cb_fn,void*arg,void*data,intao2_flags){returnao2_callback_data(channels,ao2_flags,cb_fn,arg,data);}structast_channel_iterator{/* storage for non-dynamically allocated iterator */structao2_iteratorsimple_iterator;/* pointer to the actual iterator (simple_iterator or a dynamically * allocated iterator) */structao2_iterator*active_iterator;};structast_channel_iterator*ast_channel_iterator_destroy(structast_channel_iterator*i){ao2_iterator_destroy(i->active_iterator);ast_free(i);returnNULL;}staticstructast_channel_iterator*channel_iterator_search(constchar*name,size_tname_len,constchar*exten,constchar*context){structast_channel_iterator*i;structast_channeltmp_chan={.name=name,/* This is sort of a hack. Basically, we're using an arbitrary field * in ast_channel to pass the name_len for a prefix match. If this * gets changed, then the compare callback must be changed, too. */.rings=name_len,};if(!(i=ast_calloc(1,sizeof(*i)))){returnNULL;}if(exten){ast_copy_string(tmp_chan.exten,exten,sizeof(tmp_chan.exten));}if(context){ast_copy_string(tmp_chan.context,context,sizeof(tmp_chan.context));}if(!(i->active_iterator=ao2_find(channels,&tmp_chan,OBJ_MULTIPLE|((!ast_strlen_zero(name)&&(name_len==0))?OBJ_POINTER:0)))){ast_free(i);returnNULL;}returni;}structast_channel_iterator*ast_channel_iterator_by_exten_new(constchar*exten,constchar*context){returnchannel_iterator_search(NULL,0,exten,context);}structast_channel_iterator*ast_channel_iterator_by_name_new(constchar*name,size_tname_len){returnchannel_iterator_search(name,name_len,NULL,NULL);}structast_channel_iterator*ast_channel_iterator_all_new(void){structast_channel_iterator*i;if(!(i=ast_calloc(1,sizeof(*i)))){returnNULL;}i->simple_iterator=ao2_iterator_init(channels,0);i->active_iterator=&i->simple_iterator;returni;}structast_channel*ast_channel_iterator_next(structast_channel_iterator*i){returnao2_iterator_next(i->active_iterator);}staticintast_channel_cmp_cb(void*obj,void*arg,intflags){structast_channel*chan=obj,*cmp_args=arg;size_tname_len;intret=CMP_MATCH;/* This is sort of a hack. Basically, we're using an arbitrary field * in ast_channel to pass the name_len for a prefix match. If this * gets changed, then the uses of ao2_find() must be changed, too. */name_len=cmp_args->rings;ast_channel_lock(chan);if(!ast_strlen_zero(cmp_args->name)){/* match by name */if((!name_len&&strcasecmp(chan->name,cmp_args->name))||(name_len&&strncasecmp(chan->name,cmp_args->name,name_len))){ret=0;/* name match failed */}}elseif(!ast_strlen_zero(cmp_args->exten)){if(cmp_args->context&&strcasecmp(chan->context,cmp_args->context)&&strcasecmp(chan->macrocontext,cmp_args->context)){ret=0;/* context match failed */}if(ret&&strcasecmp(chan->exten,cmp_args->exten)&&strcasecmp(chan->macroexten,cmp_args->exten)){ret=0;/* exten match failed */}}elseif(!ast_strlen_zero(cmp_args->uniqueid)){if((!name_len&&strcasecmp(chan->uniqueid,cmp_args->uniqueid))||(name_len&&strncasecmp(chan->uniqueid,cmp_args->uniqueid,name_len))){ret=0;/* uniqueid match failed */}}else{ret=0;}ast_channel_unlock(chan);returnret;}staticstructast_channel*ast_channel_get_full(constchar*name,size_tname_len,constchar*exten,constchar*context){structast_channeltmp_chan={.name=name,/* This is sort of a hack. Basically, we're using an arbitrary field * in ast_channel to pass the name_len for a prefix match. If this * gets changed, then the compare callback must be changed, too. */.rings=name_len,};structast_channel*chan;if(exten){ast_copy_string(tmp_chan.exten,exten,sizeof(tmp_chan.exten));}if(context){ast_copy_string(tmp_chan.context,context,sizeof(tmp_chan.context));}if((chan=ao2_find(channels,&tmp_chan,(!ast_strlen_zero(name)&&(name_len==0))?OBJ_POINTER:0))){returnchan;}if(!name){returnNULL;}/* If name was specified, but the result was NULL, * try a search on uniqueid, instead. */{structast_channeltmp_chan2={.uniqueid=name,.rings=name_len,};returnao2_find(channels,&tmp_chan2,0);}}structast_channel*ast_channel_get_by_name(constchar*name){returnast_channel_get_full(name,0,NULL,NULL);}structast_channel*ast_channel_get_by_name_prefix(constchar*name,size_tname_len){returnast_channel_get_full(name,name_len,NULL,NULL);}structast_channel*ast_channel_get_by_exten(constchar*exten,constchar*context){returnast_channel_get_full(NULL,0,exten,context);}intast_is_deferrable_frame(conststructast_frame*frame){/* Do not add a default entry in this switch statement. Each new * frame type should be addressed directly as to whether it should * be queued up or not. */switch(frame->frametype){caseAST_FRAME_CONTROL:caseAST_FRAME_TEXT:caseAST_FRAME_IMAGE:caseAST_FRAME_HTML:return1;caseAST_FRAME_DTMF_END:caseAST_FRAME_DTMF_BEGIN:caseAST_FRAME_VOICE:caseAST_FRAME_VIDEO:caseAST_FRAME_NULL:caseAST_FRAME_IAX:caseAST_FRAME_CNG:caseAST_FRAME_MODEM:return0;}return0;}/*! \brief Wait, look for hangups and condition arg */intast_safe_sleep_conditional(structast_channel*chan,intms,int(*cond)(void*),void*data){structast_frame*f;structast_silence_generator*silgen=NULL;intres=0;AST_LIST_HEAD_NOLOCK(,ast_frame)deferred_frames;AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);/* If no other generator is present, start silencegen while waiting */if(ast_opt_transmit_silence&&!chan->generatordata){silgen=ast_channel_start_silence_generator(chan);}while(ms>0){structast_frame*dup_f=NULL;if(cond&&((*cond)(data)==0)){break;}ms=ast_waitfor(chan,ms);if(ms<0){res=-1;break;}if(ms>0){f=ast_read(chan);if(!f){res=-1;break;}if(!ast_is_deferrable_frame(f)){ast_frfree(f);continue;}if((dup_f=ast_frisolate(f))){if(dup_f!=f){ast_frfree(f);}AST_LIST_INSERT_HEAD(&deferred_frames,dup_f,frame_list);}}}/* stop silgen if present */if(silgen){ast_channel_stop_silence_generator(chan,silgen);}/* We need to free all the deferred frames, but we only need to * queue the deferred frames if there was no error and no * hangup was received */ast_channel_lock(chan);while((f=AST_LIST_REMOVE_HEAD(&deferred_frames,frame_list))){if(!res){ast_queue_frame_head(chan,f);}ast_frfree(f);}ast_channel_unlock(chan);returnres;}/*! \brief Wait, look for hangups */intast_safe_sleep(structast_channel*chan,intms){returnast_safe_sleep_conditional(chan,ms,NULL,NULL);}structast_channel*ast_channel_release(structast_channel*chan){/* Safe, even if already unlinked. */ao2_unlink(channels,chan);returnast_channel_unref(chan);}voidast_party_name_init(structast_party_name*init){init->str=NULL;init->char_set=AST_PARTY_CHAR_SET_ISO8859_1;init->presentation=AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;init->valid=0;}voidast_party_name_copy(structast_party_name*dest,conststructast_party_name*src){if(dest==src){/* Don't copy to self */return;}ast_free(dest->str);dest->str=ast_strdup(src->str);dest->char_set=src->char_set;dest->presentation=src->presentation;dest->valid=src->valid;}voidast_party_name_set_init(structast_party_name*init,conststructast_party_name*guide){init->str=NULL;init->char_set=guide->char_set;init->presentation=guide->presentation;init->valid=guide->valid;}voidast_party_name_set(structast_party_name*dest,conststructast_party_name*src){if(dest==src){/* Don't set to self */return;}if(src->str&&src->str!=dest->str){ast_free(dest->str);dest->str=ast_strdup(src->str);}dest->char_set=src->char_set;dest->presentation=src->presentation;dest->valid=src->valid;}voidast_party_name_free(structast_party_name*doomed){ast_free(doomed->str);doomed->str=NULL;}voidast_party_number_init(structast_party_number*init){init->str=NULL;init->plan=0;/* Unknown */init->presentation=AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;init->valid=0;}voidast_party_number_copy(structast_party_number*dest,conststructast_party_number*src){if(dest==src){/* Don't copy to self */return;}ast_free(dest->str);dest->str=ast_strdup(src->str);dest->plan=src->plan;dest->presentation=src->presentation;dest->valid=src->valid;}voidast_party_number_set_init(structast_party_number*init,conststructast_party_number*guide){init->str=NULL;init->plan=guide->plan;init->presentation=guide->presentation;init->valid=guide->valid;}voidast_party_number_set(structast_party_number*dest,conststructast_party_number*src){if(dest==src){/* Don't set to self */return;}if(src->str&&src->str!=dest->str){ast_free(dest->str);dest->str=ast_strdup(src->str);}dest->plan=src->plan;dest->presentation=src->presentation;dest->valid=src->valid;}voidast_party_number_free(structast_party_number*doomed){ast_free(doomed->str);doomed->str=NULL;}voidast_party_subaddress_init(structast_party_subaddress*init){init->str=NULL;init->type=0;init->odd_even_indicator=0;init->valid=0;}voidast_party_subaddress_copy(structast_party_subaddress*dest,conststructast_party_subaddress*src){if(dest==src){/* Don't copy to self */return;}ast_free(dest->str);dest->str=ast_strdup(src->str);dest->type=src->type;dest->odd_even_indicator=src->odd_even_indicator;dest->valid=src->valid;}voidast_party_subaddress_set_init(structast_party_subaddress*init,conststructast_party_subaddress*guide){init->str=NULL;init->type=guide->type;init->odd_even_indicator=guide->odd_even_indicator;init->valid=guide->valid;}voidast_party_subaddress_set(structast_party_subaddress*dest,conststructast_party_subaddress*src){if(dest==src){/* Don't set to self */return;}if(src->str&&src->str!=dest->str){ast_free(dest->str);dest->str=ast_strdup(src->str);}dest->type=src->type;dest->odd_even_indicator=src->odd_even_indicator;dest->valid=src->valid;}voidast_party_subaddress_free(structast_party_subaddress*doomed){ast_free(doomed->str);doomed->str=NULL;}voidast_party_id_init(structast_party_id*init){ast_party_name_init(&init->name);ast_party_number_init(&init->number);ast_party_subaddress_init(&init->subaddress);init->tag=NULL;}voidast_party_id_copy(structast_party_id*dest,conststructast_party_id*src){if(dest==src){/* Don't copy to self */return;}ast_party_name_copy(&dest->name,&src->name);ast_party_number_copy(&dest->number,&src->number);ast_party_subaddress_copy(&dest->subaddress,&src->subaddress);ast_free(dest->tag);dest->tag=ast_strdup(src->tag);}voidast_party_id_set_init(structast_party_id*init,conststructast_party_id*guide){ast_party_name_set_init(&init->name,&guide->name);ast_party_number_set_init(&init->number,&guide->number);ast_party_subaddress_set_init(&init->subaddress,&guide->subaddress);init->tag=NULL;}voidast_party_id_set(structast_party_id*dest,conststructast_party_id*src,conststructast_set_party_id*update){if(dest==src){/* Don't set to self */return;}if(!update||update->name){ast_party_name_set(&dest->name,&src->name);}if(!update||update->number){ast_party_number_set(&dest->number,&src->number);}if(!update||update->subaddress){ast_party_subaddress_set(&dest->subaddress,&src->subaddress);}if(src->tag&&src->tag!=dest->tag){ast_free(dest->tag);dest->tag=ast_strdup(src->tag);}}voidast_party_id_free(structast_party_id*doomed){ast_party_name_free(&doomed->name);ast_party_number_free(&doomed->number);ast_party_subaddress_free(&doomed->subaddress);ast_free(doomed->tag);doomed->tag=NULL;}intast_party_id_presentation(conststructast_party_id*id){intnumber_priority;intnumber_value;intnumber_screening;intname_priority;intname_value;/* Determine name presentation priority. */if(!id->name.valid){name_value=AST_PRES_UNAVAILABLE;name_priority=3;}else{name_value=id->name.presentation&AST_PRES_RESTRICTION;switch(name_value){caseAST_PRES_RESTRICTED:name_priority=0;break;caseAST_PRES_ALLOWED:name_priority=1;break;caseAST_PRES_UNAVAILABLE:name_priority=2;break;default:name_value=AST_PRES_UNAVAILABLE;name_priority=3;break;}}/* Determine number presentation priority. */if(!id->number.valid){number_screening=AST_PRES_USER_NUMBER_UNSCREENED;number_value=AST_PRES_UNAVAILABLE;number_priority=3;}else{number_screening=id->number.presentation&AST_PRES_NUMBER_TYPE;number_value=id->number.presentation&AST_PRES_RESTRICTION;switch(number_value){caseAST_PRES_RESTRICTED:number_priority=0;break;caseAST_PRES_ALLOWED:number_priority=1;break;caseAST_PRES_UNAVAILABLE:number_priority=2;break;default:number_screening=AST_PRES_USER_NUMBER_UNSCREENED;number_value=AST_PRES_UNAVAILABLE;number_priority=3;break;}}/* Select the wining presentation value. */if(name_priority<number_priority){number_value=name_value;}returnnumber_value|number_screening;}voidast_party_dialed_init(structast_party_dialed*init){init->number.str=NULL;init->number.plan=0;/* Unknown */ast_party_subaddress_init(&init->subaddress);init->transit_network_select=0;}voidast_party_dialed_copy(structast_party_dialed*dest,conststructast_party_dialed*src){if(dest==src){/* Don't copy to self */return;}ast_free(dest->number.str);dest->number.str=ast_strdup(src->number.str);dest->number.plan=src->number.plan;ast_party_subaddress_copy(&dest->subaddress,&src->subaddress);dest->transit_network_select=src->transit_network_select;}voidast_party_dialed_set_init(structast_party_dialed*init,conststructast_party_dialed*guide){init->number.str=NULL;init->number.plan=guide->number.plan;ast_party_subaddress_set_init(&init->subaddress,&guide->subaddress);init->transit_network_select=guide->transit_network_select;}voidast_party_dialed_set(structast_party_dialed*dest,conststructast_party_dialed*src){if(src->number.str&&src->number.str!=dest->number.str){ast_free(dest->number.str);dest->number.str=ast_strdup(src->number.str);}dest->number.plan=src->number.plan;ast_party_subaddress_set(&dest->subaddress,&src->subaddress);dest->transit_network_select=src->transit_network_select;}voidast_party_dialed_free(structast_party_dialed*doomed){ast_free(doomed->number.str);doomed->number.str=NULL;ast_party_subaddress_free(&doomed->subaddress);}voidast_party_caller_init(structast_party_caller*init){ast_party_id_init(&init->id);ast_party_id_init(&init->ani);init->ani2=0;}voidast_party_caller_copy(structast_party_caller*dest,conststructast_party_caller*src){if(dest==src){/* Don't copy to self */return;}ast_party_id_copy(&dest->id,&src->id);ast_party_id_copy(&dest->ani,&src->ani);dest->ani2=src->ani2;}voidast_party_caller_set_init(structast_party_caller*init,conststructast_party_caller*guide){ast_party_id_set_init(&init->id,&guide->id);ast_party_id_set_init(&init->ani,&guide->ani);init->ani2=guide->ani2;}voidast_party_caller_set(structast_party_caller*dest,conststructast_party_caller*src,conststructast_set_party_caller*update){ast_party_id_set(&dest->id,&src->id,update?&update->id:NULL);ast_party_id_set(&dest->ani,&src->ani,update?&update->ani:NULL);dest->ani2=src->ani2;}voidast_party_caller_free(structast_party_caller*doomed){ast_party_id_free(&doomed->id);ast_party_id_free(&doomed->ani);}voidast_party_connected_line_init(structast_party_connected_line*init){ast_party_id_init(&init->id);ast_party_id_init(&init->ani);init->ani2=0;init->source=AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN;}voidast_party_connected_line_copy(structast_party_connected_line*dest,conststructast_party_connected_line*src){if(dest==src){/* Don't copy to self */return;}ast_party_id_copy(&dest->id,&src->id);ast_party_id_copy(&dest->ani,&src->ani);dest->ani2=src->ani2;dest->source=src->source;}voidast_party_connected_line_set_init(structast_party_connected_line*init,conststructast_party_connected_line*guide){ast_party_id_set_init(&init->id,&guide->id);ast_party_id_set_init(&init->ani,&guide->ani);init->ani2=guide->ani2;init->source=guide->source;}voidast_party_connected_line_set(structast_party_connected_line*dest,conststructast_party_connected_line*src,conststructast_set_party_connected_line*update){ast_party_id_set(&dest->id,&src->id,update?&update->id:NULL);ast_party_id_set(&dest->ani,&src->ani,update?&update->ani:NULL);dest->ani2=src->ani2;dest->source=src->source;}voidast_party_connected_line_collect_caller(structast_party_connected_line*connected,structast_party_caller*caller){connected->id=caller->id;connected->ani=caller->ani;connected->ani2=caller->ani2;connected->source=AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN;}voidast_party_connected_line_free(structast_party_connected_line*doomed){ast_party_id_free(&doomed->id);ast_party_id_free(&doomed->ani);}voidast_party_redirecting_init(structast_party_redirecting*init){ast_party_id_init(&init->from);ast_party_id_init(&init->to);init->count=0;init->reason=AST_REDIRECTING_REASON_UNKNOWN;}voidast_party_redirecting_copy(structast_party_redirecting*dest,conststructast_party_redirecting*src){if(dest==src){/* Don't copy to self */return;}ast_party_id_copy(&dest->from,&src->from);ast_party_id_copy(&dest->to,&src->to);dest->count=src->count;dest->reason=src->reason;}voidast_party_redirecting_set_init(structast_party_redirecting*init,conststructast_party_redirecting*guide){ast_party_id_set_init(&init->from,&guide->from);ast_party_id_set_init(&init->to,&guide->to);init->count=guide->count;init->reason=guide->reason;}voidast_party_redirecting_set(structast_party_redirecting*dest,conststructast_party_redirecting*src,conststructast_set_party_redirecting*update){ast_party_id_set(&dest->from,&src->from,update?&update->from:NULL);ast_party_id_set(&dest->to,&src->to,update?&update->to:NULL);dest->reason=src->reason;dest->count=src->count;}voidast_party_redirecting_free(structast_party_redirecting*doomed){ast_party_id_free(&doomed->from);ast_party_id_free(&doomed->to);}/*! \brief Free a channel structure */staticvoidast_channel_destructor(void*obj){structast_channel*chan=obj;intfd;#ifdef HAVE_EPOLLinti;#endifstructast_var_t*vardata;structast_frame*f;structvarshead*headp;structast_datastore*datastore;chardevice_name[AST_CHANNEL_NAME];if(chan->name){/* The string fields were initialized. */ast_cel_report_event(chan,AST_CEL_CHANNEL_END,NULL,NULL,NULL);ast_cel_check_retire_linkedid(chan);}/* Get rid of each of the data stores on the channel */ast_channel_lock(chan);while((datastore=AST_LIST_REMOVE_HEAD(&chan->datastores,entry)))/* Free the data store */ast_datastore_free(datastore);ast_channel_unlock(chan);/* Lock and unlock the channel just to be sure nobody has it locked still due to a reference that was stored in a datastore. (i.e. app_chanspy) */ast_channel_lock(chan);ast_channel_unlock(chan);if(chan->tech_pvt){ast_log(LOG_WARNING,"Channel '%s' may not have been hung up properly\n",chan->name);ast_free(chan->tech_pvt);}if(chan->sched){ast_sched_context_destroy(chan->sched);}if(chan->name){char*dashptr;/* The string fields were initialized. */ast_copy_string(device_name,chan->name,sizeof(device_name));if((dashptr=strrchr(device_name,'-'))){*dashptr='\0';}}else{device_name[0]='\0';}/* Stop monitoring */if(chan->monitor)chan->monitor->stop(chan,0);/* If there is native format music-on-hold state, free it */if(chan->music_state)ast_moh_cleanup(chan);/* Free translators */if(chan->readtrans)ast_translator_free_path(chan->readtrans);if(chan->writetrans)ast_translator_free_path(chan->writetrans);if(chan->pbx)ast_log(LOG_WARNING,"PBX may not have been terminated properly on '%s'\n",chan->name);ast_party_dialed_free(&chan->dialed);ast_party_caller_free(&chan->caller);ast_party_connected_line_free(&chan->connected);ast_party_redirecting_free(&chan->redirecting);/* Close pipes if appropriate */if((fd=chan->alertpipe[0])>-1)close(fd);if((fd=chan->alertpipe[1])>-1)close(fd);if(chan->timer){ast_timer_close(chan->timer);}#ifdef HAVE_EPOLLfor(i=0;i<AST_MAX_FDS;i++){if(chan->epfd_data[i])free(chan->epfd_data[i]);}close(chan->epfd);#endifwhile((f=AST_LIST_REMOVE_HEAD(&chan->readq,frame_list)))ast_frfree(f);/* loop over the variables list, freeing all data and deleting list items *//* no need to lock the list, as the channel is already locked */headp=&chan->varshead;while((vardata=AST_LIST_REMOVE_HEAD(headp,entries)))ast_var_delete(vardata);ast_app_group_discard(chan);/* Destroy the jitterbuffer */ast_jb_destroy(chan);if(chan->cdr){ast_cdr_discard(chan->cdr);chan->cdr=NULL;}if(chan->zone){chan->zone=ast_tone_zone_unref(chan->zone);}ast_string_field_free_memory(chan);if(device_name[0]){/* * We have a device name to notify of a new state. * * Queue an unknown state, because, while we know that this particular * instance is dead, we don't know the state of all other possible * instances. */ast_devstate_changed_literal(AST_DEVICE_UNKNOWN,device_name);}chan->nativeformats=ast_format_cap_destroy(chan->nativeformats);}/*! \brief Free a dummy channel structure */staticvoidast_dummy_channel_destructor(void*obj){structast_channel*chan=obj;structast_var_t*vardata;structvarshead*headp;headp=&chan->varshead;ast_party_dialed_free(&chan->dialed);ast_party_caller_free(&chan->caller);ast_party_connected_line_free(&chan->connected);ast_party_redirecting_free(&chan->redirecting);/* loop over the variables list, freeing all data and deleting list items *//* no need to lock the list, as the channel is already locked */while((vardata=AST_LIST_REMOVE_HEAD(headp,entries)))ast_var_delete(vardata);if(chan->cdr){ast_cdr_discard(chan->cdr);chan->cdr=NULL;}ast_string_field_free_memory(chan);}structast_datastore*ast_channel_datastore_alloc(conststructast_datastore_info*info,constchar*uid){returnast_datastore_alloc(info,uid);}intast_channel_datastore_free(structast_datastore*datastore){returnast_datastore_free(datastore);}intast_channel_datastore_inherit(structast_channel*from,structast_channel*to){structast_datastore*datastore=NULL,*datastore2;AST_LIST_TRAVERSE(&from->datastores,datastore,entry){if(datastore->inheritance>0){datastore2=ast_datastore_alloc(datastore->info,datastore->uid);if(datastore2){datastore2->data=datastore->info->duplicate?datastore->info->duplicate(datastore->data):NULL;datastore2->inheritance=datastore->inheritance==DATASTORE_INHERIT_FOREVER?DATASTORE_INHERIT_FOREVER:datastore->inheritance-1;AST_LIST_INSERT_TAIL(&to->datastores,datastore2,entry);}}}return0;}intast_channel_datastore_add(structast_channel*chan,structast_datastore*datastore){intres=0;AST_LIST_INSERT_HEAD(&chan->datastores,datastore,entry);returnres;}intast_channel_datastore_remove(structast_channel*chan,structast_datastore*datastore){returnAST_LIST_REMOVE(&chan->datastores,datastore,entry)?0:-1;}structast_datastore*ast_channel_datastore_find(structast_channel*chan,conststructast_datastore_info*info,constchar*uid){structast_datastore*datastore=NULL;if(info==NULL)returnNULL;AST_LIST_TRAVERSE(&chan->datastores,datastore,entry){if(datastore->info!=info){continue;}if(uid==NULL){/* matched by type only */break;}if((datastore->uid!=NULL)&&!strcasecmp(uid,datastore->uid)){/* Matched by type AND uid */break;}}returndatastore;}/*! Set the file descriptor on the channel */voidast_channel_set_fd(structast_channel*chan,intwhich,intfd){#ifdef HAVE_EPOLLstructepoll_eventev;structast_epoll_data*aed=NULL;if(chan->fds[which]>-1){epoll_ctl(chan->epfd,EPOLL_CTL_DEL,chan->fds[which],&ev);aed=chan->epfd_data[which];}/* If this new fd is valid, add it to the epoll */if(fd>-1){if(!aed&&(!(aed=ast_calloc(1,sizeof(*aed)))))return;chan->epfd_data[which]=aed;aed->chan=chan;aed->which=which;ev.events=EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP;ev.data.ptr=aed;epoll_ctl(chan->epfd,EPOLL_CTL_ADD,fd,&ev);}elseif(aed){/* We don't have to keep around this epoll data structure now */free(aed);chan->epfd_data[which]=NULL;}#endifchan->fds[which]=fd;return;}/*! Add a channel to an optimized waitfor */voidast_poll_channel_add(structast_channel*chan0,structast_channel*chan1){#ifdef HAVE_EPOLLstructepoll_eventev;inti=0;if(chan0->epfd==-1)return;/* Iterate through the file descriptors on chan1, adding them to chan0 */for(i=0;i<AST_MAX_FDS;i++){if(chan1->fds[i]==-1)continue;ev.events=EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP;ev.data.ptr=chan1->epfd_data[i];epoll_ctl(chan0->epfd,EPOLL_CTL_ADD,chan1->fds[i],&ev);}#endifreturn;}/*! Delete a channel from an optimized waitfor */voidast_poll_channel_del(structast_channel*chan0,structast_channel*chan1){#ifdef HAVE_EPOLLstructepoll_eventev;inti=0;if(chan0->epfd==-1)return;for(i=0;i<AST_MAX_FDS;i++){if(chan1->fds[i]==-1)continue;epoll_ctl(chan0->epfd,EPOLL_CTL_DEL,chan1->fds[i],&ev);}#endifreturn;}voidast_channel_clear_softhangup(structast_channel*chan,intflag){ast_channel_lock(chan);chan->_softhangup&=~flag;if(!chan->_softhangup){structast_frame*fr;/* If we have completely cleared the softhangup flag, * then we need to fully abort the hangup process. This requires * pulling the END_OF_Q frame out of the channel frame queue if it * still happens to be there. */fr=AST_LIST_LAST(&chan->readq);if(fr&&fr->frametype==AST_FRAME_CONTROL&&fr->subclass.integer==AST_CONTROL_END_OF_Q){AST_LIST_REMOVE(&chan->readq,fr,frame_list);ast_frfree(fr);}}ast_channel_unlock(chan);}/*! \brief Softly hangup a channel, don't lock */intast_softhangup_nolock(structast_channel*chan,intcause){ast_debug(1,"Soft-Hanging up channel '%s'\n",chan->name);/* Inform channel driver that we need to be hung up, if it cares */chan->_softhangup|=cause;ast_queue_frame(chan,&ast_null_frame);/* Interrupt any poll call or such */if(ast_test_flag(chan,AST_FLAG_BLOCKING))pthread_kill(chan->blocker,SIGURG);return0;}/*! \brief Softly hangup a channel, lock */intast_softhangup(structast_channel*chan,intcause){intres;ast_channel_lock(chan);res=ast_softhangup_nolock(chan,cause);manager_event(EVENT_FLAG_CALL,"SoftHangupRequest","Channel: %s\r\n""Uniqueid: %s\r\n""Cause: %d\r\n",chan->name,chan->uniqueid,cause);ast_channel_unlock(chan);returnres;}staticvoidfree_translation(structast_channel*clonechan){if(clonechan->writetrans)ast_translator_free_path(clonechan->writetrans);if(clonechan->readtrans)ast_translator_free_path(clonechan->readtrans);clonechan->writetrans=NULL;clonechan->readtrans=NULL;if(ast_format_cap_is_empty(clonechan->nativeformats)){ast_format_clear(&clonechan->rawwriteformat);ast_format_clear(&clonechan->rawreadformat);}else{structast_formattmpfmt;ast_best_codec(clonechan->nativeformats,&tmpfmt);ast_format_copy(&clonechan->rawwriteformat,&tmpfmt);ast_format_copy(&clonechan->rawreadformat,&tmpfmt);}}voidast_set_hangupsource(structast_channel*chan,constchar*source,intforce){structast_channel*bridge;ast_channel_lock(chan);if(force||ast_strlen_zero(chan->hangupsource)){ast_string_field_set(chan,hangupsource,source);}bridge=ast_bridged_channel(chan);ast_channel_unlock(chan);if(bridge&&(force||ast_strlen_zero(bridge->hangupsource))){ast_channel_lock(bridge);ast_string_field_set(chan,hangupsource,source);ast_channel_unlock(bridge);}}/*! \brief Hangup a channel */intast_hangup(structast_channel*chan){intres=0;charextra_str[64];/* used for cel logging below *//* Don't actually hang up a channel that will masquerade as someone else, or if someone is going to masquerade as us */ast_channel_lock(chan);if(chan->audiohooks){ast_audiohook_detach_list(chan->audiohooks);chan->audiohooks=NULL;}ast_framehook_list_destroy(chan);ast_autoservice_stop(chan);if(chan->masq){ast_channel_unlock(chan);if(ast_do_masquerade(chan)){ast_log(LOG_WARNING,"Failed to perform masquerade\n");}ast_channel_lock(chan);}if(chan->masq){ast_log(LOG_WARNING,"%s getting hung up, but someone is trying to masq into us?!?\n",chan->name);ast_channel_unlock(chan);return0;}/* If this channel is one which will be masqueraded into something, mark it as a zombie already, so we know to free it later */if(chan->masqr){ast_set_flag(chan,AST_FLAG_ZOMBIE);ast_channel_unlock(chan);return0;}ast_channel_unlock(chan);ao2_unlink(channels,chan);ast_channel_lock(chan);free_translation(chan);/* Close audio stream */if(chan->stream){ast_closestream(chan->stream);chan->stream=NULL;}/* Close video stream */if(chan->vstream){ast_closestream(chan->vstream);chan->vstream=NULL;}if(chan->sched){ast_sched_context_destroy(chan->sched);chan->sched=NULL;}if(chan->generatordata)/* Clear any tone stuff remaining */if(chan->generator&&chan->generator->release)chan->generator->release(chan,chan->generatordata);chan->generatordata=NULL;chan->generator=NULL;snprintf(extra_str,sizeof(extra_str),"%d,%s,%s",chan->hangupcause,chan->hangupsource,S_OR(pbx_builtin_getvar_helper(chan,"DIALSTATUS"),""));ast_cel_report_event(chan,AST_CEL_HANGUP,NULL,extra_str,NULL);if(ast_test_flag(chan,AST_FLAG_BLOCKING)){ast_log(LOG_WARNING,"Hard hangup called by thread %ld on %s, while fd ""is blocked by thread %ld in procedure %s! Expect a failure\n",(long)pthread_self(),chan->name,(long)chan->blocker,chan->blockproc);ast_assert(ast_test_flag(chan,AST_FLAG_BLOCKING)==0);}if(!ast_test_flag(chan,AST_FLAG_ZOMBIE)){ast_debug(1,"Hanging up channel '%s'\n",chan->name);if(chan->tech->hangup)res=chan->tech->hangup(chan);}else{ast_debug(1,"Hanging up zombie '%s'\n",chan->name);}ast_channel_unlock(chan);ast_cc_offer(chan);ast_manager_event(chan,EVENT_FLAG_CALL,"Hangup","Channel: %s\r\n""Uniqueid: %s\r\n""CallerIDNum: %s\r\n""CallerIDName: %s\r\n""ConnectedLineNum: %s\r\n""ConnectedLineName: %s\r\n""Cause: %d\r\n""Cause-txt: %s\r\n",chan->name,chan->uniqueid,S_COR(chan->caller.id.number.valid,chan->caller.id.number.str,"<unknown>"),S_COR(chan->caller.id.name.valid,chan->caller.id.name.str,"<unknown>"),S_COR(chan->connected.id.number.valid,chan->connected.id.number.str,"<unknown>"),S_COR(chan->connected.id.name.valid,chan->connected.id.name.str,"<unknown>"),chan->hangupcause,ast_cause2str(chan->hangupcause));if(chan->cdr&&!ast_test_flag(chan->cdr,AST_CDR_FLAG_BRIDGED)&&!ast_test_flag(chan->cdr,AST_CDR_FLAG_POST_DISABLED)&&(chan->cdr->disposition!=AST_CDR_NULL||ast_test_flag(chan->cdr,AST_CDR_FLAG_DIALED))){ast_channel_lock(chan);ast_cdr_end(chan->cdr);ast_cdr_detach(chan->cdr);chan->cdr=NULL;ast_channel_unlock(chan);}chan=ast_channel_release(chan);returnres;}intast_raw_answer(structast_channel*chan,intcdr_answer){intres=0;ast_channel_lock(chan);/* You can't answer an outbound call */if(ast_test_flag(chan,AST_FLAG_OUTGOING)){ast_channel_unlock(chan);return0;}/* Stop if we're a zombie or need a soft hangup */if(ast_test_flag(chan,AST_FLAG_ZOMBIE)||ast_check_hangup(chan)){ast_channel_unlock(chan);return-1;}ast_channel_unlock(chan);switch(chan->_state){caseAST_STATE_RINGING:caseAST_STATE_RING:ast_channel_lock(chan);if(chan->tech->answer){res=chan->tech->answer(chan);}ast_setstate(chan,AST_STATE_UP);if(cdr_answer){ast_cdr_answer(chan->cdr);}ast_cel_report_event(chan,AST_CEL_ANSWER,NULL,NULL,NULL);ast_channel_unlock(chan);break;caseAST_STATE_UP:ast_cel_report_event(chan,AST_CEL_ANSWER,NULL,NULL,NULL);/* Calling ast_cdr_answer when it it has previously been called * is essentially a no-op, so it is safe. */if(cdr_answer){ast_cdr_answer(chan->cdr);}break;default:break;}ast_indicate(chan,-1);returnres;}int__ast_answer(structast_channel*chan,unsignedintdelay,intcdr_answer){intres=0;enumast_channel_stateold_state;old_state=chan->_state;if((res=ast_raw_answer(chan,cdr_answer))){returnres;}switch(old_state){caseAST_STATE_RINGING:caseAST_STATE_RING:/* wait for media to start flowing, but don't wait any longer * than 'delay' or 500 milliseconds, whichever is longer */do{AST_LIST_HEAD_NOLOCK(,ast_frame)frames;structast_frame*cur,*new;intms=MAX(delay,500);unsignedintdone=0;AST_LIST_HEAD_INIT_NOLOCK(&frames);for(;;){ms=ast_waitfor(chan,ms);if(ms<0){ast_log(LOG_WARNING,"Error condition occurred when polling channel %s for a voice frame: %s\n",chan->name,strerror(errno));res=-1;break;}if(ms==0){ast_debug(2,"Didn't receive a media frame from %s within %d ms of answering. Continuing anyway\n",chan->name,MAX(delay,500));break;}cur=ast_read(chan);if(!cur||((cur->frametype==AST_FRAME_CONTROL)&&(cur->subclass.integer==AST_CONTROL_HANGUP))){if(cur){ast_frfree(cur);}res=-1;ast_debug(2,"Hangup of channel %s detected in answer routine\n",chan->name);break;}if((new=ast_frisolate(cur))!=cur){ast_frfree(cur);}AST_LIST_INSERT_HEAD(&frames,new,frame_list);/* if a specific delay period was requested, continue * until that delay has passed. don't stop just because * incoming media has arrived. */if(delay){continue;}switch(new->frametype){/* all of these frametypes qualify as 'media' */caseAST_FRAME_VOICE:caseAST_FRAME_VIDEO:caseAST_FRAME_TEXT:caseAST_FRAME_DTMF_BEGIN:caseAST_FRAME_DTMF_END:caseAST_FRAME_IMAGE:caseAST_FRAME_HTML:caseAST_FRAME_MODEM:done=1;break;caseAST_FRAME_CONTROL:caseAST_FRAME_IAX:caseAST_FRAME_NULL:caseAST_FRAME_CNG:break;}if(done){break;}}if(res==0){ast_channel_lock(chan);while((cur=AST_LIST_REMOVE_HEAD(&frames,frame_list))){ast_queue_frame_head(chan,cur);ast_frfree(cur);}ast_channel_unlock(chan);}}while(0);break;default:break;}returnres;}intast_answer(structast_channel*chan){return__ast_answer(chan,0,1);}voidast_deactivate_generator(structast_channel*chan){ast_channel_lock(chan);if(chan->generatordata){if(chan->generator&&chan->generator->release)chan->generator->release(chan,chan->generatordata);chan->generatordata=NULL;chan->generator=NULL;ast_channel_set_fd(chan,AST_GENERATOR_FD,-1);ast_clear_flag(chan,AST_FLAG_WRITE_INT);ast_settimeout(chan,0,NULL,NULL);}ast_channel_unlock(chan);}staticvoidgenerator_write_format_change(structast_channel*chan){ast_channel_lock(chan);if(chan->generator&&chan->generator->write_format_change){chan->generator->write_format_change(chan,chan->generatordata);}ast_channel_unlock(chan);}staticintgenerator_force(constvoid*data){/* Called if generator doesn't have data */void*tmp;intres;int(*generate)(structast_channel*chan,void*tmp,intdatalen,intsamples)=NULL;structast_channel*chan=(structast_channel*)data;ast_channel_lock(chan);tmp=chan->generatordata;chan->generatordata=NULL;if(chan->generator)generate=chan->generator->generate;ast_channel_unlock(chan);if(!tmp||!generate)return0;res=generate(chan,tmp,0,ast_format_rate(&chan->writeformat)/50);chan->generatordata=tmp;if(res){ast_debug(1,"Auto-deactivating generator\n");ast_deactivate_generator(chan);}return0;}intast_activate_generator(structast_channel*chan,structast_generator*gen,void*params){intres=0;ast_channel_lock(chan);if(chan->generatordata){if(chan->generator&&chan->generator->release)chan->generator->release(chan,chan->generatordata);chan->generatordata=NULL;}if(gen->alloc&&!(chan->generatordata=gen->alloc(chan,params))){res=-1;}if(!res){ast_settimeout(chan,50,generator_force,chan);chan->generator=gen;}ast_channel_unlock(chan);ast_prod(chan);returnres;}/*! \brief Wait for x amount of time on a file descriptor to have input. */intast_waitfor_n_fd(int*fds,intn,int*ms,int*exception){intwinner=-1;ast_waitfor_nandfds(NULL,0,fds,n,exception,&winner,ms);returnwinner;}/*! \brief Wait for x amount of time on a file descriptor to have input. */#ifdef HAVE_EPOLLstaticstructast_channel*ast_waitfor_nandfds_classic(structast_channel**c,intn,int*fds,intnfds,int*exception,int*outfd,int*ms)#elsestructast_channel*ast_waitfor_nandfds(structast_channel**c,intn,int*fds,intnfds,int*exception,int*outfd,int*ms)#endif{structtimevalstart={0,0};structpollfd*pfds=NULL;intres;longrms;intx,y,max;intsz;structtimevalnow={0,0};structtimevalwhentohangup={0,0},diff;structast_channel*winner=NULL;structfdmap{intchan;intfdno;}*fdmap=NULL;if((sz=n*AST_MAX_FDS+nfds)){pfds=alloca(sizeof(*pfds)*sz);fdmap=alloca(sizeof(*fdmap)*sz);}if(outfd)*outfd=-99999;if(exception)*exception=0;/* Perform any pending masquerades */for(x=0;x<n;x++){if(c[x]->masq&&ast_do_masquerade(c[x])){ast_log(LOG_WARNING,"Masquerade failed\n");*ms=-1;returnNULL;}ast_channel_lock(c[x]);if(!ast_tvzero(c[x]->whentohangup)){if(ast_tvzero(whentohangup))now=ast_tvnow();diff=ast_tvsub(c[x]->whentohangup,now);if(diff.tv_sec<0||ast_tvzero(diff)){/* Should already be hungup */c[x]->_softhangup|=AST_SOFTHANGUP_TIMEOUT;ast_channel_unlock(c[x]);returnc[x];}if(ast_tvzero(whentohangup)||ast_tvcmp(diff,whentohangup)<0)whentohangup=diff;}ast_channel_unlock(c[x]);}/* Wait full interval */rms=*ms;/* INT_MAX, not LONG_MAX, because it matters on 64-bit */if(!ast_tvzero(whentohangup)&&whentohangup.tv_sec<INT_MAX/1000){rms=whentohangup.tv_sec*1000+whentohangup.tv_usec/1000;/* timeout in milliseconds */if(*ms>=0&&*ms<rms){/* original *ms still smaller */rms=*ms;}}elseif(!ast_tvzero(whentohangup)&&rms<0){/* Tiny corner case... call would need to last >24 days */rms=INT_MAX;}/* * Build the pollfd array, putting the channels' fds first, * followed by individual fds. Order is important because * individual fd's must have priority over channel fds. */max=0;for(x=0;x<n;x++){for(y=0;y<AST_MAX_FDS;y++){fdmap[max].fdno=y;/* fd y is linked to this pfds */fdmap[max].chan=x;/* channel x is linked to this pfds */max+=ast_add_fd(&pfds[max],c[x]->fds[y]);}CHECK_BLOCKING(c[x]);}/* Add the individual fds */for(x=0;x<nfds;x++){fdmap[max].chan=-1;max+=ast_add_fd(&pfds[max],fds[x]);}if(*ms>0)start=ast_tvnow();if(sizeof(int)==4){/* XXX fix timeout > 600000 on linux x86-32 */do{intkbrms=rms;if(kbrms>600000)kbrms=600000;res=ast_poll(pfds,max,kbrms);if(!res)rms-=kbrms;}while(!res&&(rms>0));}else{res=ast_poll(pfds,max,rms);}for(x=0;x<n;x++)ast_clear_flag(c[x],AST_FLAG_BLOCKING);if(res<0){/* Simulate a timeout if we were interrupted */if(errno!=EINTR)*ms=-1;returnNULL;}if(!ast_tvzero(whentohangup)){/* if we have a timeout, check who expired */now=ast_tvnow();for(x=0;x<n;x++){if(!ast_tvzero(c[x]->whentohangup)&&ast_tvcmp(c[x]->whentohangup,now)<=0){c[x]->_softhangup|=AST_SOFTHANGUP_TIMEOUT;if(winner==NULL)winner=c[x];}}}if(res==0){/* no fd ready, reset timeout and done */*ms=0;/* XXX use 0 since we may not have an exact timeout. */returnwinner;}/* * Then check if any channel or fd has a pending event. * Remember to check channels first and fds last, as they * must have priority on setting 'winner' */for(x=0;x<max;x++){res=pfds[x].revents;if(res==0)continue;if(fdmap[x].chan>=0){/* this is a channel */winner=c[fdmap[x].chan];/* override previous winners */if(res&POLLPRI)ast_set_flag(winner,AST_FLAG_EXCEPTION);elseast_clear_flag(winner,AST_FLAG_EXCEPTION);winner->fdno=fdmap[x].fdno;}else{/* this is an fd */if(outfd)*outfd=pfds[x].fd;if(exception)*exception=(res&POLLPRI)?-1:0;winner=NULL;}}if(*ms>0){*ms-=ast_tvdiff_ms(ast_tvnow(),start);if(*ms<0)*ms=0;}returnwinner;}#ifdef HAVE_EPOLLstaticstructast_channel*ast_waitfor_nandfds_simple(structast_channel*chan,int*ms){structtimevalstart={0,0};intres=0;structepoll_eventev[1];longdiff,rms=*ms;structast_channel*winner=NULL;structast_epoll_data*aed=NULL;/* See if this channel needs to be masqueraded */if(chan->masq&&ast_do_masquerade(chan)){ast_log(LOG_WARNING,"Failed to perform masquerade on %s\n",chan->name);*ms=-1;returnNULL;}ast_channel_lock(chan);/* Figure out their timeout */if(!ast_tvzero(chan->whentohangup)){if((diff=ast_tvdiff_ms(chan->whentohangup,ast_tvnow()))<0){/* They should already be hungup! */chan->_softhangup|=AST_SOFTHANGUP_TIMEOUT;ast_channel_unlock(chan);returnNULL;}/* If this value is smaller then the current one... make it priority */if(rms>diff)rms=diff;}ast_channel_unlock(chan);/* Time to make this channel block... */CHECK_BLOCKING(chan);if(*ms>0)start=ast_tvnow();/* We don't have to add any file descriptors... they are already added, we just have to wait! */res=epoll_wait(chan->epfd,ev,1,rms);/* Stop blocking */ast_clear_flag(chan,AST_FLAG_BLOCKING);/* Simulate a timeout if we were interrupted */if(res<0){if(errno!=EINTR)*ms=-1;returnNULL;}/* If this channel has a timeout see if it expired */if(!ast_tvzero(chan->whentohangup)){if(ast_tvdiff_ms(ast_tvnow(),chan->whentohangup)>=0){chan->_softhangup|=AST_SOFTHANGUP_TIMEOUT;winner=chan;}}/* No fd ready, reset timeout and be done for now */if(!res){*ms=0;returnwinner;}/* See what events are pending */aed=ev[0].data.ptr;chan->fdno=aed->which;if(ev[0].events&EPOLLPRI)ast_set_flag(chan,AST_FLAG_EXCEPTION);elseast_clear_flag(chan,AST_FLAG_EXCEPTION);if(*ms>0){*ms-=ast_tvdiff_ms(ast_tvnow(),start);if(*ms<0)*ms=0;}returnchan;}staticstructast_channel*ast_waitfor_nandfds_complex(structast_channel**c,intn,int*ms){structtimevalstart={0,0};intres=0,i;structepoll_eventev[25]={{0,}};structtimevalnow={0,0};longwhentohangup=0,diff=0,rms=*ms;structast_channel*winner=NULL;for(i=0;i<n;i++){if(c[i]->masq&&ast_do_masquerade(c[i])){ast_log(LOG_WARNING,"Masquerade failed\n");*ms=-1;returnNULL;}ast_channel_lock(c[i]);if(!ast_tvzero(c[i]->whentohangup)){if(whentohangup==0)now=ast_tvnow();if((diff=ast_tvdiff_ms(c[i]->whentohangup,now))<0){c[i]->_softhangup|=AST_SOFTHANGUP_TIMEOUT;ast_channel_unlock(c[i]);returnc[i];}if(!whentohangup||whentohangup>diff)whentohangup=diff;}ast_channel_unlock(c[i]);CHECK_BLOCKING(c[i]);}rms=*ms;if(whentohangup){rms=whentohangup;if(*ms>=0&&*ms<rms)rms=*ms;}if(*ms>0)start=ast_tvnow();res=epoll_wait(c[0]->epfd,ev,25,rms);for(i=0;i<n;i++)ast_clear_flag(c[i],AST_FLAG_BLOCKING);if(res<0){if(errno!=EINTR)*ms=-1;returnNULL;}if(whentohangup){now=ast_tvnow();for(i=0;i<n;i++){if(!ast_tvzero(c[i]->whentohangup)&&ast_tvdiff_ms(now,c[i]->whentohangup)>=0){c[i]->_softhangup|=AST_SOFTHANGUP_TIMEOUT;if(!winner)winner=c[i];}}}if(!res){*ms=0;returnwinner;}for(i=0;i<res;i++){structast_epoll_data*aed=ev[i].data.ptr;if(!ev[i].events||!aed)continue;winner=aed->chan;if(ev[i].events&EPOLLPRI)ast_set_flag(winner,AST_FLAG_EXCEPTION);elseast_clear_flag(winner,AST_FLAG_EXCEPTION);winner->fdno=aed->which;}if(*ms>0){*ms-=ast_tvdiff_ms(ast_tvnow(),start);if(*ms<0)*ms=0;}returnwinner;}structast_channel*ast_waitfor_nandfds(structast_channel**c,intn,int*fds,intnfds,int*exception,int*outfd,int*ms){/* Clear all provided values in one place. */if(outfd)*outfd=-99999;if(exception)*exception=0;/* If no epoll file descriptor is available resort to classic nandfds */if(!n||nfds||c[0]->epfd==-1)returnast_waitfor_nandfds_classic(c,n,fds,nfds,exception,outfd,ms);elseif(!nfds&&n==1)returnast_waitfor_nandfds_simple(c[0],ms);elsereturnast_waitfor_nandfds_complex(c,n,ms);}#endifstructast_channel*ast_waitfor_n(structast_channel**c,intn,int*ms){returnast_waitfor_nandfds(c,n,NULL,0,NULL,NULL,ms);}intast_waitfor(structast_channel*c,intms){intoldms=ms;/* -1 if no timeout */ast_waitfor_nandfds(&c,1,NULL,0,NULL,NULL,&ms);if((ms<0)&&(oldms<0))ms=0;returnms;}/* XXX never to be called with ms = -1 */intast_waitfordigit(structast_channel*c,intms){returnast_waitfordigit_full(c,ms,-1,-1);}intast_settimeout(structast_channel*c,unsignedintrate,int(*func)(constvoid*data),void*data){intres;unsignedintreal_rate=rate,max_rate;ast_channel_lock(c);if(c->timingfd==-1){ast_channel_unlock(c);return-1;}if(!func){rate=0;data=NULL;}if(rate&&rate>(max_rate=ast_timer_get_max_rate(c->timer))){real_rate=max_rate;}ast_debug(1,"Scheduling timer at (%u requested / %u actual) timer ticks per second\n",rate,real_rate);res=ast_timer_set_rate(c->timer,real_rate);c->timingfunc=func;c->timingdata=data;ast_channel_unlock(c);returnres;}intast_waitfordigit_full(structast_channel*c,intms,intaudiofd,intcmdfd){/* Stop if we're a zombie or need a soft hangup */if(ast_test_flag(c,AST_FLAG_ZOMBIE)||ast_check_hangup(c))return-1;/* Only look for the end of DTMF, don't bother with the beginning and don't emulate things */ast_set_flag(c,AST_FLAG_END_DTMF_ONLY);/* Wait for a digit, no more than ms milliseconds total. */while(ms){structast_channel*rchan;intoutfd=-1;errno=0;rchan=ast_waitfor_nandfds(&c,1,&cmdfd,(cmdfd>-1)?1:0,NULL,&outfd,&ms);if(!rchan&&outfd<0&&ms){if(errno==0||errno==EINTR)continue;ast_log(LOG_WARNING,"Wait failed (%s)\n",strerror(errno));ast_clear_flag(c,AST_FLAG_END_DTMF_ONLY);return-1;}elseif(outfd>-1){/* The FD we were watching has something waiting */ast_log(LOG_WARNING,"The FD we were waiting for has something waiting. Waitfordigit returning numeric 1\n");ast_clear_flag(c,AST_FLAG_END_DTMF_ONLY);return1;}elseif(rchan){intres;structast_frame*f=ast_read(c);if(!f)return-1;switch(f->frametype){caseAST_FRAME_DTMF_BEGIN:break;caseAST_FRAME_DTMF_END:res=f->subclass.integer;ast_frfree(f);ast_clear_flag(c,AST_FLAG_END_DTMF_ONLY);returnres;caseAST_FRAME_CONTROL:switch(f->subclass.integer){caseAST_CONTROL_HANGUP:ast_frfree(f);ast_clear_flag(c,AST_FLAG_END_DTMF_ONLY);return-1;caseAST_CONTROL_RINGING:caseAST_CONTROL_ANSWER:caseAST_CONTROL_SRCUPDATE:caseAST_CONTROL_SRCCHANGE:caseAST_CONTROL_CONNECTED_LINE:caseAST_CONTROL_REDIRECTING:case-1:/* Unimportant */break;default:ast_log(LOG_WARNING,"Unexpected control subclass '%d'\n",f->subclass.integer);break;}break;caseAST_FRAME_VOICE:/* Write audio if appropriate */if(audiofd>-1){if(write(audiofd,f->data.ptr,f->datalen)<0){ast_log(LOG_WARNING,"write() failed: %s\n",strerror(errno));}}default:/* Ignore */break;}ast_frfree(f);}}ast_clear_flag(c,AST_FLAG_END_DTMF_ONLY);return0;/* Time is up */}staticvoidsend_dtmf_event(structast_channel*chan,constchar*direction,constchardigit,constchar*begin,constchar*end){ast_manager_event(chan,EVENT_FLAG_DTMF,"DTMF","Channel: %s\r\n""Uniqueid: %s\r\n""Digit: %c\r\n""Direction: %s\r\n""Begin: %s\r\n""End: %s\r\n",chan->name,chan->uniqueid,digit,direction,begin,end);}staticvoidast_read_generator_actions(structast_channel*chan,structast_frame*f){if(chan->generator&&chan->generator->generate&&chan->generatordata&&!ast_internal_timing_enabled(chan)){void*tmp=chan->generatordata;int(*generate)(structast_channel*chan,void*tmp,intdatalen,intsamples)=chan->generator->generate;intres;intsamples;if(chan->timingfunc){ast_debug(1,"Generator got voice, switching to phase locked mode\n");ast_settimeout(chan,0,NULL,NULL);}chan->generatordata=NULL;/* reset, to let writes go through */if(ast_format_cmp(&f->subclass.format,&chan->writeformat)==AST_FORMAT_CMP_NOT_EQUAL){floatfactor;factor=((float)ast_format_rate(&chan->writeformat))/((float)ast_format_rate(&f->subclass.format));samples=(int)(((float)f->samples)*factor);}else{samples=f->samples;}/* This unlock is here based on two assumptions that hold true at this point in the * code. 1) this function is only called from within __ast_read() and 2) all generators * call ast_write() in their generate callback. * * The reason this is added is so that when ast_write is called, the lock that occurs * there will not recursively lock the channel. Doing this will cause intended deadlock * avoidance not to work in deeper functions */ast_channel_unlock(chan);res=generate(chan,tmp,f->datalen,samples);ast_channel_lock(chan);chan->generatordata=tmp;if(res){ast_debug(1,"Auto-deactivating generator\n");ast_deactivate_generator(chan);}}elseif(f->frametype==AST_FRAME_CNG){if(chan->generator&&!chan->timingfunc&&(chan->timingfd>-1)){ast_debug(1,"Generator got CNG, switching to timed mode\n");ast_settimeout(chan,50,generator_force,chan);}}}staticinlinevoidqueue_dtmf_readq(structast_channel*chan,structast_frame*f){structast_frame*fr=&chan->dtmff;fr->frametype=AST_FRAME_DTMF_END;fr->subclass.integer=f->subclass.integer;fr->len=f->len;/* The only time this function will be called is for a frame that just came * out of the channel driver. So, we want to stick it on the tail of the * readq. */ast_queue_frame(chan,fr);}/*! * \brief Determine whether or not we should ignore DTMF in the readq */staticinlineintshould_skip_dtmf(structast_channel*chan){if(ast_test_flag(chan,AST_FLAG_DEFER_DTMF|AST_FLAG_EMULATE_DTMF)){/* We're in the middle of emulating a digit, or DTMF has been * explicitly deferred. Skip this digit, then. */return1;}if(!ast_tvzero(chan->dtmf_tv)&&ast_tvdiff_ms(ast_tvnow(),chan->dtmf_tv)<AST_MIN_DTMF_GAP){/* We're not in the middle of a digit, but it hasn't been long enough * since the last digit, so we'll have to skip DTMF for now. */return1;}return0;}/*! * \brief calculates the number of samples to jump forward with in a monitor stream. * \note When using ast_seekstream() with the read and write streams of a monitor, * the number of samples to seek forward must be of the same sample rate as the stream * or else the jump will not be calculated correctly. * * \retval number of samples to seek forward after rate conversion. */staticinlineintcalc_monitor_jump(intsamples,intsample_rate,intseek_rate){intdiff=sample_rate-seek_rate;if(diff>0){samples=samples/(float)(sample_rate/seek_rate);}elseif(diff<0){samples=samples*(float)(seek_rate/sample_rate);}returnsamples;}staticstructast_frame*__ast_read(structast_channel*chan,intdropaudio){structast_frame*f=NULL;/* the return value */intblah;intprestate;intcause=0;/* this function is very long so make sure there is only one return * point at the end (there are only two exceptions to this). */if(chan->masq){if(ast_do_masquerade(chan))ast_log(LOG_WARNING,"Failed to perform masquerade\n");elsef=&ast_null_frame;returnf;}/* if here, no masq has happened, lock the channel and proceed */ast_channel_lock(chan);/* Stop if we're a zombie or need a soft hangup */if(ast_test_flag(chan,AST_FLAG_ZOMBIE)||ast_check_hangup(chan)){if(chan->generator)ast_deactivate_generator(chan);/* * It is possible for chan->_softhangup to be set and there * still be control frames that need to be read. Instead of * just going to 'done' in the case of ast_check_hangup(), we * need to queue the end-of-Q frame so that it can mark the end * of the read queue. If there are frames to be read, * ast_queue_control() will be called repeatedly, but will only * queue the first end-of-Q frame. */if(chan->_softhangup){ast_queue_control(chan,AST_CONTROL_END_OF_Q);}else{gotodone;}}else{#ifdef AST_DEVMODE/* * The ast_waitfor() code records which of the channel's file * descriptors reported that data is available. In theory, * ast_read() should only be called after ast_waitfor() reports * that a channel has data available for reading. However, * there still may be some edge cases throughout the code where * ast_read() is called improperly. This can potentially cause * problems, so if this is a developer build, make a lot of * noise if this happens so that it can be addressed. * * One of the potential problems is blocking on a dead channel. */if(chan->fdno==-1){ast_log(LOG_ERROR,"ast_read() on chan '%s' called with no recorded file descriptor.\n",chan->name);}#endif}prestate=chan->_state;/* Read and ignore anything on the alertpipe, but read only one sizeof(blah) per frame that we send from it */if(chan->alertpipe[0]>-1){intflags=fcntl(chan->alertpipe[0],F_GETFL);/* For some odd reason, the alertpipe occasionally loses nonblocking status, * which immediately causes a deadlock scenario. Detect and prevent this. */if((flags&O_NONBLOCK)==0){ast_log(LOG_ERROR,"Alertpipe on channel %s lost O_NONBLOCK?!!\n",chan->name);if(fcntl(chan->alertpipe[0],F_SETFL,flags|O_NONBLOCK)<0){ast_log(LOG_WARNING,"Unable to set alertpipe nonblocking! (%d: %s)\n",errno,strerror(errno));f=&ast_null_frame;gotodone;}}if(read(chan->alertpipe[0],&blah,sizeof(blah))<0){if(errno!=EINTR&&errno!=EAGAIN)ast_log(LOG_WARNING,"read() failed: %s\n",strerror(errno));}}if(chan->timingfd>-1&&chan->fdno==AST_TIMING_FD){enumast_timer_eventres;ast_clear_flag(chan,AST_FLAG_EXCEPTION);res=ast_timer_get_event(chan->timer);switch(res){caseAST_TIMING_EVENT_EXPIRED:ast_timer_ack(chan->timer,1);if(chan->timingfunc){/* save a copy of func/data before unlocking the channel */int(*func)(constvoid*)=chan->timingfunc;void*data=chan->timingdata;chan->fdno=-1;ast_channel_unlock(chan);func(data);}else{ast_timer_set_rate(chan->timer,0);chan->fdno=-1;ast_channel_unlock(chan);}/* cannot 'goto done' because the channel is already unlocked */return&ast_null_frame;caseAST_TIMING_EVENT_CONTINUOUS:if(AST_LIST_EMPTY(&chan->readq)||!AST_LIST_NEXT(AST_LIST_FIRST(&chan->readq),frame_list)){ast_timer_disable_continuous(chan->timer);}break;}}elseif(chan->fds[AST_GENERATOR_FD]>-1&&chan->fdno==AST_GENERATOR_FD){/* if the AST_GENERATOR_FD is set, call the generator with args * set to -1 so it can do whatever it needs to. */void*tmp=chan->generatordata;chan->generatordata=NULL;/* reset to let ast_write get through */chan->generator->generate(chan,tmp,-1,-1);chan->generatordata=tmp;f=&ast_null_frame;chan->fdno=-1;gotodone;}elseif(chan->fds[AST_JITTERBUFFER_FD]>-1&&chan->fdno==AST_JITTERBUFFER_FD){ast_clear_flag(chan,AST_FLAG_EXCEPTION);}/* Check for pending read queue */if(!AST_LIST_EMPTY(&chan->readq)){intskip_dtmf=should_skip_dtmf(chan);AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->readq,f,frame_list){/* We have to be picky about which frame we pull off of the readq because * there are cases where we want to leave DTMF frames on the queue until * some later time. */if((f->frametype==AST_FRAME_DTMF_BEGIN||f->frametype==AST_FRAME_DTMF_END)&&skip_dtmf){continue;}AST_LIST_REMOVE_CURRENT(frame_list);break;}AST_LIST_TRAVERSE_SAFE_END;if(!f){/* There were no acceptable frames on the readq. */f=&ast_null_frame;if(chan->alertpipe[0]>-1){intpoke=0;/* Restore the state of the alertpipe since we aren't ready for any * of the frames in the readq. */if(write(chan->alertpipe[1],&poke,sizeof(poke))!=sizeof(poke)){ast_log(LOG_ERROR,"Failed to write to alertpipe: %s\n",strerror(errno));}}}/* Interpret hangup and end-of-Q frames to return NULL *//* XXX why not the same for frames from the channel ? */if(f->frametype==AST_FRAME_CONTROL){switch(f->subclass.integer){caseAST_CONTROL_HANGUP:chan->_softhangup|=AST_SOFTHANGUP_DEV;cause=f->data.uint32;/* Fall through */caseAST_CONTROL_END_OF_Q:ast_frfree(f);f=NULL;break;default:break;}}}else{chan->blocker=pthread_self();if(ast_test_flag(chan,AST_FLAG_EXCEPTION)){if(chan->tech->exception)f=chan->tech->exception(chan);else{ast_log(LOG_WARNING,"Exception flag set on '%s', but no exception handler\n",chan->name);f=&ast_null_frame;}/* Clear the exception flag */ast_clear_flag(chan,AST_FLAG_EXCEPTION);}elseif(chan->tech&&chan->tech->read)f=chan->tech->read(chan);elseast_log(LOG_WARNING,"No read routine on channel %s\n",chan->name);}/* Perform the framehook read event here. After the frame enters the framehook list * there is no telling what will happen, <insert mad scientist laugh here>!!! */f=ast_framehook_list_read_event(chan->framehooks,f);/* * Reset the recorded file descriptor that triggered this read so that we can * easily detect when ast_read() is called without properly using ast_waitfor(). */chan->fdno=-1;if(f){structast_frame*readq_tail=AST_LIST_LAST(&chan->readq);structast_control_read_action_payload*read_action_payload;structast_party_connected_lineconnected;/* if the channel driver returned more than one frame, stuff the excess into the readq for the next ast_read call */if(AST_LIST_NEXT(f,frame_list)){ast_queue_frame(chan,AST_LIST_NEXT(f,frame_list));ast_frfree(AST_LIST_NEXT(f,frame_list));AST_LIST_NEXT(f,frame_list)=NULL;}switch(f->frametype){caseAST_FRAME_CONTROL:if(f->subclass.integer==AST_CONTROL_ANSWER){if(!ast_test_flag(chan,AST_FLAG_OUTGOING)){ast_debug(1,"Ignoring answer on an inbound call!\n");ast_frfree(f);f=&ast_null_frame;}elseif(prestate==AST_STATE_UP&&ast_bridged_channel(chan)){ast_debug(1,"Dropping duplicate answer!\n");ast_frfree(f);f=&ast_null_frame;}else{/* Answer the CDR */ast_setstate(chan,AST_STATE_UP);/* removed a call to ast_cdr_answer(chan->cdr) from here. */ast_cel_report_event(chan,AST_CEL_ANSWER,NULL,NULL,NULL);}}elseif(f->subclass.integer==AST_CONTROL_READ_ACTION){read_action_payload=f->data.ptr;switch(read_action_payload->action){caseAST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO:ast_party_connected_line_init(&connected);ast_party_connected_line_copy(&connected,&chan->connected);if(ast_connected_line_parse_data(read_action_payload->payload,read_action_payload->payload_size,&connected)){ast_party_connected_line_free(&connected);break;}if(ast_channel_connected_line_macro(NULL,chan,&connected,1,0)){ast_indicate_data(chan,AST_CONTROL_CONNECTED_LINE,read_action_payload->payload,read_action_payload->payload_size);}ast_party_connected_line_free(&connected);break;}ast_frfree(f);f=&ast_null_frame;}break;caseAST_FRAME_DTMF_END:send_dtmf_event(chan,"Received",f->subclass.integer,"No","Yes");ast_log(LOG_DTMF,"DTMF end '%c' received on %s, duration %ld ms\n",f->subclass.integer,chan->name,f->len);/* Queue it up if DTMF is deferred, or if DTMF emulation is forced. */if(ast_test_flag(chan,AST_FLAG_DEFER_DTMF)||ast_test_flag(chan,AST_FLAG_EMULATE_DTMF)){queue_dtmf_readq(chan,f);ast_frfree(f);f=&ast_null_frame;}elseif(!ast_test_flag(chan,AST_FLAG_IN_DTMF|AST_FLAG_END_DTMF_ONLY)){if(!ast_tvzero(chan->dtmf_tv)&&ast_tvdiff_ms(ast_tvnow(),chan->dtmf_tv)<AST_MIN_DTMF_GAP){/* If it hasn't been long enough, defer this digit */queue_dtmf_readq(chan,f);ast_frfree(f);f=&ast_null_frame;}else{/* There was no begin, turn this into a begin and send the end later */f->frametype=AST_FRAME_DTMF_BEGIN;ast_set_flag(chan,AST_FLAG_EMULATE_DTMF);chan->emulate_dtmf_digit=f->subclass.integer;chan->dtmf_tv=ast_tvnow();if(f->len){if(f->len>AST_MIN_DTMF_DURATION)chan->emulate_dtmf_duration=f->len;elsechan->emulate_dtmf_duration=AST_MIN_DTMF_DURATION;}elsechan->emulate_dtmf_duration=AST_DEFAULT_EMULATE_DTMF_DURATION;ast_log(LOG_DTMF,"DTMF begin emulation of '%c' with duration %u queued on %s\n",f->subclass.integer,chan->emulate_dtmf_duration,chan->name);}if(chan->audiohooks){structast_frame*old_frame=f;/*! * \todo XXX It is possible to write a digit to the audiohook twice * if the digit was originally read while the channel was in autoservice. */f=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_READ,f);if(old_frame!=f)ast_frfree(old_frame);}}else{structtimevalnow=ast_tvnow();if(ast_test_flag(chan,AST_FLAG_IN_DTMF)){ast_log(LOG_DTMF,"DTMF end accepted with begin '%c' on %s\n",f->subclass.integer,chan->name);ast_clear_flag(chan,AST_FLAG_IN_DTMF);if(!f->len)f->len=ast_tvdiff_ms(now,chan->dtmf_tv);/* detect tones that were received on * the wire with durations shorter than * AST_MIN_DTMF_DURATION and set f->len * to the actual duration of the DTMF * frames on the wire. This will cause * dtmf emulation to be triggered later * on. */if(ast_tvdiff_ms(now,chan->dtmf_tv)<AST_MIN_DTMF_DURATION){f->len=ast_tvdiff_ms(now,chan->dtmf_tv);ast_log(LOG_DTMF,"DTMF end '%c' detected to have actual duration %ld on the wire, emulation will be triggered on %s\n",f->subclass.integer,f->len,chan->name);}}elseif(!f->len){ast_log(LOG_DTMF,"DTMF end accepted without begin '%c' on %s\n",f->subclass.integer,chan->name);f->len=AST_MIN_DTMF_DURATION;}if(f->len<AST_MIN_DTMF_DURATION&&!ast_test_flag(chan,AST_FLAG_END_DTMF_ONLY)){ast_log(LOG_DTMF,"DTMF end '%c' has duration %ld but want minimum %d, emulating on %s\n",f->subclass.integer,f->len,AST_MIN_DTMF_DURATION,chan->name);ast_set_flag(chan,AST_FLAG_EMULATE_DTMF);chan->emulate_dtmf_digit=f->subclass.integer;chan->emulate_dtmf_duration=AST_MIN_DTMF_DURATION-f->len;ast_frfree(f);f=&ast_null_frame;}else{ast_log(LOG_DTMF,"DTMF end passthrough '%c' on %s\n",f->subclass.integer,chan->name);if(f->len<AST_MIN_DTMF_DURATION){f->len=AST_MIN_DTMF_DURATION;}chan->dtmf_tv=now;}if(chan->audiohooks){structast_frame*old_frame=f;f=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_READ,f);if(old_frame!=f)ast_frfree(old_frame);}}break;caseAST_FRAME_DTMF_BEGIN:send_dtmf_event(chan,"Received",f->subclass.integer,"Yes","No");ast_log(LOG_DTMF,"DTMF begin '%c' received on %s\n",f->subclass.integer,chan->name);if(ast_test_flag(chan,AST_FLAG_DEFER_DTMF|AST_FLAG_END_DTMF_ONLY|AST_FLAG_EMULATE_DTMF)||(!ast_tvzero(chan->dtmf_tv)&&ast_tvdiff_ms(ast_tvnow(),chan->dtmf_tv)<AST_MIN_DTMF_GAP)){ast_log(LOG_DTMF,"DTMF begin ignored '%c' on %s\n",f->subclass.integer,chan->name);ast_frfree(f);f=&ast_null_frame;}else{ast_set_flag(chan,AST_FLAG_IN_DTMF);chan->dtmf_tv=ast_tvnow();ast_log(LOG_DTMF,"DTMF begin passthrough '%c' on %s\n",f->subclass.integer,chan->name);}break;caseAST_FRAME_NULL:/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration * is reached , because we want to make sure we pass at least one * voice frame through before starting the next digit, to ensure a gap * between DTMF digits. */if(ast_test_flag(chan,AST_FLAG_EMULATE_DTMF)){structtimevalnow=ast_tvnow();if(!chan->emulate_dtmf_duration){ast_clear_flag(chan,AST_FLAG_EMULATE_DTMF);chan->emulate_dtmf_digit=0;}elseif(ast_tvdiff_ms(now,chan->dtmf_tv)>=chan->emulate_dtmf_duration){chan->emulate_dtmf_duration=0;ast_frfree(f);f=&chan->dtmff;f->frametype=AST_FRAME_DTMF_END;f->subclass.integer=chan->emulate_dtmf_digit;f->len=ast_tvdiff_ms(now,chan->dtmf_tv);chan->dtmf_tv=now;ast_clear_flag(chan,AST_FLAG_EMULATE_DTMF);chan->emulate_dtmf_digit=0;ast_log(LOG_DTMF,"DTMF end emulation of '%c' queued on %s\n",f->subclass.integer,chan->name);if(chan->audiohooks){structast_frame*old_frame=f;f=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_READ,f);if(old_frame!=f){ast_frfree(old_frame);}}}}break;caseAST_FRAME_VOICE:/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration * is reached , because we want to make sure we pass at least one * voice frame through before starting the next digit, to ensure a gap * between DTMF digits. */if(ast_test_flag(chan,AST_FLAG_EMULATE_DTMF)&&!chan->emulate_dtmf_duration){ast_clear_flag(chan,AST_FLAG_EMULATE_DTMF);chan->emulate_dtmf_digit=0;}if(dropaudio||ast_test_flag(chan,AST_FLAG_IN_DTMF)){if(dropaudio)ast_read_generator_actions(chan,f);ast_frfree(f);f=&ast_null_frame;}if(ast_test_flag(chan,AST_FLAG_EMULATE_DTMF)&&!ast_test_flag(chan,AST_FLAG_IN_DTMF)){structtimevalnow=ast_tvnow();if(ast_tvdiff_ms(now,chan->dtmf_tv)>=chan->emulate_dtmf_duration){chan->emulate_dtmf_duration=0;ast_frfree(f);f=&chan->dtmff;f->frametype=AST_FRAME_DTMF_END;f->subclass.integer=chan->emulate_dtmf_digit;f->len=ast_tvdiff_ms(now,chan->dtmf_tv);chan->dtmf_tv=now;if(chan->audiohooks){structast_frame*old_frame=f;f=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_READ,f);if(old_frame!=f)ast_frfree(old_frame);}ast_log(LOG_DTMF,"DTMF end emulation of '%c' queued on %s\n",f->subclass.integer,chan->name);}else{/* Drop voice frames while we're still in the middle of the digit */ast_frfree(f);f=&ast_null_frame;}}elseif((f->frametype==AST_FRAME_VOICE)&&!ast_format_cap_iscompatible(chan->nativeformats,&f->subclass.format)){/* This frame is not one of the current native formats -- drop it on the floor */charto[200];ast_log(LOG_NOTICE,"Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",chan->name,ast_getformatname(&f->subclass.format),ast_getformatname_multiple(to,sizeof(to),chan->nativeformats));ast_frfree(f);f=&ast_null_frame;}elseif((f->frametype==AST_FRAME_VOICE)){/* Send frame to audiohooks if present */if(chan->audiohooks){structast_frame*old_frame=f;f=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_READ,f);if(old_frame!=f)ast_frfree(old_frame);}if(chan->monitor&&chan->monitor->read_stream){/* XXX what does this do ? */#ifndef MONITOR_CONSTANT_DELAYintjump=chan->outsmpl-chan->insmpl-4*f->samples;if(jump>=0){jump=calc_monitor_jump((chan->outsmpl-chan->insmpl),ast_format_rate(&f->subclass.format),ast_format_rate(&chan->monitor->read_stream->fmt->format));if(ast_seekstream(chan->monitor->read_stream,jump,SEEK_FORCECUR)==-1)ast_log(LOG_WARNING,"Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");chan->insmpl+=(chan->outsmpl-chan->insmpl)+f->samples;}elsechan->insmpl+=f->samples;#elseintjump=calc_monitor_jump((chan->outsmpl-chan->insmpl),ast_format_rate(f->subclass.codec),ast_format_rate(chan->monitor->read_stream->fmt->format));if(jump-MONITOR_DELAY>=0){if(ast_seekstream(chan->monitor->read_stream,jump-f->samples,SEEK_FORCECUR)==-1)ast_log(LOG_WARNING,"Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");chan->insmpl+=chan->outsmpl-chan->insmpl;}elsechan->insmpl+=f->samples;#endifif(chan->monitor->state==AST_MONITOR_RUNNING){if(ast_writestream(chan->monitor->read_stream,f)<0)ast_log(LOG_WARNING,"Failed to write data to channel monitor read stream\n");}}if(chan->readtrans&&(f=ast_translate(chan->readtrans,f,1))==NULL){f=&ast_null_frame;}/* it is possible for the translation process on chan->readtrans to have produced multiple frames from the single input frame we passed it; if this happens, queue the additional frames *before* the frames we may have queued earlier. if the readq was empty, put them at the head of the queue, and if it was not, put them just after the frame that was at the end of the queue. */if(AST_LIST_NEXT(f,frame_list)){if(!readq_tail){ast_queue_frame_head(chan,AST_LIST_NEXT(f,frame_list));}else{__ast_queue_frame(chan,AST_LIST_NEXT(f,frame_list),0,readq_tail);}ast_frfree(AST_LIST_NEXT(f,frame_list));AST_LIST_NEXT(f,frame_list)=NULL;}/* Run generator sitting on the line if timing device not available * and synchronous generation of outgoing frames is necessary */ast_read_generator_actions(chan,f);}break;default:/* Just pass it on! */break;}}else{/* Make sure we always return NULL in the future */if(!chan->_softhangup){chan->_softhangup|=AST_SOFTHANGUP_DEV;}if(cause)chan->hangupcause=cause;if(chan->generator)ast_deactivate_generator(chan);/* We no longer End the CDR here */}/* High bit prints debugging */if(chan->fin&DEBUGCHAN_FLAG)ast_frame_dump(chan->name,f,"<<");chan->fin=FRAMECOUNT_INC(chan->fin);done:if(chan->music_state&&chan->generator&&chan->generator->digit&&f&&f->frametype==AST_FRAME_DTMF_END)chan->generator->digit(chan,f->subclass.integer);if(chan->audiohooks&&ast_audiohook_write_list_empty(chan->audiohooks)){/* The list gets recreated if audiohooks are added again later */ast_audiohook_detach_list(chan->audiohooks);chan->audiohooks=NULL;}ast_channel_unlock(chan);returnf;}intast_internal_timing_enabled(structast_channel*chan){return(ast_opt_internal_timing&&chan->timingfd>-1);}structast_frame*ast_read(structast_channel*chan){return__ast_read(chan,0);}structast_frame*ast_read_noaudio(structast_channel*chan){return__ast_read(chan,1);}intast_indicate(structast_channel*chan,intcondition){returnast_indicate_data(chan,condition,NULL,0);}staticintattribute_constis_visible_indication(enumast_control_frame_typecondition){/* Don't include a default case here so that we get compiler warnings * when a new type is added. */switch(condition){caseAST_CONTROL_PROGRESS:caseAST_CONTROL_PROCEEDING:caseAST_CONTROL_VIDUPDATE:caseAST_CONTROL_SRCUPDATE:caseAST_CONTROL_SRCCHANGE:caseAST_CONTROL_RADIO_KEY:caseAST_CONTROL_RADIO_UNKEY:caseAST_CONTROL_OPTION:caseAST_CONTROL_WINK:caseAST_CONTROL_FLASH:caseAST_CONTROL_OFFHOOK:caseAST_CONTROL_TAKEOFFHOOK:caseAST_CONTROL_ANSWER:caseAST_CONTROL_HANGUP:caseAST_CONTROL_CONNECTED_LINE:caseAST_CONTROL_REDIRECTING:caseAST_CONTROL_TRANSFER:caseAST_CONTROL_T38_PARAMETERS:case_XXX_AST_CONTROL_T38:caseAST_CONTROL_CC:caseAST_CONTROL_READ_ACTION:caseAST_CONTROL_AOC:caseAST_CONTROL_END_OF_Q:caseAST_CONTROL_MCID:break;caseAST_CONTROL_CONGESTION:caseAST_CONTROL_BUSY:caseAST_CONTROL_RINGING:caseAST_CONTROL_RING:caseAST_CONTROL_HOLD:/* You can hear these */return1;caseAST_CONTROL_UNHOLD:/* This is a special case. You stop hearing this. */break;}return0;}intast_indicate_data(structast_channel*chan,int_condition,constvoid*data,size_tdatalen){/* By using an enum, we'll get compiler warnings for values not handled * in switch statements. */enumast_control_frame_typecondition=_condition;structast_tone_zone_sound*ts=NULL;intres;/* this frame is used by framehooks. if it is set, we must free it at the end of this function */structast_frame*awesome_frame=NULL;ast_channel_lock(chan);/* Don't bother if the channel is about to go away, anyway. */if(ast_test_flag(chan,AST_FLAG_ZOMBIE)||ast_check_hangup(chan)){res=-1;gotoindicate_cleanup;}if(!ast_framehook_list_is_empty(chan->framehooks)){/* Do framehooks now, do it, go, go now */structast_frameframe={.frametype=AST_FRAME_CONTROL,.subclass.integer=condition,.data.ptr=(void*)data,/* this cast from const is only okay because we do the ast_frdup below */.datalen=datalen};/* we have now committed to freeing this frame */awesome_frame=ast_frdup(&frame);/* who knows what we will get back! the anticipation is killing me. */if(!(awesome_frame=ast_framehook_list_write_event(chan->framehooks,awesome_frame))||awesome_frame->frametype!=AST_FRAME_CONTROL){res=0;gotoindicate_cleanup;}condition=awesome_frame->subclass.integer;data=awesome_frame->data.ptr;datalen=awesome_frame->datalen;}switch(condition){caseAST_CONTROL_CONNECTED_LINE:{structast_party_connected_lineconnected;ast_party_connected_line_set_init(&connected,&chan->connected);res=ast_connected_line_parse_data(data,datalen,&connected);if(!res){ast_channel_set_connected_line(chan,&connected,NULL);}ast_party_connected_line_free(&connected);}break;caseAST_CONTROL_REDIRECTING:{structast_party_redirectingredirecting;ast_party_redirecting_set_init(&redirecting,&chan->redirecting);res=ast_redirecting_parse_data(data,datalen,&redirecting);if(!res){ast_channel_set_redirecting(chan,&redirecting,NULL);}ast_party_redirecting_free(&redirecting);}break;default:break;}if(is_visible_indication(condition)){/* A new visible indication is requested. */chan->visible_indication=condition;}elseif(condition==AST_CONTROL_UNHOLD||_condition<0){/* Visible indication is cleared/stopped. */chan->visible_indication=0;}if(chan->tech->indicate){/* See if the channel driver can handle this condition. */res=chan->tech->indicate(chan,condition,data,datalen);}else{res=-1;}if(!res){/* The channel driver successfully handled this indication */res=0;gotoindicate_cleanup;}/* The channel driver does not support this indication, let's fake * it by doing our own tone generation if applicable. *//*!\note If we compare the enumeration type, which does not have any * negative constants, the compiler may optimize this code away. * Therefore, we must perform an integer comparison here. */if(_condition<0){/* Stop any tones that are playing */ast_playtones_stop(chan);res=0;gotoindicate_cleanup;}/* Handle conditions that we have tones for. */switch(condition){case_XXX_AST_CONTROL_T38:/* deprecated T.38 control frame */res=-1;gotoindicate_cleanup;caseAST_CONTROL_T38_PARAMETERS:/* there is no way to provide 'default' behavior for these * control frames, so we need to return failure, but there * is also no value in the log message below being emitted * since failure to handle these frames is not an 'error' * so just return right now. in addition, we want to return * whatever value the channel driver returned, in case it * has some meaning.*/gotoindicate_cleanup;caseAST_CONTROL_RINGING:ts=ast_get_indication_tone(chan->zone,"ring");/* It is common practice for channel drivers to return -1 if trying * to indicate ringing on a channel which is up. The idea is to let the * core generate the ringing inband. However, we don't want the * warning message about not being able to handle the specific indication * to print nor do we want ast_indicate_data to return an "error" for this * condition */if(chan->_state==AST_STATE_UP){res=0;}break;caseAST_CONTROL_BUSY:ts=ast_get_indication_tone(chan->zone,"busy");break;caseAST_CONTROL_CONGESTION:ts=ast_get_indication_tone(chan->zone,"congestion");break;caseAST_CONTROL_PROGRESS:caseAST_CONTROL_PROCEEDING:caseAST_CONTROL_VIDUPDATE:caseAST_CONTROL_SRCUPDATE:caseAST_CONTROL_SRCCHANGE:caseAST_CONTROL_RADIO_KEY:caseAST_CONTROL_RADIO_UNKEY:caseAST_CONTROL_OPTION:caseAST_CONTROL_WINK:caseAST_CONTROL_FLASH:caseAST_CONTROL_OFFHOOK:caseAST_CONTROL_TAKEOFFHOOK:caseAST_CONTROL_ANSWER:caseAST_CONTROL_HANGUP:caseAST_CONTROL_RING:caseAST_CONTROL_HOLD:caseAST_CONTROL_UNHOLD:caseAST_CONTROL_TRANSFER:caseAST_CONTROL_CONNECTED_LINE:caseAST_CONTROL_REDIRECTING:caseAST_CONTROL_CC:caseAST_CONTROL_READ_ACTION:caseAST_CONTROL_AOC:caseAST_CONTROL_END_OF_Q:caseAST_CONTROL_MCID:/* Nothing left to do for these. */res=0;break;}if(ts){/* We have a tone to play, yay. */ast_debug(1,"Driver for channel '%s' does not support indication %d, emulating it\n",chan->name,condition);res=ast_playtones_start(chan,0,ts->data,1);ts=ast_tone_zone_sound_unref(ts);}if(res){/* not handled */ast_log(LOG_WARNING,"Unable to handle indication %d for '%s'\n",condition,chan->name);}indicate_cleanup:ast_channel_unlock(chan);if(awesome_frame){ast_frfree(awesome_frame);}returnres;}intast_recvchar(structast_channel*chan,inttimeout){intc;char*buf=ast_recvtext(chan,timeout);if(buf==NULL)return-1;/* error or timeout */c=*(unsignedchar*)buf;ast_free(buf);returnc;}char*ast_recvtext(structast_channel*chan,inttimeout){intres,done=0;char*buf=NULL;while(!done){structast_frame*f;if(ast_check_hangup(chan))break;res=ast_waitfor(chan,timeout);if(res<=0)/* timeout or error */break;timeout=res;/* update timeout */f=ast_read(chan);if(f==NULL)break;/* no frame */if(f->frametype==AST_FRAME_CONTROL&&f->subclass.integer==AST_CONTROL_HANGUP)done=1;/* force a break */elseif(f->frametype==AST_FRAME_TEXT){/* what we want */buf=ast_strndup((char*)f->data.ptr,f->datalen);/* dup and break */done=1;}ast_frfree(f);}returnbuf;}intast_sendtext(structast_channel*chan,constchar*text){intres=0;ast_channel_lock(chan);/* Stop if we're a zombie or need a soft hangup */if(ast_test_flag(chan,AST_FLAG_ZOMBIE)||ast_check_hangup(chan)){ast_channel_unlock(chan);return-1;}CHECK_BLOCKING(chan);if(chan->tech->send_text)res=chan->tech->send_text(chan,text);ast_clear_flag(chan,AST_FLAG_BLOCKING);ast_channel_unlock(chan);returnres;}intast_senddigit_begin(structast_channel*chan,chardigit){/* Device does not support DTMF tones, lets fake * it by doing our own generation. */staticconstchar*constdtmf_tones[]={"941+1336",/* 0 */"697+1209",/* 1 */"697+1336",/* 2 */"697+1477",/* 3 */"770+1209",/* 4 */"770+1336",/* 5 */"770+1477",/* 6 */"852+1209",/* 7 */"852+1336",/* 8 */"852+1477",/* 9 */"697+1633",/* A */"770+1633",/* B */"852+1633",/* C */"941+1633",/* D */"941+1209",/* * */"941+1477"/* # */};if(!chan->tech->send_digit_begin)return0;if(!chan->tech->send_digit_begin(chan,digit))return0;if(digit>='0'&&digit<='9')ast_playtones_start(chan,0,dtmf_tones[digit-'0'],0);elseif(digit>='A'&&digit<='D')ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10],0);elseif(digit=='*')ast_playtones_start(chan,0,dtmf_tones[14],0);elseif(digit=='#')ast_playtones_start(chan,0,dtmf_tones[15],0);else{/* not handled */ast_debug(1,"Unable to generate DTMF tone '%c' for '%s'\n",digit,chan->name);}return0;}intast_senddigit_end(structast_channel*chan,chardigit,unsignedintduration){intres=-1;if(chan->tech->send_digit_end)res=chan->tech->send_digit_end(chan,digit,duration);if(res&&chan->generator)ast_playtones_stop(chan);return0;}intast_senddigit(structast_channel*chan,chardigit,unsignedintduration){if(chan->tech->send_digit_begin){ast_senddigit_begin(chan,digit);ast_safe_sleep(chan,(duration>=AST_DEFAULT_EMULATE_DTMF_DURATION?duration:AST_DEFAULT_EMULATE_DTMF_DURATION));}returnast_senddigit_end(chan,digit,(duration>=AST_DEFAULT_EMULATE_DTMF_DURATION?duration:AST_DEFAULT_EMULATE_DTMF_DURATION));}intast_prod(structast_channel*chan){structast_framea={AST_FRAME_VOICE};charnothing[128];/* Send an empty audio frame to get things moving */if(chan->_state!=AST_STATE_UP){ast_debug(1,"Prodding channel '%s'\n",chan->name);ast_format_copy(&a.subclass.format,&chan->rawwriteformat);a.data.ptr=nothing+AST_FRIENDLY_OFFSET;a.src="ast_prod";/* this better match check in ast_write */if(ast_write(chan,&a))ast_log(LOG_WARNING,"Prodding channel '%s' failed\n",chan->name);}return0;}intast_write_video(structast_channel*chan,structast_frame*fr){intres;if(!chan->tech->write_video)return0;res=ast_write(chan,fr);if(!res)res=1;returnres;}structplc_ds{/* A buffer in which to store SLIN PLC * samples generated by the generic PLC * functionality in plc.c */int16_t*samples_buf;/* The current number of samples in the * samples_buf */size_tnum_samples;plc_state_tplc_state;};staticvoidplc_ds_destroy(void*data){structplc_ds*plc=data;ast_free(plc->samples_buf);ast_free(plc);}staticstructast_datastore_infoplc_ds_info={.type="plc",.destroy=plc_ds_destroy,};staticvoidadjust_frame_for_plc(structast_channel*chan,structast_frame*frame,structast_datastore*datastore){intnum_new_samples=frame->samples;structplc_ds*plc=datastore->data;/* As a general note, let me explain the somewhat odd calculations used when taking * the frame offset into account here. According to documentation in frame.h, the frame's * offset field indicates the number of bytes that the audio is offset. The plc->samples_buf * is not an array of bytes, but rather an array of 16-bit integers since it holds SLIN * samples. So I had two choices to make here with the offset. * * 1. Make the offset AST_FRIENDLY_OFFSET bytes. The main downside for this is that * I can't just add AST_FRIENDLY_OFFSET to the plc->samples_buf and have the pointer * arithmetic come out right. I would have to do some odd casting or division for this to * work as I wanted. * 2. Make the offset AST_FRIENDLY_OFFSET * 2 bytes. This allows the pointer arithmetic * to work out better with the plc->samples_buf. The downside here is that the buffer's * allocation contains an extra 64 bytes of unused space. * * I decided to go with option 2. This is why in the calloc statement and the statement that * sets the frame's offset, AST_FRIENDLY_OFFSET is multiplied by 2. *//* If this audio frame has no samples to fill in, ignore it */if(!num_new_samples){return;}/* First, we need to be sure that our buffer is large enough to accomodate * the samples we need to fill in. This will likely only occur on the first * frame we write. */if(plc->num_samples<num_new_samples){ast_free(plc->samples_buf);plc->samples_buf=ast_calloc(1,(num_new_samples*sizeof(*plc->samples_buf))+(AST_FRIENDLY_OFFSET*2));if(!plc->samples_buf){ast_channel_datastore_remove(chan,datastore);ast_datastore_free(datastore);return;}plc->num_samples=num_new_samples;}if(frame->datalen==0){plc_fillin(&plc->plc_state,plc->samples_buf+AST_FRIENDLY_OFFSET,frame->samples);frame->data.ptr=plc->samples_buf+AST_FRIENDLY_OFFSET;frame->datalen=num_new_samples*2;frame->offset=AST_FRIENDLY_OFFSET*2;}else{plc_rx(&plc->plc_state,frame->data.ptr,frame->samples);}}staticvoidapply_plc(structast_channel*chan,structast_frame*frame){structast_datastore*datastore;structplc_ds*plc;datastore=ast_channel_datastore_find(chan,&plc_ds_info,NULL);if(datastore){plc=datastore->data;adjust_frame_for_plc(chan,frame,datastore);return;}datastore=ast_datastore_alloc(&plc_ds_info,NULL);if(!datastore){return;}plc=ast_calloc(1,sizeof(*plc));if(!plc){ast_datastore_free(datastore);return;}datastore->data=plc;ast_channel_datastore_add(chan,datastore);adjust_frame_for_plc(chan,frame,datastore);}intast_write(structast_channel*chan,structast_frame*fr){intres=-1;structast_frame*f=NULL;intcount=0;/*Deadlock avoidance*/while(ast_channel_trylock(chan)){/*cannot goto done since the channel is not locked*/if(count++>10){ast_debug(1,"Deadlock avoided for write to channel '%s'\n",chan->name);return0;}usleep(1);}/* Stop if we're a zombie or need a soft hangup */if(ast_test_flag(chan,AST_FLAG_ZOMBIE)||ast_check_hangup(chan))gotodone;/* Handle any pending masquerades */if(chan->masq){ast_channel_unlock(chan);if(ast_do_masquerade(chan)){ast_log(LOG_WARNING,"Failed to perform masquerade\n");returnres;/* no need to goto done: chan is already unlocked for masq */}ast_channel_lock(chan);}if(chan->masqr){res=0;/* XXX explain, why 0 ? */gotodone;}/* Perform the framehook write event here. After the frame enters the framehook list * there is no telling what will happen, how awesome is that!!! */if(!(fr=ast_framehook_list_write_event(chan->framehooks,fr))){res=0;gotodone;}if(chan->generatordata&&(!fr->src||strcasecmp(fr->src,"ast_prod"))){if(ast_test_flag(chan,AST_FLAG_WRITE_INT)){ast_deactivate_generator(chan);}else{if(fr->frametype==AST_FRAME_DTMF_END){/* There is a generator running while we're in the middle of a digit. * It's probably inband DTMF, so go ahead and pass it so it can * stop the generator */ast_clear_flag(chan,AST_FLAG_BLOCKING);ast_channel_unlock(chan);res=ast_senddigit_end(chan,fr->subclass.integer,fr->len);ast_channel_lock(chan);CHECK_BLOCKING(chan);}elseif(fr->frametype==AST_FRAME_CONTROL&&fr->subclass.integer==AST_CONTROL_UNHOLD){/* This is a side case where Echo is basically being called and the person put themselves on hold and took themselves off hold */res=(chan->tech->indicate==NULL)?0:chan->tech->indicate(chan,fr->subclass.integer,fr->data.ptr,fr->datalen);}res=0;/* XXX explain, why 0 ? */gotodone;}}/* High bit prints debugging */if(chan->fout&DEBUGCHAN_FLAG)ast_frame_dump(chan->name,fr,">>");CHECK_BLOCKING(chan);switch(fr->frametype){caseAST_FRAME_CONTROL:res=(chan->tech->indicate==NULL)?0:chan->tech->indicate(chan,fr->subclass.integer,fr->data.ptr,fr->datalen);break;caseAST_FRAME_DTMF_BEGIN:if(chan->audiohooks){structast_frame*old_frame=fr;fr=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_WRITE,fr);if(old_frame!=fr)f=fr;}send_dtmf_event(chan,"Sent",fr->subclass.integer,"Yes","No");ast_clear_flag(chan,AST_FLAG_BLOCKING);ast_channel_unlock(chan);res=ast_senddigit_begin(chan,fr->subclass.integer);ast_channel_lock(chan);CHECK_BLOCKING(chan);break;caseAST_FRAME_DTMF_END:if(chan->audiohooks){structast_frame*new_frame=fr;new_frame=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_WRITE,fr);if(new_frame!=fr){ast_frfree(new_frame);}}send_dtmf_event(chan,"Sent",fr->subclass.integer,"No","Yes");ast_clear_flag(chan,AST_FLAG_BLOCKING);ast_channel_unlock(chan);res=ast_senddigit_end(chan,fr->subclass.integer,fr->len);ast_channel_lock(chan);CHECK_BLOCKING(chan);break;caseAST_FRAME_TEXT:if(fr->subclass.integer==AST_FORMAT_T140){res=(chan->tech->write_text==NULL)?0:chan->tech->write_text(chan,fr);}else{res=(chan->tech->send_text==NULL)?0:chan->tech->send_text(chan,(char*)fr->data.ptr);}break;caseAST_FRAME_HTML:res=(chan->tech->send_html==NULL)?0:chan->tech->send_html(chan,fr->subclass.integer,(char*)fr->data.ptr,fr->datalen);break;caseAST_FRAME_VIDEO:/* XXX Handle translation of video codecs one day XXX */res=(chan->tech->write_video==NULL)?0:chan->tech->write_video(chan,fr);break;caseAST_FRAME_MODEM:res=(chan->tech->write==NULL)?0:chan->tech->write(chan,fr);break;caseAST_FRAME_VOICE:if(chan->tech->write==NULL)break;/*! \todo XXX should return 0 maybe ? */if(ast_opt_generic_plc&&fr->subclass.format.id==AST_FORMAT_SLINEAR){apply_plc(chan,fr);}/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */if(ast_format_cmp(&fr->subclass.format,&chan->rawwriteformat)!=AST_FORMAT_CMP_NOT_EQUAL)f=fr;elsef=(chan->writetrans)?ast_translate(chan->writetrans,fr,0):fr;if(!f){res=0;break;}if(chan->audiohooks){structast_frame*prev=NULL,*new_frame,*cur,*dup;intfreeoldlist=0;if(f!=fr){freeoldlist=1;}/* Since ast_audiohook_write may return a new frame, and the cur frame is * an item in a list of frames, create a new list adding each cur frame back to it * regardless if the cur frame changes or not. */for(cur=f;cur;cur=AST_LIST_NEXT(cur,frame_list)){new_frame=ast_audiohook_write_list(chan,chan->audiohooks,AST_AUDIOHOOK_DIRECTION_WRITE,cur);/* if this frame is different than cur, preserve the end of the list, * free the old frames, and set cur to be the new frame */if(new_frame!=cur){/* doing an ast_frisolate here seems silly, but we are not guaranteed the new_frame * isn't part of local storage, meaning if ast_audiohook_write is called multiple * times it may override the previous frame we got from it unless we dup it */