Skip to content
Snippets Groups Projects
app_rpt.c 199 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		} else if (!strcmp(var->name, "nobusyout")) {
    			rpt_vars[n].p.nobusyout = ast_true(var->value);
    		} else if (!strcmp(var->name, "nodes")) {
    			ast_copy_string(rpt_vars[n].p.nodes, var->value, sizeof(rpt_vars[n].p.nodes));
    
    		} else if (!strcmp(var->name, "rxnotch")) {
    			char *tmp = ast_strdupa(val);
    
    			strs.argc &= ~1; /* force an even number, rounded down */
    			if (strs.argc >= 2) {
    				for (j = 0; j < strs.argc; j += 2) {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    					rpt_mknotch(atof(strs.str[j]), atof(strs.str[j + 1]),
    
    						&rpt_vars[n].filters[j >> 1].gain,
    						&rpt_vars[n].filters[j >> 1].const0,
    						&rpt_vars[n].filters[j >> 1].const1,
    						&rpt_vars[n].filters[j >> 1].const2);
    					sprintf(rpt_vars[n].filters[j >> 1].desc, "%s Hz, BW = %s",
    						strs.str[j], strs.str[j + 1]);
    				}
    			}
    #endif
    
    	/* If these aren't specified, copy them from the functions property. */
    	if (ast_strlen_zero(rpt_vars[n].p.link_functions))
    		ast_copy_string(rpt_vars[n].p.link_functions, rpt_vars[n].p.functions, sizeof(rpt_vars[n].p.link_functions));
    
    	rpt_vars[n].longestnode = 0;
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].longestnode)
    			rpt_vars[n].longestnode = j;
    
    	}
    
    	/*
    	* For this repeater, Determine the length of the longest function 
    	*/
    	rpt_vars[n].longestfunc = 0;
    
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.functions); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].longestfunc)
    
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].link_longestfunc)
    
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].phone_longestfunc)
    			rpt_vars[n].phone_longestfunc = j;
    
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].dphone_longestfunc)
    			rpt_vars[n].dphone_longestfunc = j;
    
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.macro); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].macro_longest)
    
    
    	rpt_vars[n].gosub_longest = 1;
    	for (vp = ast_variable_browse(cfg, rpt_vars[n].p.gosub); vp; vp = vp->next) {
    		if ((j = strlen(vp->name)) > rpt_vars[n].gosub_longest)
    			rpt_vars[n].gosub_longest = j;
    	}
    
    /*
    * Enable or disable debug output at a given level at the console
    */
    
    static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "rpt debug level";
    		e->usage =
    			"Usage: rpt debug level {0-7}\n"
    			"       Enables debug messages in app_rpt\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    	if (a->argc != 4)
    		return CLI_SHOWUSAGE;
    	newlevel = myatoi(a->argv[3]);
    
    	if ((newlevel < 0) || (newlevel > 7))
    
    		ast_cli(a->fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
    
    		ast_cli(a->fd, "app_rpt Debugging disabled\n");
    
    	debug = newlevel;
    
    static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "rpt dump";
    		e->usage =
    			"Usage: rpt dump <nodename>\n"
    			"       Dumps struct debug info to log\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 3)
    		return CLI_SHOWUSAGE;
    
    		if (!strcmp(a->argv[2], rpt_vars[i].name)) {
    
    			rpt_vars[i].disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
    
    			ast_cli(a->fd, "app_rpt struct dump requested for node %s\n", a->argv[2]);
    			return CLI_SUCCESS;
    
    static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	int dailytxtime, dailykerchunks;
    	int totalkerchunks, dailykeyups, totalkeyups, timeouts;
    	int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
    	long long totaltxtime;
    
    	char *listoflinks[MAX_STAT_LINKS];	
    	char *lastnodewhichkeyedusup, *lastdtmfcommand;
    	char *tot_state, *ider_state, *patch_state;
    	char *reverse_patch_state, *enable_state, *input_signal, *called_number;
    	struct rpt *myrpt;
    
    	static char *not_applicable = "N/A";
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "rpt stats";
    		e->usage =
    			"Usage: rpt stats <nodename>\n"
    			"       Dumps node statistics to console\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 3)
    		return CLI_SHOWUSAGE;
    
    	for (i = 0 ; i <= MAX_STAT_LINKS; i++)
    
    		listoflinks[i] = NULL;
    
    	tot_state = ider_state = 
    	patch_state = reverse_patch_state = 
    	input_signal = called_number = 
    	lastdtmfcommand = not_applicable;
    
    
    		if (!strcmp(a->argv[2], rpt_vars[i].name)) {
    
    			/* Make a copy of all stat variables while locked */
    			myrpt = &rpt_vars[i];
    			rpt_mutex_lock(&myrpt->lock); /* LOCK */
    
    			dailytxtime = myrpt->dailytxtime;
    			totaltxtime = myrpt->totaltxtime;
    			dailykeyups = myrpt->dailykeyups;
    			totalkeyups = myrpt->totalkeyups;
    			dailykerchunks = myrpt->dailykerchunks;
    			totalkerchunks = myrpt->totalkerchunks;
    			dailyexecdcommands = myrpt->dailyexecdcommands;
    			totalexecdcommands = myrpt->totalexecdcommands;
    			timeouts = myrpt->timeouts;
    
    			/* Traverse the list of connected nodes */
    			reverse_patch_state = "DOWN";
    			j = 0;
    			l = myrpt->links.next;
    
    			while (l != &myrpt->links) {
    				if (l->name[0] == '0') { /* Skip '0' nodes */
    
    					reverse_patch_state = "UP";
    					l = l->next;
    					continue;
    				}
    				listoflinks[j] = ast_strdupa(l->name);
    
    					j++;
    				l = l->next;
    			}
    
    			lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup);			
    
    			if ((!lastnodewhichkeyedusup) || (ast_strlen_zero(lastnodewhichkeyedusup)))
    
    			else if (myrpt->totimer != myrpt->p.totime)
    
    				ider_state = "QUEUED FOR CLEANUP";
    			else
    				ider_state = "CLEAN";
    
    
    			switch (myrpt->callmode) {
    			case 1:
    				patch_state = "DIALING";
    				break;
    			case 2:
    				patch_state = "CONNECTING";
    				break;
    			case 3:
    				patch_state = "UP";
    				break;
    			case 4:
    				patch_state = "CALL FAILED";
    				break;
    			default:
    				patch_state = "DOWN";
    
    			if (!ast_strlen_zero(myrpt->exten))
    
    			if (!ast_strlen_zero(myrpt->lastdtmfcommand))
    
    				lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand);
    
    			rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
    
    
    			ast_cli(a->fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
    			ast_cli(a->fd, "Signal on input..................................: %s\n", input_signal);
    			ast_cli(a->fd, "Transmitter enabled..............................: %s\n", enable_state);
    			ast_cli(a->fd, "Time out timer state.............................: %s\n", tot_state);
    			ast_cli(a->fd, "Time outs since system initialization............: %d\n", timeouts);
    			ast_cli(a->fd, "Identifier state.................................: %s\n", ider_state);
    			ast_cli(a->fd, "Kerchunks today..................................: %d\n", dailykerchunks);
    			ast_cli(a->fd, "Kerchunks since system initialization............: %d\n", totalkerchunks);
    			ast_cli(a->fd, "Keyups today.....................................: %d\n", dailykeyups);
    			ast_cli(a->fd, "Keyups since system initialization...............: %d\n", totalkeyups);
    			ast_cli(a->fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
    			ast_cli(a->fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
    			ast_cli(a->fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand);
    
    			ast_cli(a->fd, "TX time today ...................................: %02d:%02d:%02d.%d\n",
    
    			hours = (int) totaltxtime / 3600000;
    
    			minutes = (int) totaltxtime / 60000;
    
    			seconds = (int)  totaltxtime / 1000;
    
    			ast_cli(a->fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
    
    				 hours, minutes, seconds, (int) totaltxtime);
    
    			ast_cli(a->fd, "Nodes currently connected to us..................: ");
    
    			for (j = 0;; j++) {
    				if (!listoflinks[j]) {
    					if (!j) {
    
    				ast_cli(a->fd, "%s", listoflinks[j]);
    
    					ast_cli(a->fd, "\n");
    					ast_cli(a->fd, "                                                 : ");
    
    			ast_cli(a->fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup);
    			ast_cli(a->fd, "Autopatch state..................................: %s\n", patch_state);
    			ast_cli(a->fd, "Autopatch called number..........................: %s\n", called_number);
    			ast_cli(a->fd, "Reverse patch/IAXRPT connected...................: %s\n\n", reverse_patch_state);
    
    static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	struct rpt_lstat *s, *t;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "rpt lstats";
    		e->usage =
    			"Usage: rpt lstats <nodename>\n"
    			"       Dumps link statistics to console\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 3)
    		return CLI_SHOWUSAGE;
    
    		if (!strcmp(a->argv[2], rpt_vars[i].name)) {
    
    			/* Make a copy of all stat variables while locked */
    			myrpt = &rpt_vars[i];
    			rpt_mutex_lock(&myrpt->lock); /* LOCK */
    			/* Traverse the list of connected nodes */
    			j = 0;
    			l = myrpt->links.next;
    
    			while (l != &myrpt->links) {
    				if (l->name[0] == '0') { /* Skip '0' nodes */
    
    					ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n");
    					rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
    
    				ast_copy_string(s->name, l->name, MAXREMSTR);
    
    				pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1);
    				s->mode = l->mode;
    				s->outbound = l->outbound;
    				s->reconnects = l->reconnects;
    				s->connecttime = l->connecttime;
    				insque((struct qelem *) s, (struct qelem *) s_head.next);
    				l = l->next;
    			}
    			rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
    
    			ast_cli(a->fd, "NODE      PEER                RECONNECTS  DIRECTION  CONNECT TIME\n");
    			ast_cli(a->fd, "----      ----                ----------  ---------  ------------\n");
    
    			for (s = s_head.next; s != &s_head; s = s->next) {
    
    				int hours, minutes, seconds;
    				long long connecttime = s->connecttime;
    				char conntime[31];
    				hours = (int) connecttime/3600000;
    				connecttime %= 3600000;
    				minutes = (int) connecttime/60000;
    				connecttime %= 60000;
    				seconds = (int)  connecttime/1000;
    				connecttime %= 1000;
    
    				snprintf(conntime, sizeof(conntime), "%02d:%02d:%02d.%d",
    
    					hours, minutes, seconds, (int) connecttime);
    
    				ast_cli(a->fd, "%-10s%-20s%-12d%-11s%-30s\n",
    
    					s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime);
    			}	
    			/* destroy our local link queue */
    			s = s_head.next;
    
    static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "rpt reload";
    		e->usage =
    			"Usage: rpt reload\n"
    			"       Reloads app_rpt running config parameters\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc > 2)
    		return CLI_SHOWUSAGE;
    
    	for (n = 0; n < nrpts; n++)
    		rpt_vars[n].reload = 1;
    
    static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "rpt restart";
    		e->usage =
    			"Usage: rpt restart\n"
    			"       Restarts app_rpt\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc > 2)
    		return CLI_SHOWUSAGE;
    
    	for (i = 0; i < nrpts; i++) {
    		if (rpt_vars[i].rxchannel)
    			ast_softhangup(rpt_vars[i].rxchannel, AST_SOFTHANGUP_DEV);
    
    static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
    {
    
    	if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
    		return res;
    
    	while (chan->generatordata) {
    		if (ast_safe_sleep(chan, 1))
    			return -1;
    
    }
    
    static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
    {
    	return play_tone_pair(chan, freq, 0, duration, amplitude);
    }
    
    static int play_silence(struct ast_channel *chan, int duration)
    {
    	return play_tone_pair(chan, 0, 0, duration, 0);
    }
    
    
    
    static int send_morse(struct ast_channel *chan, const char *string, int speed, int freq, int amplitude)
    
    	static struct morse_bits mbits[] = {
    
    		{0, 0}, /* SPACE */
    		{0, 0}, 
    		{6, 18},/* " */
    		{0, 0},
    		{7, 72},/* $ */
    		{0, 0},
    		{0, 0},
    		{6, 30},/* ' */
    		{5, 13},/* ( */
    		{6, 29},/* ) */
    		{0, 0},
    		{5, 10},/* + */
    		{6, 51},/* , */
    		{6, 33},/* - */
    		{6, 42},/* . */
    		{5, 9}, /* / */
    		{5, 31},/* 0 */
    		{5, 30},/* 1 */
    		{5, 28},/* 2 */
    		{5, 24},/* 3 */
    		{5, 16},/* 4 */
    		{5, 0}, /* 5 */
    		{5, 1}, /* 6 */
    		{5, 3}, /* 7 */
    		{5, 7}, /* 8 */
    		{5, 15},/* 9 */
    		{6, 7}, /* : */
    		{6, 21},/* ; */
    		{0, 0},
    		{5, 33},/* = */
    		{0, 0},
    		{6, 12},/* ? */
    		{0, 0},
    
    		{4, 5}, /* C */
    		{3, 1}, /* D */
    		{1, 0}, /* E */
    		{4, 4}, /* F */
    		{3, 3}, /* G */
    		{4, 0}, /* H */
    		{2, 0}, /* I */
    		{4, 14},/* J */
    		{3, 5}, /* K */
    		{4, 2}, /* L */
    		{2, 3}, /* M */
    		{2, 1}, /* N */
    		{3, 7}, /* O */
    		{4, 6}, /* P */
    		{4, 11},/* Q */
    		{3, 2}, /* R */
    		{3, 0}, /* S */
    		{1, 1}, /* T */
    		{3, 4}, /* U */
    		{4, 8}, /* V */
    		{3, 6}, /* W */
    		{4, 9}, /* X */
    		{4, 13},/* Y */
    		{4, 3}  /* Z */
    	};
    
    	int dottime;
    	int dashtime;
    	int intralettertime;
    	int interlettertime;
    	int interwordtime;
    	int len, ddcomb;
    	int res;
    	int c;
    
    	int i;
    	int flags;
    			
    
    	res = 0;
    	
    	/* Approximate the dot time from the speed arg. */
    	
    
    	/* Establish timing relationships */
    
    	
    	dashtime = 3 * dottime;
    	intralettertime = dottime;
    	interlettertime = dottime * 4 ;
    	interwordtime = dottime * 7;
    	
    
    	for (; (*string) && (!res); string++) {
    
    			c -= 0x20;
    		
    		/* Can't deal with any char code greater than Z, skip it */
    		
    
    			continue;
    		
    		/* If space char, wait the inter word time */
    					
    
    				res = play_silence(chan, interwordtime);
    			continue;
    		}
    		
    		/* Subtract out control char offset to match our table */
    		
    		c -= 0x20;
    		
    		/* Get the character data */
    		
    		len = mbits[c].len;
    		ddcomb = mbits[c].ddcomb;
    		
    		/* Send the character */
    		
    
    				res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
    
    				res = play_silence(chan, intralettertime);
    			ddcomb >>= 1;
    		}
    		
    		/* Wait the interletter time */
    		
    
    			res = play_silence(chan, interlettertime - intralettertime);
    	}
    	
    	/* Wait for all the frames to be sent */
    	
    	if (!res) 
    		res = ast_waitstream(chan, "");
    	ast_stopstream(chan);
    
    	
    	/*
    	* Wait for the zaptel driver to physically write the tone blocks to the hardware
    	*/
    
    
    		flags =  ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; 
    		res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
    
    			break;
    
    static int send_tone_telemetry(struct ast_channel *chan, const char *tonestring)
    
    	int i;
    	int flags;
    
    	for (;tonestring;) {
    		tonesubset = strsep(&stringp, ")");
    		if (!tonesubset)
    
    		if (sscanf(tonesubset, "(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
    
    			break;
    		res = play_tone_pair(chan, f1, f2, duration, amplitude);
    
    		res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
    	
    	if (!res) 
    		res = ast_waitstream(chan, "");
    	ast_stopstream(chan);
    
    
    	/*
    	* Wait for the zaptel driver to physically write the tone blocks to the hardware
    	*/
    
    
    		flags =  ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; 
    		res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
    
    			break;
    
    static int sayfile(struct ast_channel *mychannel, const char *fname)
    
    
    	res = ast_streamfile(mychannel, fname, mychannel->language);
    	if (!res) 
    		res = ast_waitstream(mychannel, "");
    	else
    		 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    static int saycharstr(struct ast_channel *mychannel, char *str)
    
    	res = ast_say_character_str(mychannel, str, NULL, mychannel->language);
    
    	if (!res) 
    		res = ast_waitstream(mychannel, "");
    	else
    		 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    
    static int saynum(struct ast_channel *mychannel, int num)
    {
    	int res;
    	res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
    
    	if (!res)
    		res = ast_waitstream(mychannel, "");
    	else
    		ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    static int saydigits(struct ast_channel *mychannel, int num)
    {
    	int res;
    	res = ast_say_digits(mychannel, num, NULL, mychannel->language);
    	if (!res)
    
    		res = ast_waitstream(mychannel, "");
    	else
    		ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    
    
    static int telem_any(struct rpt *myrpt, struct ast_channel *chan, const char *entry)
    
    {
    	int res;
    	char c;
    	
    	static int morsespeed;
    	static int morsefreq;
    	static int morseampl;
    	static int morseidfreq = 0;
    	static int morseidampl;
    	static char mcat[] = MORSE;
    	
    	res = 0;
    	
    
    	if (!morseidfreq) { /* Get the morse parameters if not already loaded */
    
    		morsespeed = retrieve_astcfgint(myrpt, mcat, "speed", 5, 20, 20);
    
    		morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800);
    		morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096);
    
    		morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048);
    		morseidfreq = retrieve_astcfgint(myrpt, mcat, "idfrequency", 300, 3000, 330);	
    
    		switch (c) {
    		case 'I': /* Morse ID */
    			res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
    			break;
    		case 'M': /* Morse Message */
    			res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
    			break;
    		case 'T': /* Tone sequence */
    			res = send_tone_telemetry(chan, entry + 2);
    			break;
    		default:
    			res = -1;
    
    		res = sayfile(chan, entry); /* File */
    	return res;
    }
    
    /*
    * This function looks up a telemetry name in the config file, and does a telemetry response as configured.
    *
    * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
    */
    
    
    static int telem_lookup(struct rpt *myrpt, struct ast_channel *chan, const char *node, const char *name)
    
    	/* Retrieve the section name for telemetry from the node section */
    
    	if ((telemetry = ast_variable_retrieve(myrpt->cfg, node, TELEMETRY)))
    		entry = ast_variable_retrieve(myrpt->cfg, telemetry, name);
    
    
    		/* Telemetry name wasn't found in the config file, use the default */
    
    		for (i = 0; i < sizeof(tele_defs) / sizeof(struct telem_defaults); i++) {
    			if (!strcasecmp(tele_defs[i].name, name))
    
    	if (entry) {	
    		if (!ast_strlen_zero(entry))
    			telem_any(myrpt, chan, entry);
    	} else {
    
    /*
    * Retrieve a wait interval
    */
    
    static int get_wait_interval(struct rpt *myrpt, int type)
    {
    
    	int interval = 1000;
    	const char *wait_times = ast_variable_retrieve(myrpt->cfg, myrpt->name, "wait_times");
    
    
    	switch (type) {
    	case DLY_TELEM:
    		if (wait_times)
    
    			interval = retrieve_astcfgint(myrpt, wait_times, "telemwait", 500, 5000, 1000);
    
    		break;
    	case DLY_ID:
    		if (wait_times)
    
    			interval = retrieve_astcfgint(myrpt, wait_times, "idwait", 250, 5000, 500);
    
    		else
    			interval = 500;
    		break;
    	case DLY_UNKEY:
    		if (wait_times)
    
    			interval = retrieve_astcfgint(myrpt, wait_times, "unkeywait", 500, 5000, 1000);
    
    		break;
    	case DLY_CALLTERM:
    		if (wait_times)
    
    			interval = retrieve_astcfgint(myrpt, wait_times, "calltermwait", 500, 5000, 1500);
    
    static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
    
    	interval = get_wait_interval(myrpt, type);
    
    	if (debug)
    		ast_log(LOG_NOTICE, " Delay interval = %d\n", interval);
    	if (interval)
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		ast_safe_sleep(chan, interval);
    
    	if (debug)
    		ast_log(LOG_NOTICE, "Delay complete\n");
    
    static void *rpt_tele_thread(void *this)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	ZT_CONFINFO ci;  /* conference info */
    	int	res = 0, haslink, hastx, hasremote, imdone = 0, unkeys_queued, x;
    	struct rpt_tele *mytele = (struct rpt_tele *)this;
    	struct rpt_tele *tlist;
    	struct rpt *myrpt;
    	struct rpt_link *l, *m, linkbase;
    	struct ast_channel *mychannel;
    
    #ifdef  APP_RPT_LOCK_DEBUG
    	struct lockthread *t;
    #endif
    
    	/* get a pointer to myrpt */
    	myrpt = mytele->rpt;
    
    
    	/* Snag copies of a few key myrpt variables */
    
    	rpt_mutex_lock(&myrpt->lock);
    	rpt_mutex_unlock(&myrpt->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* allocate a pseudo-channel thru asterisk */
    
    	mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
    	if (!mychannel) {
    		ast_log(LOG_WARNING, "rpt: unable to obtain pseudo channel\n");
    
    		ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_exit(NULL);
    	}
    
    	mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* make a conference for the tx */
    	ci.chan = 0;
    
    	/* If there's an ID queued, or tail message queued, */
    	/* only connect the ID audio to the local tx conference so */
    	/* linked systems can't hear it */
    
    	ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY) || 
    		(mytele->mode == TAILMSG)) ?
    		 	myrpt->txconf : myrpt->conf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ci.confmode = ZT_CONF_CONFANN;
    	/* first put the channel on the conference in announce mode */
    
    	if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
    
    		ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
    		ast_free(mytele);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_exit(NULL);
    	}
    	ast_stopstream(mychannel);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM, mychannel);
    
    		res = telem_any(myrpt, mychannel, myrpt->p.ident); 
    
    	case TAILMSG:
    		res = ast_streamfile(mychannel, myrpt->p.tailmsg.msgs[myrpt->tailmessagen], mychannel->language); 
    
    	case IDTALKOVER:
    		p = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
    		if (p)
    			res = telem_any(myrpt, mychannel, p); 
    		imdone = 1;	
    		break;
    	case PROC:
    
    		wait_interval(myrpt, DLY_TELEM, mychannel);
    
    		res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup");
    
    		if (res < 0) { /* Then default message */
    
    			res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
    		}
    
    		wait_interval(myrpt, DLY_CALLTERM, mychannel);
    
    		res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown");
    
    		if (res < 0) { /* Then default message */
    
    			res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
    		}
    
    		wait_interval(myrpt, DLY_TELEM, mychannel);
    
    		res = telem_lookup(myrpt, mychannel, myrpt->name, "functcomplete");
    
    		/* wait a little bit */
    		wait_interval(myrpt, DLY_TELEM, mychannel);
    		res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language);
    		break;