How can I pass a variable to the trunk monitor agi script

There are various scripts around for a basic email alert on a trunk failure.
I’ve used one for years.

But I finally got annoyed enough by the message not including the trunk information that I decided to look at options.

Option 1 duplicate the script for every trunk so I can set the verbiage sad I want.
Option 2 programatically get the trunk name.

Obviously I want to use option 2 because option 1 is super annoying.

The simple way to get the trunk name is to pass it as an argument right there on the trunk configuration screen.
image

The AGI all parameters are made available as agi_arg_X within the agi script.
The above example results in agi_arg_1 containing the value of Skyetel

If there is a better way, I would love to know.

You tried dumping the environment at the beginning of your AGI already, right? That would be the easiest way to see what you have to work with.

I would be astounded if the trunk module didn’t manage the trunk name somewhere. There’s already quite a bit of environment information that’s available in the parent context, so getting one of those variables should make it a little easier.

The code detective in me would search through the AGI interface and see what I could add to the code and make a change and submit that. Finally, if none of that helps, you could try submitting a Feature Request that the trunk name get passed to the Monitor script automatically. It should be a one or two line change.

This is what the log looks like as it happens.

[2020-06-24 11:42:36] VERBOSE[15230][C-00000555] pbx.c: Executing [s@func-apply-sipheaders:12] EndWhile("PJSIP/Skyetel-00000b32", "") in new stack
[2020-06-24 11:42:36] VERBOSE[15230][C-00000555] pbx.c: Executing [s@func-apply-sipheaders:4] While("PJSIP/Skyetel-00000b32", "0") in new stack
[2020-06-24 11:42:36] VERBOSE[15230][C-00000555] pbx.c: Executing [s@func-apply-sipheaders:13] Return("PJSIP/Skyetel-00000b32", "") in new stack
[2020-06-24 11:42:36] VERBOSE[15230][C-00000555] app_stack.c: Spawn extension (from-pstn-e164-us, 6604894282, 1) exited non-zero on 'PJSIP/Skyetel-00000b32'
[2020-06-24 11:42:36] VERBOSE[15230][C-00000555] app_stack.c: PJSIP/Skyetel-00000b32 Internal Gosub(func-apply-sipheaders,s,1(2)) complete GOSUB_RETVAL=
[2020-06-24 11:42:36] VERBOSE[15230][C-00000555] app_dial.c: Called PJSIP/16604894282@Skyetel
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] app_dial.c: Everyone is busy/congested at this time (1:0/1/0)
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [s@macro-dialout-trunk:26] NoOp("PJSIP/103-00000b31", "Dial failed for some reason with DIALSTATUS = CONGESTION and HANGUPCAUSE = 34") in new stack
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [s@macro-dialout-trunk:27] GotoIf("PJSIP/103-00000b31", "1?continue,1:s-CONGESTION,1") in new stack
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx_builtins.c: Goto (macro-dialout-trunk,continue,1)
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [continue@macro-dialout-trunk:1] GotoIf("PJSIP/103-00000b31", "0?noreport") in new stack
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [continue@macro-dialout-trunk:2] AGI("PJSIP/103-00000b31", "testalert.agi,Skyetel") in new stack
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] res_agi.c: Launched AGI Script /var/lib/asterisk/agi-bin/testalert.agi
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] res_agi.c: <PJSIP/103-00000b31>AGI Script testalert.agi completed, returning 0
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [continue@macro-dialout-trunk:3] NoOp("PJSIP/103-00000b31", "TRUNK Dial failed due to CONGESTION HANGUPCAUSE: 34 - failing through to other trunks") in new stack
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [continue@macro-dialout-trunk:4] ExecIf("PJSIP/103-00000b31", "1?Set(CALLERID(number)=103)") in new stack
[2020-06-24 11:42:55] VERBOSE[15230][C-00000555] pbx.c: Executing [6604894282@from-internal:14] Macro("PJSIP/103-00000b31", "dialout-trunk,9,16604894282,,on") in new stack

dumping all of the AGI arguments

# initializing agi variables
DUMPARG="Begin Argument dump:\n"
while read -e ARG && [ "$ARG" ] ; do
 DUMPARG="$DUMPARG $ARG\n"
done

results in this.

Begin Argument dump:
 agi_request: testalert.agi
 agi_channel: PJSIP/103-00000b31
 agi_language: en
 agi_type: PJSIP
 agi_uniqueid: 1593016956.5260
 agi_version: 17.3.0
 agi_callerid: 1636NXXXXXX
 agi_calleridname: Bundy Assoc Inc
 agi_callingpres: 0
 agi_callingani2: 0
 agi_callington: 0
 agi_callingtns: 0
 agi_dnid: 6604894282
 agi_rdnis: unknown
 agi_context: macro-dialout-trunk
 agi_extension: continue
 agi_priority: 2
 agi_enhanced: 0.0
 agi_accountcode: JB103
 agi_threadid: 140423022749440
 agi_arg_1: Skyetel

