[asterisk-commits] jdixon: branch jdixon/chan_usbradio-1.4 r114639 - /team/jdixon/chan_usbradio-...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Apr 24 21:09:25 CDT 2008


Author: jdixon
Date: Thu Apr 24 21:09:22 2008
New Revision: 114639

URL: http://svn.digium.com/view/asterisk?view=rev&rev=114639
Log:
Updated to latest version (0.112)

Modified:
    team/jdixon/chan_usbradio-1.4/apps/app_rpt.c

Modified: team/jdixon/chan_usbradio-1.4/apps/app_rpt.c
URL: http://svn.digium.com/view/asterisk/team/jdixon/chan_usbradio-1.4/apps/app_rpt.c?view=diff&rev=114639&r1=114638&r2=114639
==============================================================================
--- team/jdixon/chan_usbradio-1.4/apps/app_rpt.c (original)
+++ team/jdixon/chan_usbradio-1.4/apps/app_rpt.c Thu Apr 24 21:09:22 2008
@@ -1,9 +1,8 @@
 /* #define OLD_ASTERISK */
-#define	OLDKEY
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2002-2007, Jim Dixon, WB6NIL
+ * Copyright (C) 2002-2008, Jim Dixon, WB6NIL
  *
  * Jim Dixon, WB6NIL <jim at lambdatel.com>
  * Serious contributions by Steve RoDgers, WA6ZFT <hwstar at rodgers.sdcoxmail.com>
@@ -21,12 +20,13 @@
 /*! \file
  *
  * \brief Radio Repeater / Remote Base program 
- *  version 0.73 09/04/07
+ *  version 0.112 4/13/08 2055 EDT
  * 
  * \author Jim Dixon, WB6NIL <jim at lambdatel.com>
  *
  * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar at rodgers.sdcoxmail.com>
- * 
+ * \note Steven Henke, W9SH, <w9sh at arrl.net> added a few features here and there.
+ *
  * See http://www.zapatatelephony.org/app_rpt.html
  *
  *
@@ -52,9 +52,11 @@
  *
  * status cmds:
  *
- *  1 - Force ID
- *  2 - Give Time of Day
- *  3 - Give software Version
+ *  1 - Force ID (global)
+ *  2 - Give Time of Day (global)
+ *  3 - Give software Version (global)
+ *  11 - Force ID (local only)
+ *  12 - Give Time of Day (local only)
  *
  * cop (control operator) cmds:
  *
@@ -78,6 +80,15 @@
  *  18 - User functions (time, id, etc) disable
  *  19 - Select alternate hang timer
  *  20 - Select standard hang timer 
+ *  21 - Enable Parrot Mode
+ *  22 - Disable Parrot Mode
+ *  23 - Birdbath (Current Parrot Cleanup/Flush)
+ *  24 - Flush all telemetry
+ *  25 - Query last node un-keyed
+ *  26 - Query all nodes keyed/unkeyed
+ *  30 - Recall Memory Setting in Attached Xcvr
+ *  31 - Channel Selector for Parallel Programmed Xcvr
+ *  32 - Touchtone pad test: command + Digit string + # to playback all digits pressed
  *
  * ilink cmds:
  *
@@ -125,6 +136,9 @@
  *  140 - Link Status (brief)
  *  200 thru 215 - (Send DTMF 0-9,*,#,A-D) (200=0, 201=1, 210=*, etc)
  *
+ * playback cmds:
+ *  specify the name of the file to be played (for example, 25=rpt/foo)
+ *
  *
  * 'duplex' modes:  (defaults to duplex=2)
  *
@@ -152,15 +166,29 @@
 
 /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
 
+#ifdef OLD_ASTERISK
+#define ast_free free
+#define ast_malloc malloc
+#define ast_strdup strdup
+#endif
+
+
 #define	MAXDTMF 32
 #define	MAXMACRO 2048
 #define	MAXLINKLIST 512
 #define	LINKLISTTIME 10000
 #define	LINKLISTSHORTTIME 200
+#define	LINKPOSTTIME 30000
+#define	LINKPOSTSHORTTIME 200
+#define	KEYPOSTTIME 30000
+#define	KEYPOSTSHORTTIME 200
 #define	MACROTIME 100
 #define	MACROPTIME 500
 #define	DTMF_TIMEOUT 3
 #define	KENWOOD_RETRIES 5
+#define	TOPKEYN 32
+#define	TOPKEYWAIT 3
+#define	TOPKEYMAXSTR 30
 
 #define	AUTHTELLTIME 7000
 #define	AUTHTXTIME 1000
@@ -178,7 +206,15 @@
 
 #define	RETRY_TIMER_MS 5000
 
+#define	PATCH_DIALPLAN_TIMEOUT 1500
+
+#ifdef OLD_ASTERISK
+#define	START_DELAY 10
+#else
 #define	START_DELAY 2
+#endif
+
+#define	RPT_LOCKOUT_SECS 10
 
 #define MAXPEERSTR 31
 #define	MAXREMSTR 15
@@ -201,9 +237,14 @@
 #define	FUNCTIONS "functions"
 #define TELEMETRY "telemetry"
 #define MORSE "morse"
+#define	TONEMACRO "tonemacro"
 #define	FUNCCHAR '*'
 #define	ENDCHAR '#'
 #define	EXTNODEFILE "/var/lib/asterisk/rpt_extnodes"
+#define	NODENAMES "rpt/nodenames"
+#define	PARROTFILE "/tmp/parrot_%s_%u"
+
+#define	PARROTTIME 1000
 
 #define	DEFAULT_IOBASE 0x378
 
@@ -212,6 +253,10 @@
 #define	MAXCONNECTTIME 5000
 
 #define MAXNODESTR 300
+
+#define MAXNODELEN 16
+
+#define MAXIDENTLEN 32
 
 #define MAXPATCHCONTEXT 100
 
@@ -226,17 +271,29 @@
 
 #define	IC706_PL_MEMORY_OFFSET 50
 
+#define	VOX_ON_DEBOUNCE_COUNT 3
+#define	VOX_OFF_DEBOUNCE_COUNT 20
+#define	VOX_MAX_THRESHOLD 10000.0
+#define	VOX_MIN_THRESHOLD 3000.0
+#define	VOX_TIMEOUT_MS 5000
+#define	VOX_RECOVER_MS 500
+#define	SIMPLEX_PATCH_DELAY 25
+#define	SIMPLEX_PHONE_DELAY 25
+
+#define	STATPOST_PROGRAM "/usr/bin/wget,-q,--output-document=/dev/null,--no-check-certificate"
+
 #define	ALLOW_LOCAL_CHANNELS
 
 enum {REM_OFF,REM_MONITOR,REM_TX};
 
 enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
-	CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME,
+	CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME, PLAYBACK,
 	STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
 	TAILMSG, MACRO_NOTFOUND, MACRO_BUSY, LASTNODEKEY, FULLSTATUS,
 	MEMNOTFOUND, INVFREQ, REMMODE, REMLOGIN, REMXXX, REMSHORTSTATUS,
-	REMLONGSTATUS, LOGINREQ, SCAN, SCANSTAT, TUNE, SETREMOTE,
-	TIMEOUT_WARNING, ACT_TIMEOUT_WARNING, LINKUNKEY, UNAUTHTX};
+	REMLONGSTATUS, LOGINREQ, SCAN, SCANSTAT, TUNE, SETREMOTE, TOPKEY,
+	TIMEOUT_WARNING, ACT_TIMEOUT_WARNING, LINKUNKEY, UNAUTHTX, PARROT,
+	STATS_TIME_LOCAL};
 
 
 enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
@@ -245,9 +302,9 @@
 
 enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_COMPLETEQUIET, DC_DOKEY};
 
-enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
-
-enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM, DLY_COMP, DLY_LINKUNKEY};
+enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE, SOURCE_ALT};
+
+enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM, DLY_COMP, DLY_LINKUNKEY, DLY_PARROT};
 
 enum {REM_MODE_FM,REM_MODE_USB,REM_MODE_LSB,REM_MODE_AM};
 
@@ -303,6 +360,7 @@
 #include "asterisk/localtime.h"
 #include "asterisk/cdr.h"
 #include "asterisk/options.h"
+#include "asterisk/manager.h"
 #include <termios.h>
 
 /* Start a tone-list going */
