Skip to content
Snippets Groups Projects
app_rpt.c 199 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
    
     * Copyright (C) 2002-2005, Jim Dixon, WB6NIL
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Jim Dixon, WB6NIL <jim@lambdatel.com>
    
     * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * 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.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 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.
     */
    
    
     * \brief Radio Repeater / Remote Base program 
    
     * \author Jim Dixon, WB6NIL <jim@lambdatel.com>
     *
     * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
    
     * 
     * See http://www.zapatatelephony.org/app_rpt.html
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Repeater / Remote Functions:
     * "Simple" Mode:  * - autopatch access, # - autopatch hangup
     * Normal mode:
    
     * See the function list in rpt.conf (autopatchup, autopatchdn)
     * autopatchup can optionally take comma delimited setting=value pairs:
     *  
     *
     * context=string		:	Override default context with "string"
     * dialtime=ms			:	Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second)
     * farenddisconnect=1		:	Automatically disconnect when called party hangs up
     * noct=1			:	Don't send repeater courtesy tone during autopatch calls
     * quiet=1			:	Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up
     *
     *
     * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1
    
     *
     *  To send an asterisk (*) while dialing or talking on phone,
     *  use the autopatch acess code.
    
     * status cmds:
     *
     *  1 - Force ID
     *  2 - Give Time of Day
     *  3 - Give software Version
     *
     * cop (control operator) cmds:
     *
     *  1 - System warm boot
     *  2 - System enable
     *  3 - System disable
    
     *  4 - Test Tone On
     *  5 - Dump System Variables on Console (debug)
     *  6 - PTT (phone mode only)
    
     *
     * ilink cmds:
     *
     *  1 - Disconnect specified link
     *  2 - Connect specified link -- monitor only
     *  3 - Connect specified link -- tranceive
     *  4 - Enter command mode on specified link
     *  5 - System status
     *  6 - Disconnect all links
     *
     * remote cmds:
     *
    
     *  1 - Recall Memory MM  (*000-*099) (Gets memory from rpt.conf)
     *  2 - Set VFO MMMMM*KKK*O   (Mhz digits, Khz digits, Offset)
     *  3 - Set Rx PL Tone HHH*D*
     *  4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
     *  5 - Link Status (long)
     *  6 - Set operating mode M (FM, USB, LSB, AM, etc)
    
     *  100 - RX PL off (Default)
     *  101 - RX PL On
     *  102 - TX PL Off (Default)
     *  103 - TX PL On
     *  104 - Low Power
     *  105 - Med Power
     *  106 - Hi Power
    
     *  107 - Bump Down 20 Hz
     *  108 - Bump Down 100 Hz
     *  109 - Bump Down 500 Hz
     *  110 - Bump Up 20 Hz
     *  111 - Bump Up 100 Hz
     *  112 - Bump Up 500 Hz
     *  113 - Scan Down Slow
     *  114 - Scan Down Medium
     *  115 - Scan Down Fast
     *  116 - Scan Up Slow
     *  117 - Scan Up Medium
     *  118 - Scan Up Fast
     *  119 - Transmit allowing auto-tune
     *  140 - Link Status (brief)
    
     *
     * 'duplex' modes:  (defaults to duplex=2)
     *
     * 0 - Only remote links key Tx and no main repeat audio.
     * 1 - Everything other then main Rx keys Tx, no main repeat audio.
     * 2 - Normal mode
     * 3 - Normal except no main repeat audio.
     * 4 - Normal except no main repeat audio during autopatch only
     *
    
    /* Un-comment the following to include support for MDC-1200 digital tone
       signalling protocol (using KA6SQG's GPL'ed implementation) */
    /* #include "mdc_decode.c" */
    
    /* Un-comment the following to include support for notch filters in the
       rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */
    /* #include "rpt_notch.c" */
    
    /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
    
    
    #define	DISC_TIME 10000  /* report disc after 10 seconds of no connect */
    #define	MAX_RETRIES 5
    
    
    Jim Dixon's avatar
    Jim Dixon committed
    #define	REDUNDANT_TX_TIME 2000
    
    
    #define	RETRY_TIMER_MS 5000
    
    #define	FUNCTIONS "functions"
    #define TELEMETRY "telemetry"
    #define MORSE "morse"
    #define	FUNCCHAR '*'
    #define	ENDCHAR '#'
    
    enum {REM_OFF, REM_MONITOR, REM_TX};
    
    enum {ID, PROC, TERM, COMPLETE, UNKEY, REMDISC, REMALREADY, REMNOTFOUND, REMGO,
    	CONNECTED, CONNFAIL, STATUS, TIMEOUT, ID1, STATS_TIME,
    
    	STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
    
    	TAILMSG, MACRO_NOTFOUND, GOSUB_NOTFOUND, MACRO_BUSY, GOSUB_BUSY, LASTNODEKEY};
    
    enum {REM_SIMPLEX, REM_MINUS, REM_PLUS};
    
    enum {REM_LOWPWR, REM_MEDPWR, REM_HIPWR};
    
    enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
    
    enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
    
    enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
    
    
    enum {REM_MODE_FM, REM_MODE_USB, REM_MODE_LSB, REM_MODE_AM};
    
    enum {HF_SCAN_OFF, HF_SCAN_DOWN_SLOW, HF_SCAN_DOWN_QUICK,
          HF_SCAN_DOWN_FAST, HF_SCAN_UP_SLOW, HF_SCAN_UP_QUICK, HF_SCAN_UP_FAST};
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    Jim Dixon's avatar
    Jim Dixon committed
    #include <signal.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/stat.h>
    #include <dirent.h>
    #include <ctype.h>
    #include <sys/time.h>
    #include <sys/file.h>
    #include <sys/ioctl.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <math.h>
    
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include "asterisk/utils.h"
    #include "asterisk/lock.h"
    #include "asterisk/file.h"
    #include "asterisk/channel.h"
    #include "asterisk/callerid.h"
    #include "asterisk/pbx.h"
    #include "asterisk/module.h"
    #include "asterisk/translate.h"
    
    #include "asterisk/cli.h"
    #include "asterisk/config.h"
    #include "asterisk/say.h"
    #include "asterisk/localtime.h"
    
    #include "asterisk/zapata.h"
    
    
    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"
    
    "\n"
    "    Not specifying an option puts it in normal endpoint mode (where source\n"
    "    IP and nodename are verified).\n"
    "\n"
    "    Options are as follows:\n"
    "\n"
    "        X - Normal endpoint mode WITHOUT security check. Only specify\n"
    "            this if you have checked security already (like with an IAX2\n"
    "            user/password or something).\n"
    "\n"
    
    "        Rannounce-string[,timeout[,timeout-destination]] - Amateur Radio\n"
    
    "            Reverse Autopatch. Caller is put on hold, and announcement (as\n"
    "            specified by the 'announce-string') is played on radio system.\n"
    "            Users of radio system can access autopatch, dial specified\n"
    "            code, and pick up call. Announce-string is list of names of\n"
    "            recordings, or \"PARKED\" to substitute code for un-parking,\n"
    "            or \"NODE\" to substitute node number.\n"
    "\n"
    "        P - Phone Control mode. This allows a regular phone user to have\n"
    "            full control and audio access to the radio system. For the\n"
    "            user to have DTMF control, the 'phone_functions' parameter\n"
    "            must be specified for the node in 'rpt.conf'. An additional\n"
    "            function (cop,6) must be listed so that PTT control is available.\n"
    "\n"
    "        D - Dumb Phone Control mode. This allows a regular phone user to\n"
    "            have full control and audio access to the radio system. In this\n"
    "            mode, the PTT is activated for the entire length of the call.\n"
    "            For the user to have DTMF control (not generally recomended in\n"
    "            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";
    
    static unsigned int vmajor = 0;
    static unsigned int vminor = 47;
    
    
    static int debug = 0;  /* FIXME Set this >0 for extra debug output */
    
    char *discstr = "!!DISCONNECT!!";
    
    static char *remote_rig_ft897 = "ft897";
    static char *remote_rig_rbi = "rbi";
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define	MSWAIT 200
    #define	HANGTIME 5000
    #define	TOTIME 180000
    #define	IDTIME 300000
    #define	MAXRPTS 20
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static  pthread_t rpt_master_thread;
    
    
    struct rpt;
    
    struct rpt_link
    {
    	struct rpt_link *next;
    	struct rpt_link *prev;
    	char	mode;			/* 1 if in tx mode */
    	char	isremote;
    
    	char	name[MAXNODESTR];	/* identifier (routing) string */
    	char	lasttx;
    	char	lastrx;
    	char	connected;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	char	hasconnected;
    
    	char	disced;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	char	killme;
    
    	long	elaptime;
    	long	disctime;
    	long 	retrytimer;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	long	retxtimer;
    
    	int	retries;
    
    	struct ast_channel *chan;	
    	struct ast_channel *pchan;	
    } ;
    
    
    struct rpt_lstat
    {
    	struct	rpt_lstat *next;
    	struct	rpt_lstat *prev;
    	char	peer[MAXPEERSTR];
    	char	name[MAXNODESTR];
    	char	mode;
    	char	outbound;
    	char	reconnects;
    	long long	connecttime;
    } ;
    
    
    struct rpt_tele
    {
    	struct rpt_tele *next;
    	struct rpt_tele *prev;
    	struct rpt *rpt;
    
    struct function_table_tag
    {
    	char action[ACTIONSIZE];
    
    	int (*function)(struct rpt *myrpt, char *param, char *digitbuf, 
    		int command_source, struct rpt_link *mylink);
    
    } ;
    
    /* Used to store the morse code patterns */
    
    struct morse_bits
    {		  
    	int len;
    	int ddcomb;
    } ;
    
    struct telem_defaults
    {
    	char name[20];
    	char value[80];
    } ;
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct rpt
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *rxchanname;
    	char *txchanname;
    
    		char ourcontext[80];
    		char ourcallerid[80];
    		char acctcode[21];
    		char ident[80];
    		char tonezone[80];
    
    		char functions[80];
    		char link_functions[80];
    		char phone_functions[80];
    		char dphone_functions[80];
    		char nodes[80];
    
    		int hangtime;
    		int totime;
    		int idtime;
    		int tailmessagetime;
    		int tailsquashedtime;
    		int duplex;
    		int politeid;
    
    		char *tailmsgbuf;
    		AST_DECLARE_APP_ARGS(tailmsg,
    			AST_APP_ARG(msgs)[100];
    		);
    		char memory[80];
    		char macro[80];
    
    	int unkeytocttimer;
    	char keyed;
    
    	char tounkeyed;
    	char tonotify;
    	char enable;
    
    	struct ast_channel *rxchannel, *txchannel;
    	struct ast_channel *pchannel, *txpchannel, *remchannel;
    
    	struct timeval lasttv, curtv;
    	pthread_t rpt_call_thread, rpt_thread;
    	time_t dtmf_time, rem_dtmf_time, dtmf_time_rem;
    	int tailtimer, totimer, idtimer, txconf, conf, callmode, cidx, scantimer, tmsgtimer, skedtimer;
    	int mustid, tailid;
    
    	int dtmfidx, rem_dtmfidx;
    	int dailytxtime, dailykerchunks, totalkerchunks, dailykeyups, totalkeyups, timeouts;
    
    	int totalexecdcommands, dailyexecdcommands;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char exten[AST_MAX_EXTENSION];
    
    	char freq[MAXREMSTR], rxpl[MAXREMSTR], txpl[MAXREMSTR];
    
    	char offset;
    	char powerlevel;
    	char txplon;
    	char rxplon;
    
    	char remmode;
    	char tunerequest;
    	char hfscanmode;
    	int hfscanstatus;
    	char lastlinknode[MAXNODESTR];
    
    Jim Dixon's avatar
    Jim Dixon committed
    	char stopgen;
    
    	char patchfarenddisconnect;
    	char patchnoct;
    	char patchquiet;
    	char patchcontext[MAXPATCHCONTEXT];
    	int patchdialtime;
    
    	int phone_longestfunc;
    	int dphone_longestfunc;
    
    	int link_longestfunc;
    	int longestfunc;
    
    	int longestnode;
    	int threadrestarts;		
    
    	time_t disgorgetime;
    	time_t lastthreadrestarttime;
    
    	char lastnodewhichkeyedusup[MAXNODESTR];
    
    		char desc[100];
    		float x0;
    		float x1;
    		float x2;
    		float y0;
    		float y1;
    		float y2;
    		float gain;
    		float const0;
    		float const1;
    		float const2;
    
    	} filters[MAXFILTERS];
    #endif
    #ifdef	_MDC_DECODE_H_
    	mdc_decoder_t *mdc;
    	unsigned short lastunit;
    #endif
    
    
    #ifdef	APP_RPT_LOCK_DEBUG
    
    #warning COMPILING WITH LOCK-DEBUGGING ENABLED!!
    
    #define	MAXLOCKTHREAD 100
    
    
    #define	rpt_mutex_lock(x)	_rpt_mutex_lock(x, myrpt, __LINE__)
    #define	rpt_mutex_unlock(x)	_rpt_mutex_unlock(x, myrpt, __LINE__)
    
    
    struct lockthread
    {
    	pthread_t id;
    	int lockcount;
    	int lastlock;
    	int lastunlock;
    } lockthreads[MAXLOCKTHREAD];
    
    
    struct by_lightning
    {
    	int line;
    	struct timeval tv;
    	struct rpt *rpt;
    	struct lockthread lockthread;
    } lock_ring[32];
    
    
    int lock_ring_index = 0;
    
    AST_MUTEX_DEFINE_STATIC(locklock);
    
    static struct lockthread *get_lockthread(pthread_t id)
    {
    
    	for (i = 0; i < MAXLOCKTHREAD; i++) {
    		if (lockthreads[i].id == id)
    			return(&lockthreads[i]);
    
    }
    
    static struct lockthread *put_lockthread(pthread_t id)
    {
    
    	for (i = 0; i < MAXLOCKTHREAD; i++) {
    
    		if (lockthreads[i].id == id)
    			return(&lockthreads[i]);
    	}
    
    	for (i = 0; i < MAXLOCKTHREAD; i++) {
    		if (!lockthreads[i].id) {
    
    			lockthreads[i].lockcount = 0;
    			lockthreads[i].lastlock = 0;
    			lockthreads[i].lastunlock = 0;
    			lockthreads[i].id = id;
    
    }
    
    
    static void rpt_mutex_spew(void)
    {
    	struct by_lightning lock_ring_copy[32];
    	int lock_ring_index_copy;
    
    	struct timeval lasttv;
    
    	ast_mutex_lock(&locklock);
    	memcpy(&lock_ring_copy, &lock_ring, sizeof(lock_ring_copy));
    	lock_ring_index_copy = lock_ring_index;
    	ast_mutex_unlock(&locklock);
    
    	lasttv.tv_sec = lasttv.tv_usec = 0;
    
    		j = (i + lock_ring_index_copy) % 32;
    
    		ast_strftime(a, sizeof(a) - 1, "%m/%d/%Y %H:%M:%S",
    			ast_localtime(&lock_ring_copy[j].tv, &tm, NULL));
    
    		if (lasttv.tv_sec) {
    			diff = (lock_ring_copy[j].tv.tv_sec - lasttv.tv_sec) * 1000000;
    
    			diff += (lock_ring_copy[j].tv.tv_usec - lasttv.tv_usec);
    		}
    		lasttv.tv_sec = lock_ring_copy[j].tv.tv_sec;
    		lasttv.tv_usec = lock_ring_copy[j].tv.tv_usec;
    
    		if (!lock_ring_copy[j].tv.tv_sec)
    			continue;
    		if (lock_ring_copy[j].line < 0) {
    			ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] UNLOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
    				i - 31, -lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
    				(int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
    		} else {
    			ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] LOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
    				i - 31, lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
    				(int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
    
    		}
    	}
    }
    
    
    static void _rpt_mutex_lock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
    {
    
    
    	id = pthread_self();
    	ast_mutex_lock(&locklock);
    	t = put_lockthread(id);
    
    		int lastline = t->lastlock;
    		ast_mutex_unlock(&locklock);
    
    		ast_log(LOG_NOTICE, "rpt_mutex_lock: Double lock request line %d node %s pid %x, last lock was line %d\n",
    				line, myrpt->name, (int) t->id, lastline);
    
    		rpt_mutex_spew();
    		return;
    	}
    	t->lastlock = line;
    	t->lockcount = 1;
    	gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
    	lock_ring[lock_ring_index].rpt = myrpt;
    
    	memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
    
    	lock_ring[lock_ring_index++].line = line;
    
    		lock_ring_index = 0;
    	ast_mutex_unlock(&locklock);
    	ast_mutex_lock(lockp);
    }
    
    
    static void _rpt_mutex_unlock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
    {
    
    
    	id = pthread_self();
    	ast_mutex_lock(&locklock);
    	t = put_lockthread(id);
    
    		int lastline = t->lastunlock;
    		ast_mutex_unlock(&locklock);
    
    		ast_log(LOG_NOTICE, "rpt_mutex_lock: Double un-lock request line %d node %s pid %x, last un-lock was line %d\n",
    				line, myrpt->name, (int) t->id, lastline);
    
    		rpt_mutex_spew();
    		return;
    	}
    	t->lastunlock = line;
    	t->lockcount = 0;
    	gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
    	lock_ring[lock_ring_index].rpt = myrpt;
    
    	memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
    
    	lock_ring[lock_ring_index++].line = -line;
    
    		lock_ring_index = 0;
    	ast_mutex_unlock(&locklock);
    	ast_mutex_unlock(lockp);
    }
    
    #else  /* APP_RPT_LOCK_DEBUG */
    
    #define rpt_mutex_lock(x) ast_mutex_lock(x)
    #define rpt_mutex_unlock(x) ast_mutex_unlock(x)
    
    #endif  /* APP_RPT_LOCK_DEBUG */
    
    
    /*
    * CLI extensions
    */
    
    /* Debug mode */
    
    static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static struct ast_cli_entry cli_rpt[] = {
    
    	AST_CLI_DEFINE(handle_cli_rpt_debug_level, "Enable app_rpt debuggin"),
    	AST_CLI_DEFINE(handle_cli_rpt_dump,        "Dump app_rpt structs for debugging"),
    	AST_CLI_DEFINE(handle_cli_rpt_stats,       "Dump node statistics"),
    	AST_CLI_DEFINE(handle_cli_rpt_lstats,      "Dump link statistics"),
    	AST_CLI_DEFINE(handle_cli_rpt_reload,      "Reload app_rpt config"),
    	AST_CLI_DEFINE(handle_cli_rpt_restart,     "Restart app_rpt")
    
    static struct telem_defaults tele_defs[] = {
    
    	{"ct1", "|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
    	{"ct2", "|t(660,880,150,3072)"},
    	{"ct3", "|t(440,0,150,3072)"},
    	{"ct4", "|t(550,0,150,3072)"},
    	{"ct5", "|t(660,0,150,3072)"},
    	{"ct6", "|t(880,0,150,3072)"},
    	{"ct7", "|t(660,440,150,3072)"},
    	{"ct8", "|t(700,1100,150,3072)"},
    	{"remotemon", "|t(1600,0,75,2048)"},
    	{"remotetx", "|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
    	{"cmdmode", "|t(900,904,200,2048)"},
    	{"functcomplete", "|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
    
    * Forward decl's - these suppress compiler warnings when funcs coded further down the file than their invocation
    
    */
    
    static int setrbi(struct rpt *myrpt);
    
    
    
    /*
    * Define function protos for function table here
    */
    
    
    static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
    static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
    static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
    static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
    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_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
    
    /*
    * Function table
    */
    
    static struct function_table_tag function_table[] = {
    	{"cop", function_cop},
    	{"autopatchup", function_autopatchup},
    	{"autopatchdn", function_autopatchdn},
    	{"ilink", function_ilink},
    	{"status", function_status},
    
    	{"macro", function_macro},
    	{"gosub", function_gosub},
    
    
    /*
    * Match a keyword in a list, and return index of string plus 1 if there was a match,
    * else return 0. If param is passed in non-null, then it will be set to the first character past the match
    */
    
    static int matchkeyword(char *string, char **param, char *keywords[])
    {
    
    	int	i, ls;
    	for (i = 0; keywords[i]; i++) {
    
    		if (!strncmp(string, keywords[i], ls)) {
    			if (param)
    
    				*param = string + ls;
    			return i + 1; 
    		}
    	}
    	param = NULL;
    	return 0;
    }
    
    /*
    * Skip characters in string which are in charlist, and return a pointer to the
    * first non-matching character
    */
    
    static char *skipchars(char *string, char *charlist)
    {
    
    	int i;	
    	while (*string) {
    		for (i = 0; charlist[i] ; i++) {
    			if (*string == charlist[i]) {
    
    	/* leave this %i alone, non-base-10 input is useful here */
    
    	if (sscanf(str, "%i", &ret) != 1)
    		return -1;
    
    
    #ifdef	__RPT_NOTCH
    
    /* rpt filter routine */
    static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
    {
    
    	for (i = 0; i < len; i++) {
    		for (j = 0; j < MAXFILTERS; j++) {
    
    			f->x2 = ((float)buf[i]) / f->gain;
    			f->y0 = f->y1; f->y1 = f->y2;
    			f->y2 =   (f->x0 + f->x2)     +  f->const0 * f->x1
    			        + (f->const1 * f->y0) + (f->const2 * f->y1);
    
    			buf[i] = (short)f->y2;
    		}
    	}
    }
    
    #endif
    
    /* Retrieve an int from a config file */
    
    static int retrieve_astcfgint(struct rpt *myrpt, const char *category, const char *name, int min, int max, int defl)
    
    	const char *var = ast_variable_retrieve(myrpt->cfg, category, name);
    	int ret;
    
    	if (var) {
    		ret = myatoi(var);
    		if (ret < min)
    			ret = min;
    		else if (ret > max)
    			ret = max;
    	} else
    		ret = defl;
    	return ret;
    
    static void load_rpt_vars(int n, int init)
    
    	int	j;
    	struct ast_variable *vp, *var;
    	struct ast_config *cfg;
    
    	struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
    
    	AST_DECLARE_APP_ARGS(strs,
    		AST_APP_ARG(str)[100];
    	);
    
    	ast_verb(3, "%s config for repeater %s\n",
    
    			(init) ? "Loading initial" : "Re-Loading", rpt_vars[n].name);
    
    	if (rpt_vars[n].cfg)
    		ast_config_destroy(rpt_vars[n].cfg);
    
    	cfg = ast_config_load("rpt.conf", config_flags);
    
    	if (!cfg) {
    		ast_mutex_unlock(&rpt_vars[n].lock);
    
    		ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf.  Radio Repeater disabled.\n");
    
    	rpt_vars[n].cfg = cfg;
    	/* Free previously malloc'ed buffer */
    	if (!init && rpt_vars[n].p.tailmsgbuf)
    		ast_free(rpt_vars[n].p.tailmsgbuf);
    	memset(&rpt_vars[n].p, 0, sizeof(rpt_vars[n].p));
    	if (init) {
    
    		char *cp;
    		int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
    
    		cp = (char *) &rpt_vars[n].p;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		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;
    		rpt_vars[n].tailmessagen = 0;
    	}
    #ifdef	__RPT_NOTCH
    	/* zot out filters stuff */
    
    	memset(&rpt_vars[n].filters, 0, sizeof(rpt_vars[n].filters));
    
    
    	/* Defaults */
    	ast_copy_string(rpt_vars[n].p.ourcontext, rpt_vars[n].name, sizeof(rpt_vars[n].p.ourcontext));
    	rpt_vars[n].p.hangtime = HANGTIME;
    	rpt_vars[n].p.totime = TOTIME;
    	rpt_vars[n].p.duplex = 2;
    	rpt_vars[n].p.idtime = IDTIME;
    	rpt_vars[n].p.politeid = POLITEID;
    	ast_copy_string(rpt_vars[n].p.memory, MEMORY, sizeof(rpt_vars[n].p.memory));
    	ast_copy_string(rpt_vars[n].p.macro, MACRO, sizeof(rpt_vars[n].p.macro));
    
    	ast_copy_string(rpt_vars[n].p.gosub, GOSUB, sizeof(rpt_vars[n].p.gosub));
    
    	rpt_vars[n].p.iobase = DEFAULT_IOBASE;
    
    	ast_copy_string(rpt_vars[n].p.functions, FUNCTIONS, sizeof(rpt_vars[n].p.functions));
    	rpt_vars[n].p.simple = 1;
    	rpt_vars[n].p.funcchar = FUNCCHAR;
    	rpt_vars[n].p.endchar = ENDCHAR;
    	ast_copy_string(rpt_vars[n].p.nodes, NODES, sizeof(rpt_vars[n].p.nodes));
    
    	for (var = ast_variable_browse(cfg, rpt_vars[n].name); var; var = var->next) {
    		if (!strcmp(var->name, "context")) {
    			ast_copy_string(rpt_vars[n].p.ourcontext, var->value, sizeof(rpt_vars[n].p.ourcontext));
    		} else if (!strcmp(var->name, "callerid")) {
    			ast_copy_string(rpt_vars[n].p.ourcallerid, var->value, sizeof(rpt_vars[n].p.ourcallerid));
    		} else if (!strcmp(var->name, "accountcode")) {
    			ast_copy_string(rpt_vars[n].p.acctcode, var->value, sizeof(rpt_vars[n].p.acctcode));
    		} else if (!strcmp(var->name, "idrecording")) {
    			ast_copy_string(rpt_vars[n].p.ident, var->value, sizeof(rpt_vars[n].p.ident));
    		} else if (!strcmp(var->name, "hangtime")) {
    			rpt_vars[n].p.hangtime = atoi(var->value);
    		} else if (!strcmp(var->name, "totime")) {
    			rpt_vars[n].p.totime = atoi(var->value);
    		} else if (!strcmp(var->name, "tailmessagetime")) {
    			rpt_vars[n].p.tailmessagetime = atoi(var->value);
    			if (rpt_vars[n].p.tailmessagetime < 0)
    				rpt_vars[n].p.tailmessagetime = 0;
    			else if (rpt_vars[n].p.tailmessagetime > 2400000)
    				rpt_vars[n].p.tailmessagetime = 2400000;
    		} else if (!strcmp(var->name, "tailsquashedtime")) {
    			rpt_vars[n].p.tailsquashedtime = atoi(var->value);
    			if (rpt_vars[n].p.tailsquashedtime < 0)
    				rpt_vars[n].p.tailsquashedtime = 0;
    			else if (rpt_vars[n].p.tailsquashedtime > 2400000)
    				rpt_vars[n].p.tailsquashedtime = 2400000;
    		} else if (!strcmp(var->name, "duplex")) {
    			rpt_vars[n].p.duplex = atoi(var->value);
    			if (rpt_vars[n].p.duplex < 0)
    				rpt_vars[n].p.duplex = 0;
    			else if (rpt_vars[n].p.duplex > 4)
    				rpt_vars[n].p.duplex = 4;
    		} else if (!strcmp(var->name, "idtime")) {
    			rpt_vars[n].p.idtime = atoi(var->value);
    			if (rpt_vars[n].p.idtime < 60000)
    				rpt_vars[n].p.idtime = 60000;
    			else if (rpt_vars[n].p.idtime > 2400000)
    				rpt_vars[n].p.idtime = 2400000;
    		} else if (!strcmp(var->name, "politeid")) {
    			rpt_vars[n].p.politeid = atoi(var->value);
    			if (rpt_vars[n].p.politeid < 30000)
    				rpt_vars[n].p.politeid = 30000;
    			else if (rpt_vars[n].p.politeid > 300000)
    				rpt_vars[n].p.politeid = 300000;
    		} else if (!strcmp(var->name, "tonezone")) {
    			ast_copy_string(rpt_vars[n].p.tonezone, var->value, sizeof(rpt_vars[n].p.tonezone));
    		} else if (!strcmp(var->name, "tailmessagelist")) {
    			rpt_vars[n].p.tailmsgbuf = ast_strdup(var->value);
    
    			AST_STANDARD_APP_ARGS(rpt_vars[n].p.tailmsg, rpt_vars[n].p.tailmsgbuf);
    
    		} else if (!strcmp(var->name, "memory")) {
    			ast_copy_string(rpt_vars[n].p.memory, var->value, sizeof(rpt_vars[n].p.memory));
    		} else if (!strcmp(var->name, "macro")) {
    			ast_copy_string(rpt_vars[n].p.macro, var->value, sizeof(rpt_vars[n].p.macro));
    
    		} else if (!strcmp(var->name, "gosub")) {
    			ast_copy_string(rpt_vars[n].p.gosub, var->value, sizeof(rpt_vars[n].p.gosub));
    
    		} else if (!strcmp(var->name, "startup_macro")) {
    			ast_copy_string(rpt_vars[n].p.startupmacro, var->value, sizeof(rpt_vars[n].p.startupmacro));
    
    		} else if (!strcmp(var->name, "startup_gosub")) {
    			ast_copy_string(rpt_vars[n].p.startupgosub, var->value, sizeof(rpt_vars[n].p.startupgosub));
    
    		} else if (!strcmp(var->name, "iobase")) {
    			/* do not use atoi() here, we need to be able to have
    			   the input specified in hex or decimal so we use
    			   sscanf with a %i */
    			if (sscanf(var->value, "%i", &rpt_vars[n].p.iobase) != 1)
    				rpt_vars[n].p.iobase = DEFAULT_IOBASE;
    		} else if (!strcmp(var->name, "functions")) {
    			rpt_vars[n].p.simple = 0;
    			ast_copy_string(rpt_vars[n].p.functions, var->value, sizeof(rpt_vars[n].p.functions));
    		} else if (!strcmp(var->name, "link_functions")) {
    			ast_copy_string(rpt_vars[n].p.link_functions, var->value, sizeof(rpt_vars[n].p.link_functions));
    		} else if (!strcmp(var->name, "phone_functions")) {
    			ast_copy_string(rpt_vars[n].p.phone_functions, var->value, sizeof(rpt_vars[n].p.phone_functions));
    		} else if (!strcmp(var->name, "dphone_functions")) {
    			ast_copy_string(rpt_vars[n].p.dphone_functions, var->value, sizeof(rpt_vars[n].p.dphone_functions));
    		} else if (!strcmp(var->name, "funcchar")) {
    			rpt_vars[n].p.funcchar = *var->value;
    		} else if (!strcmp(var->name, "endchar")) {
    			rpt_vars[n].p.endchar = *var->value;