At the point that the agi is called by the process, there are no channel variables available.
You can use global variables, but nothing from the channel.

image

Also not all global variables are passed to AGI by default, but can be passed as above.

# initializing agi variables
DUMPARG=" Begin Argument dump:\n"
declare -a array
while read -e ARG && [ "$ARG" ] ; do
 array=(` echo $ARG | sed -e 's/://'`)
 export ${array[0]}=${array[1]}
 DUMPARG="$DUMPARG $ARG\n"
done

DUMPARG="$DUMPARG Direct: SIPLANG = $SIPLANG\n"
DUMPARG="$DUMPARG Direct: AST_CONFIG_DIR = $AST_CONFIG_DIR\n"

image

I’m kind-of stumped at this point too.

I guess the only choices would be to feed it through a Bulk Handler CSV or submit it as a FR. I’m really surprised that the trunk ID isn’t available in the original context so you can pass the trunk variable from the original all. That way, at least, you could cut and paste it across all of your trunks.

Most places only have a couple trunks. so it is not hard to set this up like this. But yeah, I would have preferred “magic”

A final thought: try looking at the res_agi.c code in the source archive and seeing what gets handled there. You might find that more of the globals are available than are currently passed?

I will see what I can do later, as I would prefer a little different information. For now, it works to pass it.

For anyone curious, I have put the updated version of this on my github.

3 Likes

I just saw this in the forums summary email and it intersects with a backburner issue I have to get to.
Will this monitor and send out a notification when the registration is lost for a trunk? My non-educated reading is that it looks for call failures, but this isn’t my wheelhouse at all. One of 4 trunks goes down on one server, and I would like a notification about it when it happens so I can proactively take a look hand restart) rather than wait until someone tells me they can’t get calls.

No, it will not. This gets inserted into dialplan as an if statement. The if statement is checked after the dialplan returns from sending the call to the trunk. If the return code was an error, then execute the script.

What you want is a cron job that checks your trunk status. every X seconds/minutes.

This is not actually the case, the channel variables you’re looking for are available at that point in the call flow. The problem is with nesting variables inside variables. I have added a monitor script to 2 trunks on a system, one with ${} and one without. The value you put in the "Monitor Trunk Failures’ fields is first written to sql

MariaDB [asterisk]> select trunkid, name, failscript from trunks;
+---------+--------+--------------------------+
| trunkid | name   | failscript               |
+---------+--------+--------------------------+
|       1 | fpbx-1 | trunkfail.agi,${AMPUSER} |
|       2 | fpbx-2 | trunkfail.agi,AMPUSER    |
+---------+--------+--------------------------+
2 rows in set (0.01 sec)

Then on rebuild, these values are written to extensions_additional.conf as global variables:

[root@58448910 asterisk]# grep OUTFAIL /etc/asterisk/extensions_additional.conf
OUTFAIL_1 = trunkfail.agi,${AMPUSER}
OUTFAIL_2 = trunkfail.agi,AMPUSER

Which in turn is loaded by asterisk:

[root@58448910 asterisk]# asterisk -x "dialplan show globals" | grep OUTFAIL_
   OUTFAIL_2=trunkfail.agi,AMPUSER
   OUTFAIL_1=trunkfail.agi,

and those global variables are finally referenced in dialplan when the alert is triggered.

My above tests show that the entire Monitor Trunk Failures field is faithfully written to SQL and to the conf file, but Asterisk fails to load it properly if the global has the ${} characters. Not sure if there is any way to write a global var so it references a channel var or not.

The only supported way to do this that I can think of is to use a php script and then load the phpagi libraries so you can reference the channel variables directly in the script, instead of passing as args. Realize this is easier said than done, so here is a rough start on how that might be accomplished: lgaetz-trunk-monitor.php · GitHub

With that script, trunk name is logged in full log:

[2020-06-27 16:02:17] VERBOSE[11835][C-00000001] pbx.c: Executing [continue@macro-dialout-trunk:2] AGI("PJSIP/6014-00000000", "agi://127.0.0.1/lgaetz-trunk-monitor.php") in new stack
[2020-06-27 16:02:17] VERBOSE[11835][C-00000001] res_agi.c: agi://127.0.0.1/lgaetz-trunk-monitor.php: Oubound call faled on trunk: @fpbx-1

Just throwing spitballs here but some things I might try…

\${...}

${${...}}

$[${...}]

"${...}"

No real need for php or phpagi for such limited use. The following inserted after the bash script’s DUMPVAR loop should suffice:

echo "GET VARIABLE DIAL_TRUNK"
read -r line
line="${line#*\(}"
line="${line%)*}"

echo "GET VARIABLE OUT_$line"
read -r v1 v2 v3
[ "$v3" = "(PJSIP)" ] && {
  echo "GET VARIABLE OUT_${line}_SUFFIX"
  read -r v1 v2 v3
}
v3="${v3#*\(}"
v3="${v3%)*}"
TRUNK="$v3"
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.