@@ -310,14 +368,15 @@
 /*! Stop the tones from playing */
 void ast_playtones_stop(struct ast_channel *chan);
 
-static  char *tdesc = "Radio Repeater / Remote Base  version 0.73  09/04/2007";
+static  char *tdesc = "Radio Repeater / Remote Base  version 0.112  4/13/2008";
 
 static char *app = "Rpt";
 
 static char *synopsis = "Radio Repeater/Remote Base Control System";
 
 static char *descrip = 
-"  Rpt(nodename[|options]):  Radio Remote Link or Remote Base Link Endpoint Process.\n"
+"  Rpt(nodename[|options][|M][|*]):  \n"
+"    Radio Remote Link or Remote Base Link Endpoint Process.\n"
 "\n"
 "    Not specifying an option puts it in normal endpoint mode (where source\n"
 "    IP and nodename are verified).\n"
@@ -349,7 +408,27 @@
 "            this mode), the 'dphone_functions' parameter must be specified\n"
 "            for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
 "            available to the phone user.\n"
+"\n"
+"        S - Simplex Dumb Phone Control mode. This allows a regular phone user\n"
+"            audio-only access to the radio system. In this mode, the\n"
+"            transmitter is toggled on and off when the phone user presses the\n"
+"            funcchar (*) key on the telephone set. In addition, the transmitter\n"
+"            will turn off if the endchar (#) key is pressed. When a user first\n"
+"            calls in, the transmitter will be off, and the user can listen for\n"
+"            radio traffic. When the user wants to transmit, they press the *\n" 
+"            key, start talking, then press the * key again or the # key to turn\n"
+"            the transmitter off.  No other functions can be executed by the\n"
+"            user on the phone when this mode is selected. Note: If your\n"
+"            radio system is full-duplex, we recommend using either P or D\n"
+"            modes as they provide more flexibility.\n"
+"\n"
+"        q - Query Status. Sets channel variables and returns + 101 in plan.\n"
+"\n"
+"        M - Memory Channel Steer as MXX where XX is the memory channel number.\n"
+"\n"
+"        * - Alt Macro to execute (e.g. *7 for status)\n"
 "\n";
+;
 
 static int debug = 0;  /* Set this >0 for extra debug output */
 static int nrpts = 0;
@@ -372,10 +451,17 @@
 };
 
 char *discstr = "!!DISCONNECT!!";
+char *newkeystr = "!NEWKEY!";
 static char *remote_rig_ft897="ft897";
 static char *remote_rig_rbi="rbi";
 static char *remote_rig_kenwood="kenwood";
 static char *remote_rig_ic706="ic706";
+static char *remote_rig_rtx150="rtx150";
+static char *remote_rig_rtx450="rtx450";
+static char *remote_rig_ppp16="ppp16";	  		// parallel port programmable 16 channels
+
+#define ISRIG_RTX(x) ((!strcmp(x,remote_rig_rtx150)) || (!strcmp(x,remote_rig_rtx450)))
+#define	IS_XPMR(x) (!strncasecmp(x->rxchanname,"rad",3))
 
 #ifdef	OLD_ASTERISK
 STANDARD_LOCAL_USER;
@@ -396,6 +482,26 @@
 
 #define MAX_SYSSTATES 10
 
+struct vox {
+	float	speech_energy;
+	float	noise_energy;
+	int	enacount;
+	char	voxena;
+	char	lastvox;
+	int	offdebcnt;
+	int	ondebcnt;
+} ;
+
+#define	mymax(x,y) ((x > y) ? x : y)
+#define	mymin(x,y) ((x < y) ? x : y)
+
+struct rpt_topkey
+{
+char	node[TOPKEYMAXSTR];
+int	timesince;
+int	keyed;
+} ;
+
 struct rpt_xlat
 {
 char	funccharseq[MAXXLAT];
@@ -419,9 +525,12 @@
 	char	mode;			/* 1 if in tx mode */
 	char	isremote;
 	char	phonemode;
+	char	phonevox;		/* vox the phone */
 	char	name[MAXNODESTR];	/* identifier (routing) string */
 	char	lasttx;
+	char	lasttx1;
 	char	lastrx;
+	char	lastrealrx;
 	char	lastrx1;
 	char	connected;
 	char	hasconnected;
@@ -446,8 +555,19 @@
 	long	linklisttimer;
 	int	dtmfed;
 	int linkunkeytocttimer;
+	struct timeval lastlinktv;
 	struct	ast_frame *lastf1,*lastf2;
 	struct	rpt_chan_stat chan_stat[NRPTSTAT];
+	struct vox vox;
+	char wasvox;
+	int voxtotimer;
+	char voxtostate;
+	char newkey;
+#ifdef OLD_ASTERISK
+        AST_LIST_HEAD(, ast_frame) rxq;
+#else
+	AST_LIST_HEAD_NOLOCK(, ast_frame) rxq;
+#endif
 } ;
 
 struct rpt_lstat
