Despite the frequency with which it arises here in the forum, there is not yet a good resource for learning to use dialplan hooks in FreePBX. This will probably end up in the wiki at some point, but until that happens, here are the broad strokes for leveraging dialplan hooks in FreePBX 14.
What is a dialplan hook?
A dialplan hook refers to several pre-defined FreePBX contexts that exist solely for users to add their own Asterisk Macros to be run at specific locations in the call flow. They all occur immediately before a Dial()
application and are used to run custom dialplan to perform some function not supported by the GUI. As a user, you define dialplan hooks as macros in the file /etc/asterisk/extensions_custom.conf
as individual asterisk contexts. Use whatever method you are comfortable with for editing the file; beginners may find is useful to use the Config Edit module. You may be aware of something in Asterisk called a âPredial Handlerâ. Despite the name similarity with the macros that follow, they are completely separate things having nothing to do with each other.
Arenât macros deprecated in Asterisk?
Yes. However, as of this writing (with 15 in beta) , FreePBX core still relies on dozens of Macros. This will have to be dealt with in time, and I have already opened a ticket for dealing with the predial hook macros. At some point this post will need updates to show how to use the predial subroutines.
How do I add custom SIP headers?
This entire post is a lead in to what is really needed, and that is a guide to adding custom SIP headers in a supportable way. This is beyond the scope of todayâs sermon but will form the basis of a future thread. I have been working with SIP headers a lot lately, and have been reminded once again of all the flawed internet how toâs or recommended methods that may once have worked, but no longer. For the longest time we had one SIP driver, and got comfortable with methods for adding SIP headers to chan_sip channels. When PJSIP came along, those methods didnât work for pjsip channels, so new methods were devised. FreePBX 14 introduced Dial changes that now break some of the early methods for adding headers for PJSIP channels. The end result is that virtually all of the posted methods for adding custom SIP headers are incomplete at best, or at worst are completely useless for FreePBX 14.
When do I use a dialplan hook and when do I use a Custom Destination?
If youâre able to structure your call flow such that the custom dialplan you want to execute looks something like this:
Inbound Route -> Time Condition -> <custom dialplan> -> Ring Group
Then you donât need (or want) to use a dialplan hook. The above is better done with a Custom Destination. Custom Destinations are preferred over dialplan hooks so use the GUI wherever possible.
How do I do X on every outbound call?
The first (and for the longest time, the only) dialplan hook going back more than a decade is macro-dialout-trunk-predial-hook
. There are many examples floating around the 'net on how to use it, and itâs existence is testament to all the configuration edge cases for the various SIP providers around the world. If your provider requires some specific setting that canât be done via the GUI, you would use this hook in order to make the necessary channel modifications prior to the trunk dial. A bare bones example would look like this:
[macro-dialout-trunk-predial-hook]
exten => s,1,NoOp(Entering user defined context macro-dialout-trunk-predial-hook in extensions_custom.conf)
; additional lines here
exten => s,n,MacroExit
The above example is trivial, it only records a single line in the Asterisk full log and then exits sending the call back to the FreePBX generated dialplan. An extension of s
is used as is required for all Macros. You can see the above in action by saving the change, reloading the dialplan and make a call after running the following at the bash prompt:
[root@freepbx ~]# tail -f /var/log/asterisk/full | grep predial
[2019-04-06 10:46:09] VERBOSE[20229][C-00000011] pbx.c: Executing [s@macro-dialout-trunk:20] Macro("PJSIP/5004-00000024", "dialout-trunk-predial-hook,") in new stack
[2019-04-06 10:46:09] VERBOSE[20229][C-00000011] pbx.c: Executing [s@macro-dialout-trunk-predial-hook:1] NoOp("PJSIP/5004-00000024", "Entering user defined context macro-dialout-trunk-predial-hook in extensions_custom.conf") in new stack
[2019-04-06 10:46:09] VERBOSE[20229][C-00000011] pbx.c: Executing [s@macro-dialout-trunk-predial-hook:3] MacroExit("PJSIP/5004-00000024", "") in new stack
Once the bare bones dialplan is in place and confirmed working with tail
, the hard part begins where you actually write useful dialplan that both works for all edge cases and doesnât break all the other edge cases.
How do I do X on every local call?
Itâs only a little less straight forward to perform similar action on local extension calls. There are several dialplan hooks used for calls to local extensions depending on the ring strategy used to reach the extension. You can see the dialplan hooks in use on local calls by running the same tail
from above. So focusing on a simple case where local extension 5004 calls local extension 5005 with no FMFM enabled, you would see the following:
[root@freepbx ~]# tail -f /var/log/asterisk/full | grep predial
[2019-04-06 10:50:07] VERBOSE[20945][C-00000012] pbx.c: Executing [s@macro-dial-one:52] Macro("PJSIP/5004-00000026", "dialout-one-predial-hook,") in new stack
[2019-04-06 10:50:07] VERBOSE[20945][C-00000012] pbx.c: Executing [s@macro-dialout-one-predial-hook:1] MacroExit("PJSIP/5004-00000026", "") in new stack
The above shows us that just before a call goes to a local extension, the Macro macro-dialout-one-predial-hook
is called. Using the same technique as above, we can add our own dialplan such as:
[macro-dialout-one-predial-hook]
exten => s,1,Noop(Entering user defined context macro-dialout-one-predial-hook in extensions_custom.conf)
; add additional lines here
exten => s,n,MacroExit
After save and reload, the tail
confirms itâs working:
[root@freepbx ~]# tail -f /var/log/asterisk/full | grep predial
[2019-04-06 10:57:00] VERBOSE[22171][C-00000013] pbx.c: Executing [s@macro-dial-one:52] Macro("PJSIP/5004-00000027", "dialout-one-predial-hook,") in new stack
[2019-04-06 10:57:00] VERBOSE[22171][C-00000013] pbx.c: Executing [s@macro-dialout-one-predial-hook:1] NoOp("PJSIP/5004-00000027", "Entering user defined context macro-dialout-one-predial-hook in extensions_custom.conf") in new stack
[2019-04-06 10:57:00] VERBOSE[22171][C-00000013] pbx.c: Executing [s@macro-dialout-one-predial-hook:2] MacroExit("PJSIP/5004-00000027", "") in new stack
How do I apply custom dialplan selectively on some outbound calls?
There is no single simple answer to this and sometimes the answer is âyou canâtâ. But usually one can identify a channel variable that is set to a unique value for all the calls you want to modify, and if so, that allows you to write dialplan that only acts on those calls. A simple example would be dialplan that does something different if the local call originates from extension 5004:
[macro-dialout-one-predial-hook]
exten => s,1,Noop(Entering user defined context macro-dialout-one-predial-hook in extensions_custom.conf)
exten => s,n,GotoIf($["${AMPUSER}"="5004"]?special)
exten => s,n,MacroExit
exten => s,n(special),NoOp(Incoming call from etension 5004)
exten => s,n,MacroExit
Call from 5004:
[root@freepbx ~]# tail -f /var/log/asterisk/full | grep predial
[2019-04-06 11:55:35] VERBOSE[31283][C-00000016] pbx.c: Executing [s@macro-dial-one:52] Macro("PJSIP/5004-0000002a", "dialout-one-predial-hook,") in new stack
[2019-04-06 11:55:35] VERBOSE[31283][C-00000016] pbx.c: Executing [s@macro-dialout-one-predial-hook:1] NoOp("PJSIP/5004-0000002a", "Entering user defined context macro-dialout-one-predial-hook in extensions_custom.conf") in new stack
[2019-04-06 11:55:35] VERBOSE[31283][C-00000016] pbx.c: Executing [s@macro-dialout-one-predial-hook:2] GotoIf("PJSIP/5004-0000002a", "1?special") in new stack
[2019-04-06 11:55:35] VERBOSE[31283][C-00000016] pbx_builtins.c: Goto (macro-dialout-one-predial-hook,s,4)
[2019-04-06 11:55:35] VERBOSE[31283][C-00000016] pbx.c: Executing [s@macro-dialout-one-predial-hook:4] NoOp("PJSIP/5004-0000002a", "Incoming call from etension 5004") in new stack
[2019-04-06 11:55:35] VERBOSE[31283][C-00000016] pbx.c: Executing [s@macro-dialout-one-predial-hook:5] MacroExit("PJSIP/5004-0000002a", "") in new stack
Call from 5016:
[root@freepbx ~]# tail -f /var/log/asterisk/full | grep predial
[2019-04-06 11:55:45] VERBOSE[31493][C-00000017] pbx.c: Executing [s@macro-dial-one:52] Macro("PJSIP/5016-0000002b", "dialout-one-predial-hook,") in new stack
[2019-04-06 11:55:45] VERBOSE[31493][C-00000017] pbx.c: Executing [s@macro-dialout-one-predial-hook:1] NoOp("PJSIP/5016-0000002b", "Entering user defined context macro-dialout-one-predial-hook in extensions_custom.conf") in new stack
[2019-04-06 11:55:45] VERBOSE[31493][C-00000017] pbx.c: Executing [s@macro-dialout-one-predial-hook:2] GotoIf("PJSIP/5016-0000002b", "0?special") in new stack
[2019-04-06 11:55:45] VERBOSE[31493][C-00000017] pbx.c: Executing [s@macro-dialout-one-predial-hook:3] MacroExit("PJSIP/5016-0000002b", "") in new stack
And that leads to the obvious question, how do I know which channel variable to check and for what value? Each situation is unique so you need to figure that out for yourself. In doing so the DumpChan
dialplan application is essential. Otherwise start a thread asking for ideas.
How do I perform a custom action on hangup?
Nothing to it. Using the techniques above, you would add a hangup handler to the channel. A hangup handler, as the name implies, is a subroutine that runs after the channel hangs up. A simple example:
The dialplan:
[macro-dialout-one-predial-hook]
exten => s,1,Noop(Entering user defined context macro-dialout-one-predial-hook in extensions_custom.conf)
exten => s,n,Set(CHANNEL(hangup_handler_push)=lgaetz-do-this-on-hangup,s,1)
exten => s,n,MacroExit
[lgaetz-do-this-on-hangup]
exten => s,1,Noop(Entering user defined context lgaetz-do-this-on-hangup in extensions_custom.conf)
; additional lines
exten => s,n,Return
The log entries:
[root@freepbx asterisk]# tail -f /var/log/asterisk/full | grep lgaetz
[2019-04-06 16:31:31] VERBOSE[7503][C-00000018] pbx.c: Executing [s@macro-dialout-one-predial-hook:2] Set("SIP/5002-00000007", "CHANNEL(hangup_handler_push)=lgaetz-do-this-on-hangup,s,1") in new stack
[2019-04-06 16:31:35] VERBOSE[7503][C-00000018] app_stack.c: SIP/5002-00000007 Internal Gosub(lgaetz-do-this-on-hangup,s,1) start
[2019-04-06 16:31:35] VERBOSE[7503][C-00000018] pbx.c: Executing [s@lgaetz-do-this-on-hangup:1] NoOp("SIP/5002-00000007", "Entering user defined context lgaetz-do-this-on-hangup in extensions_custom.conf") in new stack
[2019-04-06 16:31:35] VERBOSE[7503][C-00000018] pbx.c: Executing [s@lgaetz-do-this-on-hangup:2] Return("SIP/5002-00000007", "") in new stack
[2019-04-06 16:31:35] VERBOSE[7503][C-00000018] app_stack.c: SIP/5002-00000007 Internal Gosub(lgaetz-do-this-on-hangup,s,1) complete GOSUB_RETVAL=
Continued in part 2 - Virtuous Signalling