|
#!/bin/bash
##---------------------------------------------------------------------------##
# AGI framework for bash. To use:
#
# #!/bin/bash
# source $AST_AGI_DIR/bashagi.sh
# AGI_read_vars
# (your script goes here)
##---------------------------------------------------------------------------##
# A Bash-AGI script can run on one of two modes:
# - Standard: handled by Asterisk
# - Background: Commands are "handled" (usually ignored) by this script
# because its stdin/stdout are not connected to Asterisk's stdout/stdin
# See the AGI_background funcion for more information.
##---------------------------------------------------------------------------##
##---------------------------------------------------------------------------##
# AGI_read_vars: Read standard input and convert lines to shell variables
##---------------------------------------------------------------------------##
# AGI_ACCOUNTCODE=""
# AGI_ARG_1="<value of passed arg 1>" (repeats for additional args)
# AGI_CALLERID="2025550112" (received from the trunk)
# AGI_CALLERIDNAME="Caller Name" (received from the trunk)
# AGI_CALLINGANI2="0"
# AGI_CALLINGPRES="0"
# AGI_CALLINGTNS="0"
# AGI_CALLINGTON="0"
# AGI_CHANNEL="PJSIP/trunkname-00000030"
# AGI_CONTEXT="context-name"
# AGI_DNID="12025550123" (Trunk's DID)
# AGI_ENHANCED="0.0"
# AGI_EXTENSION="s"
# AGI_LANGUAGE="en"
# AGI_PRIORITY="1"
# AGI_RDNIS="unknown"
# AGI_REQUEST="whitelist.sh"
# AGI_THREADID="140092990433024"
# AGI_TYPE="PJSIP"
# AGI_UNIQUEID="1578321547.115"
# AGI_VERSION="16.6.2"
##---------------------------------------------------------------------------##
function AGI_read_vars {
eval "$(awk '/^[[:space:]]*$/ { exit}
{ print "export " toupper(substr($1, 1, length($1)-1)) "=\"" substr($0, length($1)+2) "\"" }')"
AGI_BACKGROUND=false
}
##---------------------------------------------------------------------------##
# AGI_command: send an AGI command to Asterisk and read the response.
# Parameters:
# 1. AGI command: see list at end of script
# 2. Parameters. Should be quoted unless it truly is a single word; for
# example, AGI_DATABASE_GET 'family variable' SHELL_VAR
# (Exception: AGI_VERBOSE combines all parameters into a phrase)
# 3. If the command is 'GET VARIABLE' or 'DATABASE GET' and this param
# has a value, it names an environment variable for storing the
# value returned by Asterisk.
##---------------------------------------------------------------------------##
function AGI_command {
local STATUS
# Handle background mode
if $AGI_BACKGROUND
then
if [ "$1" == 'VERBOSE' ]
then
shift
MSG="[$(date +'%Y-%m-%d %H:%M:%S')] VERBOSE[$$]$AGI_CALL_NUMBER $AGI_SCRIPT_NAME(bg): $@"
echo "$MSG" >>${AGI_VERBOSE_FN:-/dev/stdout}
fi
return 0
fi
# Send the command to Asterisk
if [ "$1" == 'VERBOSE' ]
then
local TEXT=''
while [ "$1" ]; do shift; TEXT="${TEXT}$1 "; done
I=$((${#TEXT}-2))
echo VERBOSE "\"${TEXT:0:$I}\""
else
echo $1 $2
fi
# Read the response
read STATUS
[[ "$STATUS" =~ result=([0-9]+) ]] && RC=${BASH_REMATCH[1]} || RC=0
if [ "$1" == 'GET VARIABLE' -o "$1" == 'DATABASE GET' ] &&
[ "$2" -a $RC == 1 ]
then
REGEXP='200 result=1 \((.*)\)'
[[ "$STATUS" =~ $REGEXP ]] && eval "$3=\"${BASH_REMATCH[1]}\""
fi
# If STATUS ends with '-', continue reading from stdin until a blank line
L="${#STATUS}"
if [ "${STATUS:0:3}" == '520' -o "${STATUS:$L:1}" == '-' ]
then
read LINE
while [ "$LINE" ]; do
STATUS="${STATUS}_NL_$LINE"
[ "${LINE:0:3}" == '520' ] && LINE="" || read LINE
done
echo -e "VERBOSE \"Multi-line status: $STATUS\""
read STATUS_1
fi
return $RC
}
##---------------------------------------------------------------------------##
# AGI_background: Run a new copy of the script in the background
#
# Running in the background allows the script return control to Asterisk
# as soon as possible while the background part does the work. But it
# disconnects the script from the stdin and stdout passed from Asterisk,
# meaning AGI functions no longer work. To allow logging via 'AGI_VERBOSE,'
# this function puts the call number (C-xxxxxxxx) into AGI_CALL_NUMBER
# and sets AGI_BACKGROUND to true. Subesquent calls to AGI_VERBOSE are
# sent to the log file if this script determines if it was called from
# Asterisk, else it sends them to stdout.
#
# In background mode most other functions are ignored. AGI_READ_VARIABLE
# and AGI_DATABASE_GET return no value.
#
# It is up to the caller to get variables as needed, export them so
# the background script can see them, and start the background part.
# Suggested code:
#
# #!/bin/bash
# source $AST_AGI_DIR/bashagi.sh
#
# # Prepare variables and start background operation
# if [ "$1" != '--background' ]
# then
# AGI_read_vars
# AGI_GET_VARIABLE 'CHANNEL(language)' CHAN_LANG
# AGI_GET_VARIABLE 'MIXMON_FORMAT' MXM_FMT
# AGI_DATABASE_GET 'CustomDevState DAYNIGHT0' DB_DAYNIGHT0
# export CHAN_LANG DB_DAYNIGHT0 MXM_FMT
# AGI_background
# exit
# fi
#
# # This is the background part--AGI functions no longer work, but
# # AGI_VERBOSE is emulated
# (... do background stuff ...)
# AGI_VERBOSE Background script exit
# exit
##---------------------------------------------------------------------------##
function AGI_background {
# Determine the call number
X="${AGI_CHANNEL//\//\\/}" # Change all '/' to '\/'
AGI_CALL_NUMBER="$(tail -n100 $AST_LOG_DIR/full | awk "
match(\$0,/(\\[C-[0-9a-f]+\\]).*$X/,a){print a[1];exit}
")"
# Determine if AGI_VERBOSE should write to the log file or stdout
AGI_VERBOSE_FN=/dev/stdout
TEMP_FN=/tmp/bash-agi.$$.tmp # Need to bounce pstree output through temp file:
pstree >$TEMP_FN # using pipe alters output and grep fails
grep -q 'asterisk.*pstree' $TEMP_FN && AGI_VERBOSE_FN=$AST_LOG_DIR/full
rm -f $TEMP_FN
# Relaunch the script, passing '--background' as parameter 1
AGI_BACKGROUND=true
AGI_SCRIPT_NAME="$(basename $0)"
export AGI_BACKGROUND AGI_CALL_NUMBER AGI_SCRIPT_NAME AGI_VERBOSE_FN
SELF=$0
shift
nohup $SELF --background "$@" >/dev/null 2>&1 &
unset AGI_CALL_NUMBER AGI_SCRIPT_NAME AGI_VERBOSE_FN
AGI_BACKGROUND=false
}
##---------------------------------------------------------------------------##
# Asterisk AGI function stubs
##---------------------------------------------------------------------------##
function AGI_ANSWER { AGI_command 'ANSWER'; }
function AGI_ASYNCAGI_BREAK { AGI_command 'ASYNCAGI BREAK'; }
function AGI_CHANNEL_STATUS { AGI_command 'CHANNEL STATUS' "$@"; }
function AGI_CONTROL_STREAM_FILE { AGI_command 'CONTROL STREAM FILE' "$@"; }
function AGI_DATABASE_DEL { AGI_command 'DATABASE DEL' "$@"; }
function AGI_DATABASE_DELTREE { AGI_command 'DATABASE DELTREE' "$@"; }
function AGI_DATABASE_GET { AGI_command 'DATABASE GET' "$@"; }
function AGI_DATABASE_PUT { AGI_command 'DATABASE PUT' "$@"; }
function AGI_EXEC { AGI_command 'EXEC' "$@"; }
function AGI_GET_DATA { AGI_command 'GET DATA' "$@"; }
function AGI_GET_FULL_VARIABLE { AGI_command 'GET FULL VARIABLE' "$@"; }
function AGI_GET_OPTION { AGI_command 'GET OPTION' "$@"; }
function AGI_GET_VARIABLE { AGI_command 'GET VARIABLE' "$@"; }
function AGI_GOSUB { AGI_command 'GOSUB' "$@"; }
function AGI_HANGUP { AGI_command 'HANGUP'; }
function AGI_NOOP { AGI_command 'NOOP' "$@"; }
function AGI_RECEIVE_CHAR { AGI_command 'RECEIVE CHAR' "$@"; }
function AGI_RECEIVE_TEXT { AGI_command 'RECEIVE TEXT' "$@"; }
function AGI_RECORD_FILE { AGI_command 'RECORD FILE' "$@"; }
function AGI_SAY_ALPHA { AGI_command 'SAY ALPHA' "$@"; }
function AGI_SAY_DATE { AGI_command 'SAY DATE' "$@"; }
function AGI_SAY_DATETIME { AGI_command 'SAY DATETIME' "$@"; }
function AGI_SAY_DIGITS { AGI_command 'SAY DIGITS' "$@"; }
function AGI_SAY_NUMBER { AGI_command 'SAY NUMBER' "$@"; }
function AGI_SAY_PHONETIC { AGI_command 'SAY PHONETIC' "$@"; }
function AGI_SAY_TIME { AGI_command 'SAY TIME' "$@"; }
function AGI_SEND_IMAGE { AGI_command 'SEND IMAGE' "$@"; }
function AGI_SEND_TEXT { AGI_command 'SEND TEXT' "$@"; }
function AGI_SET_AUTOHANGUP { AGI_command 'SET AUTOHANGUP' "$@"; }
function AGI_SET_CALLERID { AGI_command 'SET CALLERID' "$@"; }
function AGI_SET_CONTEXT { AGI_command 'SET CONTEXT' "$@"; }
function AGI_SET_EXTENSION { AGI_command 'SET EXTENSION' "$@"; }
function AGI_SET_MUSIC { AGI_command 'SET MUSIC' "$@"; }
function AGI_SET_PRIORITY { AGI_command 'SET PRIORITY' "$@"; }
function AGI_SET_VARIABLE { AGI_command 'SET VARIABLE' "$@"; }
function AGI_SPEECH_ACTIVATE_GRAMMAR { AGI_command 'SPEECH ACTIVATE GRAMMAR' "$@"; }
function AGI_SPEECH_CREATE { AGI_command 'SPEECH CREATE' "$@"; }
function AGI_SPEECH_DEACTIVATE_GRAMMAR { AGI_command 'SPEECH DEACTIVATE GRAMMAR' "$@"; }
function AGI_SPEECH_DESTROY { AGI_command 'SPEECH DESTROY'; }
function AGI_SPEECH_LOAD_GRAMMAR { AGI_command 'SPEECH LOAD GRAMMAR' "$@"; }
function AGI_SPEECH_RECOGNIZE { AGI_command 'SPEECH RECOGNIZE' "$@"; }
function AGI_SPEECH_SET { AGI_command 'SPEECH SET' "$@"; }
function AGI_SPEECH_UNLOAD_GRAMMAR { AGI_command 'SPEECH UNLOAD GRAMMAR' "$@"; }
function AGI_STREAM_FILE { AGI_command 'STREAM FILE' "$@"; }
function AGI_TDD_MODE { AGI_command 'TDD MODE' $1; }
function AGI_VERBOSE { AGI_command 'VERBOSE' "$@"; }
# vim: tabstop=4
|