@@ -474,6 +594,7 @@
 	struct rpt_link mylink;
 	char param[TELEPARAMSIZE];
 	int	submode;
+	unsigned int parrot;
 	pthread_t threadid;
 } ;
 
@@ -510,17 +631,37 @@
 	char alternatetail;
 };
 
+/* rpt cmd support */
+#define CMD_DEPTH 1
+#define CMD_STATE_IDLE 0
+#define CMD_STATE_BUSY 1
+#define CMD_STATE_READY 2
+#define CMD_STATE_EXECUTING 3
+
+struct rpt_cmd_struct
+{
+    int state;
+    int functionNumber;
+    char param[MAXDTMF];
+    char digits[MAXDTMF];
+    int command_source;
+};
+
 static struct rpt
 {
 	ast_mutex_t lock;
 	ast_mutex_t remlock;
+	ast_mutex_t statpost_lock;
 	struct ast_config *cfg;
 	char reload;
+	char xlink;		 							// cross link state of a share repeater/remote radio
+	unsigned int statpost_seqno;
 
 	char *name;
 	char *rxchanname;
 	char *txchanname;
-	char *remote;
+	char remote;
+	char *remoterig;
 	struct	rpt_chan_stat chan_stat[NRPTSTAT];
 	unsigned int scram;
 
@@ -535,6 +676,7 @@
 		char *link_functions;
 		char *phone_functions;
 		char *dphone_functions;
+		char *alt_functions;
 		char *nodes;
 		char *extnodes;
 		char *extnodefile;
@@ -550,6 +692,7 @@
 		int tailmessagemax;
 		char	*memory;
 		char	*macro;
+		char	*tonemacro;
 		char	*startupmacro;
 		int iobase;
 		char *ioport;
@@ -575,10 +718,23 @@
 		int remotetimeoutwarningfreq;
 		int sysstate_cur;
 		struct sysstate s[MAX_SYSSTATES];
+		char parrotmode;
+		int parrottime;
+		char *rptnode;
+		char remote_mars;
+		int voxtimeout_ms;
+		int voxrecover_ms;
+		int simplexpatchdelay;
+		int simplexphonedelay;
+		char *statpost_program;
+		char *statpost_url;
 	} p;
 	struct rpt_link links;
 	int unkeytocttimer;
+	time_t lastkeyedtime;
+	time_t lasttxkeyedtime;
 	char keyed;
+	char txkeyed;
 	char exttx;
 	char localtx;
 	char remoterx;
@@ -592,13 +748,22 @@
 	char rem_dtmfbuf[MAXDTMF];
 	char lastdtmfcommand[MAXDTMF];
 	char cmdnode[50];
-	struct ast_channel *rxchannel,*txchannel, *monchannel;
+	char nowchan;						// channel now
+	char waschan;						// channel selected initially or by command
+	char bargechan;						// barge in channel
+	char macropatch;					// autopatch via tonemacro state
+	char parrotstate;
+	int  parrottimer;
+	unsigned int parrotcnt;
+	struct ast_channel *rxchannel,*txchannel, *monchannel, *parrotchannel;
 	struct ast_channel *pchannel,*txpchannel, *zaprxchannel, *zaptxchannel;
+	struct ast_channel *voxchannel;
 	struct ast_frame *lastf1,*lastf2;
 	struct rpt_tele tele;
 	struct timeval lasttv,curtv;
 	pthread_t rpt_call_thread,rpt_thread;
 	time_t dtmf_time,rem_dtmf_time,dtmf_time_rem;
+	int calldigittimer;
 	int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer,tmsgtimer,skedtimer;
 	int mustid,tailid;
 	int tailevent;
@@ -631,6 +796,7 @@
 	int patchdialtime;
 	int macro_longest;
 	int phone_longestfunc;
+	int alt_longestfunc;
 	int dphone_longestfunc;
 	int link_longestfunc;
 	int longestfunc;
@@ -643,13 +809,33 @@
 	char	lastnodewhichkeyedusup[MAXNODESTR];
 	int	dtmf_local_timer;
 	char	dtmf_local_str[100];
-	struct ast_filestream *monstream;
+	struct ast_filestream *monstream,*parrotstream;
 	char	loginuser[50];
 	char	loginlevel[10];
 	long	authtelltimer;
 	long	authtimer;
 	int iofd;
 	time_t start_time,last_activity_time;
+	char	lasttone[32];
+	struct rpt_tele *active_telem;
+	struct 	rpt_topkey topkey[TOPKEYN];
+	int topkeystate;
+	time_t topkeytime;
+	int topkeylong;
+	struct vox vox;
+	char wasvox;
+	int voxtotimer;
+	char voxtostate;
+	int linkposttimer;			
+	int keyposttimer;			
+	char newkey;
+	char inpadtest;
+#ifdef OLD_ASTERISK
+	AST_LIST_HEAD(, ast_frame) txq;
+#else
+	AST_LIST_HEAD_NOLOCK(, ast_frame) txq;
+#endif
+	char txrealkeyed;
 #ifdef	__RPT_NOTCH
 	struct rptfilter
 	{
@@ -670,6 +856,7 @@
 	mdc_decoder_t *mdc;
 	unsigned short lastunit;
 #endif
+	struct rpt_cmd_struct cmdAction;
 } rpt_vars[MAXRPTS];	
 
 struct nodelog {
@@ -685,6 +872,9 @@
 static int set_mode_ic706(struct rpt *myrpt, char newmode);
 static int simple_command_ft897(struct rpt *myrpt, char command);
 static int setrem(struct rpt *myrpt);
+static int setrtx_check(struct rpt *myrpt);
+static int channel_revert(struct rpt *myrpt);
+static int channel_steer(struct rpt *myrpt, char *data);
 
 AST_MUTEX_DEFINE_STATIC(nodeloglock);
 
@@ -879,12 +1069,95 @@
 
 static int multimode_capable(struct rpt *myrpt)
 {
-	if(!strcmp(myrpt->remote, remote_rig_ft897))
+	if(!strcmp(myrpt->remoterig, remote_rig_ft897))
 		return 1;
-	if(!strcmp(myrpt->remote, remote_rig_ic706))
+	if(!strcmp(myrpt->remoterig, remote_rig_ic706))
 		return 1;
 	return 0;
 }	
+
+static void voxinit_rpt(struct rpt *myrpt,char enable)
+{
+
+	myrpt->vox.speech_energy = 0.0;
+	myrpt->vox.noise_energy = 0.0;
+	myrpt->vox.enacount = 0;
+	myrpt->vox.voxena = 0;
+	if (!enable) myrpt->vox.voxena = -1;
+	myrpt->vox.lastvox = 0;
+	myrpt->vox.ondebcnt = VOX_ON_DEBOUNCE_COUNT;
+	myrpt->vox.offdebcnt = VOX_OFF_DEBOUNCE_COUNT;
+	myrpt->wasvox = 0;
+	myrpt->voxtotimer = 0;
+	myrpt->voxtostate = 0;
+}
+
+static void voxinit_link(struct rpt_link *mylink,char enable)
+{
+
+	mylink->vox.speech_energy = 0.0;
+	mylink->vox.noise_energy = 0.0;
+	mylink->vox.enacount = 0;
+	mylink->vox.voxena = 0;
+	if (!enable) mylink->vox.voxena = -1;
+	mylink->vox.lastvox = 0;
+	mylink->vox.ondebcnt = VOX_ON_DEBOUNCE_COUNT;
+	mylink->vox.offdebcnt = VOX_OFF_DEBOUNCE_COUNT;
+	mylink->wasvox = 0;
+	mylink->voxtotimer = 0;
+	mylink->voxtostate = 0;
+}
+
+static int dovox(struct vox *v,short *buf,int bs)
+{
+
+	int i;
+	float	esquare = 0.0;
+	float	energy = 0.0;
+	float	threshold = 0.0;
+	
+	if (v->voxena < 0) return(v->lastvox);
+	for(i = 0; i < bs; i++)
+	{
+		esquare += (float) buf[i] * (float) buf[i];
+	}
+	energy = sqrt(esquare);
+
+	if (energy >= v->speech_energy)
+		v->speech_energy += (energy - v->speech_energy) / 4;
+	else
+		v->speech_energy += (energy - v->speech_energy) / 64;
+
+	if (energy >= v->noise_energy)
+		v->noise_energy += (energy - v->noise_energy) / 64;
+	else
+		v->noise_energy += (energy - v->noise_energy) / 4;
+	
+	if (v->voxena) threshold = v->speech_energy / 8;
+	else
+	{
+		threshold = mymax(v->speech_energy / 16,v->noise_energy * 2);
+		threshold = mymin(threshold,VOX_MAX_THRESHOLD);
+	}
+	threshold = mymax(threshold,VOX_MIN_THRESHOLD);
+	if (energy > threshold)
+	{
+		if (v->voxena) v->noise_energy *= 0.75;
+		v->voxena = 1;
+	} else 	v->voxena = 0;
+	if (v->lastvox != v->voxena)
+	{
+		if (v->enacount++ >= ((v->lastvox) ? v->offdebcnt : v->ondebcnt))
+		{
+			v->lastvox = v->voxena;
+			v->enacount = 0;
+		}
+	} else v->enacount = 0;
+	return(v->lastvox);
+}
+
+
+
 
 /*
 * CLI extensions
@@ -896,9 +1169,12 @@
 static int rpt_do_stats(int fd, int argc, char *argv[]);
 static int rpt_do_lstats(int fd, int argc, char *argv[]);
 static int rpt_do_nodes(int fd, int argc, char *argv[]);
+static int rpt_do_local_nodes(int fd, int argc, char *argv[]);
 static int rpt_do_reload(int fd, int argc, char *argv[]);
 static int rpt_do_restart(int fd, int argc, char *argv[]);
 static int rpt_do_fun(int fd, int argc, char *argv[]);
+static int rpt_do_fun1(int fd, int argc, char *argv[]);
+static int rpt_do_cmd(int fd, int argc, char *argv[]);
 
 static char debug_usage[] =
 "Usage: rpt debug level {0-7}\n"
@@ -920,6 +1196,10 @@
 "Usage: rpt nodes <nodename>\n"
 "       Dumps a list of directly and indirectly connected nodes to the console\n";
 
+static char usage_local_nodes[] =
+"Usage: rpt localnodes\n"
+"       Dumps a list of the locally configured node numbers to the console.\n";
+
 static char reload_usage[] =
 "Usage: rpt reload\n"
 "       Reloads app_rpt running config parameters\n";
@@ -932,6 +1212,9 @@
 "Usage: rpt fun <nodename> <command>\n"
 "       Send a DTMF function to a node\n";
 
+static char cmd_usage[] =
+"Usage: rpt cmd <nodename> <cmd-name> <cmd-index> <cmd-args.\n"
+"       Send a command to a node.\n        i.e. rpt cmd 2000 ilink 3 2001\n";
 
 static struct ast_cli_entry  cli_debug =
         { { "rpt", "debug", "level" }, rpt_do_debug, 
@@ -949,6 +1232,10 @@
         { { "rpt", "nodes" }, rpt_do_nodes,
 		"Dump node list", dump_nodes };
 
+static struct ast_cli_entry  cli_local_nodes =
+        { { "rpt", "localnodes" }, rpt_do_local_nodes,
+		"Dump list of local node numbers", usage_local_nodes };
+
 static struct ast_cli_entry  cli_lstats =
         { { "rpt", "lstats" }, rpt_do_lstats,
 		"Dump link statistics", dump_lstats };
@@ -964,6 +1251,14 @@
 static struct ast_cli_entry  cli_fun =
         { { "rpt", "fun" }, rpt_do_fun,
 		"Execute a DTMF function", fun_usage };
+
+static struct ast_cli_entry  cli_fun1 =
+        { { "rpt", "fun1" }, rpt_do_fun1,
+		"Execute a DTMF function", fun_usage };
+
+static struct ast_cli_entry  cli_cmd =
+        { { "rpt", "cmd" }, rpt_do_cmd,
+		"Execute a DTMF function", cmd_usage };
 
 /*
 * Telemetry defaults
@@ -1008,6 +1303,7 @@
 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
 static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
 static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_playback(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
 /*
 * Function table
 */
@@ -1019,7 +1315,8 @@
 	{"ilink", function_ilink},
 	{"status", function_status},
 	{"remote", function_remote},
-	{"macro", function_macro}
+	{"macro", function_macro},
+	{"playback", function_playback}
 } ;
 
 static long diskavail(struct rpt *myrpt)
@@ -1034,6 +1331,182 @@
 		return(-1);
 	}
 	return(statfsbuf.f_bavail);
+}
+
+static void flush_telem(struct rpt *myrpt)
+{
+	struct rpt_tele *telem;
+	if(debug > 2)
+		ast_log(LOG_NOTICE, "flush_telem()!!");
+	rpt_mutex_lock(&myrpt->lock);
+	telem = myrpt->tele.next;
+	while(telem != &myrpt->tele)
+	{
+		if (telem->mode != SETREMOTE) ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV);
+		telem = telem->next;
+	}
+	rpt_mutex_unlock(&myrpt->lock);
+}
+/*
+	return via error priority
+*/
+static int priority_jump(struct rpt *myrpt, struct ast_channel *chan)
+{
+	int res=0;
+
+	// if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0){
+	if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0){
+		res = 0;
+	} else {
+		res = -1;
+	}
+	return res;
+}
+/*
+*/
+static int linkcount(struct rpt *myrpt)
+{
+	struct	rpt_link *l;
+	char *reverse_patch_state;
+ 	int numoflinks;
+
+	reverse_patch_state = "DOWN";
+	numoflinks = 0;
+	l = myrpt->links.next;
+	while(l && (l != &myrpt->links)){
+		if(numoflinks >= MAX_STAT_LINKS){
+			ast_log(LOG_WARNING,
+			"maximum number of links exceeds %d in rpt_do_stats()!",MAX_STAT_LINKS);
+			break;
+		}
+		//if (l->name[0] == '0'){ /* Skip '0' nodes */
+		//	reverse_patch_state = "UP";
+		//	l = l->next;
+		//	continue;
+		//}
+		numoflinks++;
+	 
+		l = l->next;
+	}
+	ast_log(LOG_NOTICE, "numoflinks=%i\n",numoflinks);
+	return numoflinks;
+}
+/*
+ * Retrieve a memory channel
+ * Return 0 if sucessful,
+ * -1 if channel not found,
+ *  1 if parse error
+ */
+static int retreive_memory(struct rpt *myrpt, char *memory)
+{
+	char tmp[30], *s, *s1, *val;
+
+	if (debug)ast_log(LOG_NOTICE, "memory=%s block=%s\n",memory,myrpt->p.memory);
+
+	val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.memory, memory);
+	if (!val){
+		return -1;
+	}			
+	strncpy(tmp,val,sizeof(tmp) - 1);
+	tmp[sizeof(tmp)-1] = 0;
+
+	s = strchr(tmp,',');
+	if (!s)
+		return 1; 
+	*s++ = 0;
+	s1 = strchr(s,',');
+	if (!s1)
+		return 1;
+	*s1++ = 0;
+	strncpy(myrpt->freq, tmp, sizeof(myrpt->freq) - 1);
+	strncpy(myrpt->rxpl, s, sizeof(myrpt->rxpl) - 1);
+	strncpy(myrpt->txpl, s, sizeof(myrpt->rxpl) - 1);
+	myrpt->remmode = REM_MODE_FM;
+	myrpt->offset = REM_SIMPLEX;
+	myrpt->powerlevel = REM_MEDPWR;
+	myrpt->txplon = myrpt->rxplon = 0;
+	while(*s1){
+		switch(*s1++){
+			case 'A':
+			case 'a':
+				strcpy(myrpt->rxpl, "100.0");
+				strcpy(myrpt->txpl, "100.0");
+				myrpt->remmode = REM_MODE_AM;	
+				break;
+			case 'B':
+			case 'b':
+				strcpy(myrpt->rxpl, "100.0");
+				strcpy(myrpt->txpl, "100.0");
+				myrpt->remmode = REM_MODE_LSB;
+				break;
+			case 'F':
+				myrpt->remmode = REM_MODE_FM;
+				break;
+			case 'L':
+			case 'l':
+				myrpt->powerlevel = REM_LOWPWR;
+				break;					
+			case 'H':
+			case 'h':
+				myrpt->powerlevel = REM_HIPWR;
+				break;
+					
+			case 'M':
+			case 'm':
+				myrpt->powerlevel = REM_MEDPWR;
+				break;
+						
+			case '-':
+				myrpt->offset = REM_MINUS;
+				break;
+						
+			case '+':
+				myrpt->offset = REM_PLUS;
+				break;
+						
+			case 'S':
+			case 's':
+				myrpt->offset = REM_SIMPLEX;
+				break;
+						
+			case 'T':
+			case 't':
+				myrpt->txplon = 1;
+				break;
+						
+			case 'R':
+			case 'r':
+				myrpt->rxplon = 1;
+				break;
+
+			case 'U':
+			case 'u':
+				strcpy(myrpt->rxpl, "100.0");
+				strcpy(myrpt->txpl, "100.0");
+				myrpt->remmode = REM_MODE_USB;
+				break;
+			default:
+				return 1;
+		}
+	}
+	return 0;
+}
+/*
+
+*/
+static void birdbath(struct rpt *myrpt)
+{
+	struct rpt_tele *telem;
+	if(debug > 2)
+		ast_log(LOG_NOTICE, "birdbath!!");
+	rpt_mutex_lock(&myrpt->lock);
+	telem = myrpt->tele.next;
+	while(telem != &myrpt->tele)
+	{
+		if (telem->mode == PARROT) ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV);
+		telem = telem->next;
+	}
+	rpt_mutex_unlock(&myrpt->lock);
 }
 
 static void do_dtmf_phone(struct rpt *myrpt, struct rpt_link *mylink, char c)
@@ -1068,7 +1541,7 @@
 char	datestr[100];
 
 	if (!myrpt->p.archivedir) return;
-	nodep = (struct nodelog *)malloc(sizeof(struct nodelog));
+	nodep = (struct nodelog *)ast_malloc(sizeof(struct nodelog));
 	if (nodep == NULL)
 	{
 		ast_log(LOG_ERROR,"Cannot get memory for node log");
@@ -1153,7 +1626,33 @@
 	}
 }
 
-static int openserial(char *fname)
+static int setdtr(int fd, int enable)
+{
+struct termios mode;
+
+	if (fd < 0) return -1;
+	if (tcgetattr(fd, &mode)) {
+		ast_log(LOG_WARNING, "Unable to get serial parameters for dtr: %s\n", strerror(errno));
+		return -1;
+	}
+	if (enable)
+	{
+		cfsetspeed(&mode, B9600);
+	}
+	else
+	{
+		cfsetspeed(&mode, B0);
+		usleep(100000);
+	}
+	if (tcsetattr(fd, TCSADRAIN, &mode)) {
+		ast_log(LOG_WARNING, "Unable to set serial parameters for dtr: %s\n", strerror(errno));
+		return -1;
+	}
+	if (enable) usleep(100000);
+	return 0;
+}
+
+static int openserial(struct rpt *myrpt,char *fname)
 {
 	struct termios mode;
 	int fd;
@@ -1169,23 +1668,25 @@
 		ast_log(LOG_WARNING, "Unable to get serial parameters on %s: %s\n", fname, strerror(errno));
 		return -1;
 	}
-#ifndef SOLARIS
+#ifndef	SOLARIS
 	cfmakeraw(&mode);
 #else
         mode.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
                         |INLCR|IGNCR|ICRNL|IXON);
-        mode.c_oflag &= ~OPOST;
         mode.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
         mode.c_cflag &= ~(CSIZE|PARENB|CRTSCTS);
         mode.c_cflag |= CS8;
-	mode.c_cc[TIME] = 3;
-	mode.c_cc[MAX] = 1;
+	mode.c_cc[VTIME] = 3;
+	mode.c_cc[VMIN] = 1; 
 #endif
 
 	cfsetispeed(&mode, B9600);
 	cfsetospeed(&mode, B9600);
 	if (tcsetattr(fd, TCSANOW, &mode)) 
 		ast_log(LOG_WARNING, "Unable to set serial parameters on %s: %s\n", fname, strerror(errno));
+	if(!strcmp(myrpt->remoterig, remote_rig_kenwood)) setdtr(fd,0); 
+	usleep(100000);
+	if (debug)ast_log(LOG_NOTICE,"Opened serial port %s\n",fname);
 	return(fd);	
 }
 
@@ -1343,7 +1844,24 @@
         return(i);
 
 }
-
+/*
+	send asterisk frame text message on the current tx channel
+*/
+static int send_usb_txt(struct rpt *myrpt, char *txt) 
+{
+	struct ast_frame wf;
+ 
+	if (debug)ast_log(LOG_NOTICE, "send_usb_txt %s\n",txt);
+	wf.frametype = AST_FRAME_TEXT;
+	wf.subclass = 0;
+	wf.offset = 0;
+	wf.mallocd = 0;
+	wf.datalen = strlen(txt) + 1;
+	wf.data = txt;
+	wf.samples = 0;
+	ast_write(myrpt->txchannel,&wf); 
+	return 0;
+}
 /* must be called locked */
 static void __mklinklist(struct rpt *myrpt, struct rpt_link *mylink, char *buf)
 {
@@ -1352,6 +1870,7 @@
 int	i,spos;
 
 	buf[0] = 0; /* clear output buffer */
+	if (myrpt->remote) return;
 	/* go thru all links */
 	for(l = myrpt->links.next; l != &myrpt->links; l = l->next)
 	{
@@ -1404,6 +1923,42 @@
 		if (l->name[0] == '0') continue;
 		l->linklisttimer = LINKLISTSHORTTIME;
 	}
+	myrpt->linkposttimer = LINKPOSTSHORTTIME;
+	return;
+}
+
+static void statpost(struct rpt *myrpt,char *pairs)
+{
+char *str,*astr;
+char *astrs[100];
+int	n,pid;
+time_t	now;
+unsigned int seq;
+
+	if (!myrpt->p.statpost_url) return;
+	str = ast_malloc(strlen(pairs) + strlen(myrpt->p.statpost_url) + 200);
+	astr = ast_strdup(myrpt->p.statpost_program);
+	if ((!str) || (!astr)) return;
+	n = finddelim(astr,astrs,100);
+	if (n < 1) return;
+	ast_mutex_lock(&myrpt->statpost_lock);
+	seq = ++myrpt->statpost_seqno;
+	ast_mutex_unlock(&myrpt->statpost_lock);
+	astrs[n++] = str;
+	astrs[n] = NULL;
+	time(&now);
+	sprintf(str,"%s?node=%s&time=%u&seqno=%u",myrpt->p.statpost_url,
+		myrpt->name,(unsigned int) now,seq);
+	if (pairs) sprintf(str + strlen(str),"&%s",pairs);
+	if (!(pid = fork()))
+	{
+		execv(astrs[0],astrs);
+		ast_log(LOG_ERROR, "exec of %s failed.\n", astrs[0]);
+		perror("asterisk");
+		exit(0);
+	}
+	ast_free(astr);
+	ast_free(str);
 	return;
 }
 
@@ -1490,7 +2045,7 @@
 			return i + 1; 
 		}
 	}
-	param = NULL;
+	*param = NULL;
 	return 0;
 }
 
@@ -1536,6 +2091,14 @@
 	if ((**x < '0') || (**x > '9')) xoff = 1; else xoff = 0;
 	if ((**y < '0') || (**y > '9')) yoff = 1; else yoff = 0;
 	return(strcmp((*x) + xoff,(*y) + yoff));
+}
+
+static int topcompar(const void *a, const void *b)
+{
+struct rpt_topkey *x = (struct rpt_topkey *) a;
+struct rpt_topkey *y = (struct rpt_topkey *) b;
+
+	return(x->timesince - y->timesince);
 }
 
 #ifdef	__RPT_NOTCH
@@ -1646,8 +2209,12 @@
  	memset(&rpt_vars[n].p,0,sizeof(rpt_vars[n].p));
 	if (init)
 	{
-		/* clear all the fields in the structure after 'p' */
-		memset(&rpt_vars[n].p + sizeof(rpt_vars[0].p), 0, sizeof(rpt_vars[0]) - sizeof(rpt_vars[0].p) - offsetof(typeof(rpt_vars[0]), p));
+		char *cp;
+		int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
+
+		cp = (char *) &rpt_vars[n].p;
+		memset(cp + sizeof(rpt_vars[n].p),0,
+			sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
 		rpt_vars[n].tele.next = &rpt_vars[n].tele;
 		rpt_vars[n].tele.prev = &rpt_vars[n].tele;
 		rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
@@ -1675,6 +2242,23 @@
 	val = (char *) ast_variable_retrieve(cfg,this,"totime");
 	if (val) rpt_vars[n].p.totime = atoi(val);
 		else rpt_vars[n].p.totime = TOTIME;
+	val = (char *) ast_variable_retrieve(cfg,this,"voxtimeout");
+	if (val) rpt_vars[n].p.voxtimeout_ms = atoi(val);
+		else rpt_vars[n].p.voxtimeout_ms = VOX_TIMEOUT_MS;
+	val = (char *) ast_variable_retrieve(cfg,this,"voxrecover");
+	if (val) rpt_vars[n].p.voxrecover_ms = atoi(val);
+		else rpt_vars[n].p.voxrecover_ms = VOX_RECOVER_MS;
+	val = (char *) ast_variable_retrieve(cfg,this,"simplexpatchdelay");
+	if (val) rpt_vars[n].p.simplexpatchdelay = atoi(val);
+		else rpt_vars[n].p.simplexpatchdelay = SIMPLEX_PATCH_DELAY;
+	val = (char *) ast_variable_retrieve(cfg,this,"simplexphonedelay");
+	if (val) rpt_vars[n].p.simplexphonedelay = atoi(val);
+		else rpt_vars[n].p.simplexphonedelay = SIMPLEX_PHONE_DELAY;
+	val = (char *) ast_variable_retrieve(cfg,this,"statpost_program");
+	if (val) rpt_vars[n].p.statpost_program = val;
+		else rpt_vars[n].p.statpost_program = STATPOST_PROGRAM;
+	rpt_vars[n].p.statpost_url = 
+		(char *) ast_variable_retrieve(cfg,this,"statpost_url");
 	rpt_vars[n].p.tailmessagetime = retrieve_astcfgint(&rpt_vars[n],this, "tailmessagetime", 0, 2400000, 0);		
 	rpt_vars[n].p.tailsquashedtime = retrieve_astcfgint(&rpt_vars[n],this, "tailsquashedtime", 0, 2400000, 0);		
 	rpt_vars[n].p.duplex = retrieve_astcfgint(&rpt_vars[n],this,"duplex",0,4,2);
@@ -1692,6 +2276,9 @@
 	val = (char *) ast_variable_retrieve(cfg,this,"macro");
 	if (!val) val = MACRO;
 	rpt_vars[n].p.macro = val;
+	val = (char *) ast_variable_retrieve(cfg,this,"tonemacro");
+	if (!val) val = TONEMACRO;
+	rpt_vars[n].p.tonemacro = val;
 	val = (char *) ast_variable_retrieve(cfg,this,"startup_macro");
 	if (val) rpt_vars[n].p.startupmacro = val;
 	val = (char *) ast_variable_retrieve(cfg,this,"iobase");
@@ -1699,7 +2286,7 @@
 		the input specified in hex or decimal so we use
 		sscanf with a %i */
 	if ((!val) || (sscanf(val,"%i",&rpt_vars[n].p.iobase) != 1))
-	rpt_vars[n].p.iobase = DEFAULT_IOBASE;
+		rpt_vars[n].p.iobase = DEFAULT_IOBASE;
 	val = (char *) ast_variable_retrieve(cfg,this,"ioport");
 	rpt_vars[n].p.ioport = val;
 	val = (char *) ast_variable_retrieve(cfg,this,"functions");
@@ -1717,6 +2304,8 @@
 	if (val) rpt_vars[n].p.phone_functions = val;
 	val = (char *) ast_variable_retrieve(cfg,this,"dphone_functions");
 	if (val) rpt_vars[n].p.dphone_functions = val;
+	val = (char *) ast_variable_retrieve(cfg,this,"alt_functions");
+	if (val) rpt_vars[n].p.alt_functions = val;
 	val = (char *) ast_variable_retrieve(cfg,this,"funcchar");
 	if (!val) rpt_vars[n].p.funcchar = FUNCCHAR; else 
 		rpt_vars[n].p.funcchar = *val;		
@@ -1747,6 +2336,17 @@
 	val = (char *) ast_variable_retrieve(cfg,this,"authlevel");
 	if (val) rpt_vars[n].p.authlevel = atoi(val); 
 	else rpt_vars[n].p.authlevel = 0;
+	val = (char *) ast_variable_retrieve(cfg,this,"parrot");
+	if (val) rpt_vars[n].p.parrotmode = ast_true(val) * 2;
+	else rpt_vars[n].p.parrotmode = 0;
+	val = (char *) ast_variable_retrieve(cfg,this,"parrottime");
+	if (val) rpt_vars[n].p.parrottime = atoi(val); 
+	else rpt_vars[n].p.parrottime = PARROTTIME;
+	val = (char *) ast_variable_retrieve(cfg,this,"rptnode");
+	rpt_vars[n].p.rptnode = val;
+	val = (char *) ast_variable_retrieve(cfg,this,"mars");
+	if (val) rpt_vars[n].p.remote_mars = atoi(val); 
+	else rpt_vars[n].p.remote_mars = 0;
 	val = (char *) ast_variable_retrieve(cfg,this,"monminblocks");
 	if (val) rpt_vars[n].p.monminblocks = atol(val); 
 	else rpt_vars[n].p.monminblocks = DEFAULT_MONITOR_MIN_DISK_BLOCKS;
@@ -1865,6 +2465,17 @@
 			j = strlen(vp->name);
 			if (j > rpt_vars[n].dphone_longestfunc)
 				rpt_vars[n].dphone_longestfunc = j;
+			vp = vp->next;
+		}
+	}
+	rpt_vars[n].alt_longestfunc = 0;
+	if (rpt_vars[n].p.alt_functions)
+	{
+		vp = ast_variable_browse(cfg, rpt_vars[n].p.alt_functions);
+		while(vp){
+			j = strlen(vp->name);
+			if (j > rpt_vars[n].alt_longestfunc)
+				rpt_vars[n].alt_longestfunc = j;
 			vp = vp->next;
 		}
 	}
@@ -2012,14 +2623,16 @@
 
 static int rpt_do_stats(int fd, int argc, char *argv[])
 {
-	int i,j;
+	int i,j,numoflinks;
 	int dailytxtime, dailykerchunks;
+	time_t now;
 	int totalkerchunks, dailykeyups, totalkeyups, timeouts;
 	int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
+	int uptime;
 	long long totaltxtime;
 	struct	rpt_link *l;
 	char *listoflinks[MAX_STAT_LINKS];	
-	char *lastnodewhichkeyedusup, *lastdtmfcommand;
+	char *lastdtmfcommand,*parrot_ena;
 	char *tot_state, *ider_state, *patch_state;
 	char *reverse_patch_state, *sys_ena, *tot_ena, *link_ena, *patch_ena;
 	char *sch_ena, *input_signal, *called_number, *user_funs, *tail_type;
@@ -2030,21 +2643,19 @@
 	if(argc != 3)
 		return RESULT_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;
-
+	input_signal = not_applicable;
+	called_number = lastdtmfcommand = NULL;
+
+	time(&now);
 	for(i = 0; i < nrpts; i++)
 	{
 		if (!strcmp(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 */
-
+			uptime = (int)(now - starttime);
 			dailytxtime = myrpt->dailytxtime;
 			totaltxtime = myrpt->totaltxtime;
 			dailykeyups = myrpt->dailykeyups;
@@ -2057,28 +2668,38 @@
 
 			/* Traverse the list of connected nodes */
 			reverse_patch_state = "DOWN";
-			j = 0;
+			numoflinks = 0;
 			l = myrpt->links.next;
 			while(l && (l != &myrpt->links)){
+				if(numoflinks >= MAX_STAT_LINKS){
+					ast_log(LOG_NOTICE,
+					"maximum number of links exceeds %d in rpt_do_stats()!",MAX_STAT_LINKS);
+					break;
+				}
 				if (l->name[0] == '0'){ /* Skip '0' nodes */
 					reverse_patch_state = "UP";
 					l = l->next;
 					continue;
 				}
-				listoflinks[j] = ast_strdupa(l->name);
-				if(listoflinks[j])
-					j++;
+				listoflinks[numoflinks] = ast_strdup(l->name);
+				if(listoflinks[numoflinks] == NULL){
+					break;
+				}
+				else{
+					numoflinks++;
+				}
 				l = l->next;
 			}
-
-			lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup);			
-			if((!lastnodewhichkeyedusup) || (!strlen(lastnodewhichkeyedusup)))
-				lastnodewhichkeyedusup = not_applicable;
 
 			if(myrpt->keyed)
 				input_signal = "YES";
 			else
 				input_signal = "NO";
+
+			if(myrpt->p.parrotmode)
+				parrot_ena = "ENABLED";
+			else
+				parrot_ena = "DISABLED";
 
 			if(myrpt->p.s[myrpt->p.sysstate_cur].txdisable)
 				sys_ena = "DISABLED";
@@ -2149,23 +2770,19 @@
 			}
 
 			if(strlen(myrpt->exten)){
-				called_number = ast_strdupa(myrpt->exten);
-				if(!called_number)
-					called_number = not_applicable;
+				called_number = ast_strdup(myrpt->exten);
 			}
 
 			if(strlen(myrpt->lastdtmfcommand)){
-				lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand);
-				if(!lastdtmfcommand)
-					lastdtmfcommand = not_applicable;
-			}
-
+				lastdtmfcommand = ast_strdup(myrpt->lastdtmfcommand);
+			}
 			rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
 
 			ast_cli(fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
 			ast_cli(fd, "Selected system state............................: %d\n", myrpt->p.sysstate_cur);
 			ast_cli(fd, "Signal on input..................................: %s\n", input_signal);
 			ast_cli(fd, "System...........................................: %s\n", sys_ena);
+			ast_cli(fd, "Parrot Mode......................................: %s\n", parrot_ena);
 			ast_cli(fd, "Scheduler........................................: %s\n", sch_ena);
 			ast_cli(fd, "Tail Time........................................: %s\n", tail_type);
 			ast_cli(fd, "Time out timer...................................: %s\n", tot_ena);
@@ -2178,7 +2795,8 @@
 			ast_cli(fd, "Keyups since system initialization...............: %d\n", totalkeyups);
 			ast_cli(fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
 			ast_cli(fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
-			ast_cli(fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand);
+			ast_cli(fd, "Last DTMF command executed.......................: %s\n", 
+			(lastdtmfcommand && strlen(lastdtmfcommand)) ? lastdtmfcommand : not_applicable);
 			hours = dailytxtime/3600000;
 			dailytxtime %= 3600000;
 			minutes = dailytxtime/60000;
@@ -2186,7 +2804,7 @@
 			seconds = dailytxtime/1000;
 			dailytxtime %= 1000;
 
-			ast_cli(fd, "TX time today ...................................: %02d:%02d:%02d.%d\n",
+			ast_cli(fd, "TX time today....................................: %02d:%02d:%02d.%d\n",
 				hours, minutes, seconds, dailytxtime);
 
 			hours = (int) totaltxtime/3600000;
@@ -2198,33 +2816,51 @@
 
 			ast_cli(fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
 				 hours, minutes, seconds, (int) totaltxtime);
+
+                       	hours = uptime/3600;
+                        uptime %= 3600;
+                        minutes = uptime/60;
+                        uptime %= 60;
+
+                        ast_cli(fd, "Uptime...........................................: %02d:%02d:%02d\n",
+                                hours, minutes, uptime);
+
 			ast_cli(fd, "Nodes currently connected to us..................: ");
-			for(j = 0 ;; j++){
-				if(!listoflinks[j]){
-					if(!j){
-						ast_cli(fd,"<NONE>");
+                        if(!numoflinks){
+  	                      ast_cli(fd,"<NONE>");
+                        }
+			else{
+				for(j = 0 ;j < numoflinks; j++){
+					ast_cli(fd, "%s", listoflinks[j]);
+					if(j % 4 == 3){
+						ast_cli(fd, "\n");
+						ast_cli(fd, "                                                 : ");
+					}	
+					else{
+						if((numoflinks - 1) - j  > 0)
+							ast_cli(fd, ", ");
 					}
-					break;
 				}
-				ast_cli(fd, "%s", listoflinks[j]);
-				if(j % 4 == 3){
-					ast_cli(fd, "\n");
-					ast_cli(fd, "                                                 : ");
-				}
-				else{
-					if(listoflinks[j + 1])
-						ast_cli(fd, ", ");
-				}
 			}
 			ast_cli(fd,"\n");
 
-			ast_cli(fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup);
 			ast_cli(fd, "Autopatch........................................: %s\n", patch_ena);
 			ast_cli(fd, "Autopatch state..................................: %s\n", patch_state);
-			ast_cli(fd, "Autopatch called number..........................: %s\n", called_number);
+			ast_cli(fd, "Autopatch called number..........................: %s\n",
+			(called_number && strlen(called_number)) ? called_number : not_applicable);
 			ast_cli(fd, "Reverse patch/IAXRPT connected...................: %s\n", reverse_patch_state);
 			ast_cli(fd, "User linking commands............................: %s\n", link_ena);
 			ast_cli(fd, "User functions...................................: %s\n\n", user_funs);
+
+			for(j = 0; j < numoflinks; j++){ /* ast_free() all link names */
+				ast_free(listoflinks[j]);
+			}
+			if(called_number){
+				ast_free(called_number);
+			}
+			if(lastdtmfcommand){
+				ast_free(lastdtmfcommand);
+			}
 		        return RESULT_SUCCESS;
 		}
 	}
@@ -2264,7 +2900,7 @@
 					l = l->next;
 					continue;
 				}
-				if((s = (struct rpt_lstat *) malloc(sizeof(struct rpt_lstat))) == NULL){

[... 5114 lines stripped ...]



More information about the asterisk-commits mailing list