Using channel name in call files

Here’s a strange problem I’ve been trying to figure out for the past hour:

I would like to be able to set a variable equal to a channel name in a variable. Doing this for the extension works, no problem. I need to be able to set multiple variables for different channels, however.

Using ${CHANNEL} literally fails, because channel names contain semicolons so the System() eats the ; up. Thus, the ; needs to be escaped. So, naturally, replace ; with ;, right?

Except that \ itself is a special character, and needs to be escaped. Better make that \;, to prevent the escaper from getting escaped.

There seem to be 3 levels of escaping in play. The ; needs to be escaped in the dialplan, or the ; is interpreted as a dialplan comment (this only applies to literal channel names, not those in a variable like ${CHANNEL}). Once the ; makes it into the System() call, it needs to be escaped again or it will throw an error about invalid call file contents. But, after all that, the variable seems to get truncated, rather than set properly.

As an example:

same => n,System(echo Setvar: chan1=${STRREPLACE(ARG1,\;,\\\\\;)} >> /tmp/${UNIQUEID}.call)
	same => n,System(echo Setvar: chan2=${STRREPLACE(ARG1,\;,\\\\\\\;)} >> /tmp/${UNIQUEID}.call)
	;same => n,System(echo Setvar: chan3=${STRREPLACE(ARG1,\;,\\\\\\\\\;)} >> /tmp/${UNIQUEID}.call)
	;same => n,System(echo Setvar: chan4=${STRREPLACE(ARG1,\;,\\\;)} >> /tmp/${UNIQUEID}.call)

chan3 and chan4 are commented out, because they are invalid call file contents and will cause the call to crash. Too few or too many backslashes and it fails, so the correct number seems to be either 4 or 6 backslashes.

Here’s what that looks like in the CLI:

[Apr 20 19:49:52]     -- Executing [s@blind-transfer-recall-monitor-schedule:7] System("Local/1A12128@extensions-0000001d;2", "echo Setvar: chan1=Local/SIPATAxLB1@outgoing-00000021\\;2 >> /tmp/1618962580.78.call") in new stack
[Apr 20 19:49:52]     -- Executing [s@blind-transfer-recall-monitor-schedule:8] System("Local/1A12128@extensions-0000001d;2", "echo Setvar: chan2=Local/SIPATAxLB1@outgoing-00000021\\\;2 >> /tmp/1618962580.78.call") in new stack

So then I NoOp() them out on the other end, and what do I get?

Executing [s@blind-transfer-recall:4] NoOp("Local/35@wait-00000025;1", "Local/SIPATAxLB1@outgoing-00000021 / Local/SIPATAxLB1@outgoing-00000021 / / ") in new stack

The ;2 is cut off from the variables - meaning that somewhere between putting the channel name in a call file and the channel getting created, everything after the semicolon, including the semicolon, got truncated. Consequently, subsequent dialplan execution fails because no such channels exist.

If you take a look at the source code, this is exactly how semicolons in call files should be escaped: asterisk/pbx_spool.c at fd0ca1c3f9b972a52d48a82b492fd6bac772dc78 · asterisk/asterisk · GitHub

So, this seems to be the right approach, but everything I try either ends up with the call file failing → call crashing or a truncated variable name.

Has anyone figured out the proper way to escape the semicolon in channel names for usage in creating a call file?

Every problem looks like it can be solved with an AGI to me, but this time I might be right :slight_smile:

This would be dead easy / more robust / more maintainable as an AGI.

I’d prefer not to call yet more external code if it could be done in the dialplan.

Also worth noting is this is all part of a hangup handler, so time is of the essence and I believe spawning AGI is “expensive”.

I can appreciate that philosophy, but if you limit yourself to a hammer, everything tends to look like a nail and can prevent you from seeing better solutions.

Why? Are hangup handlers limited in some way?

expensive is relative

If you are so resource constrained that the small fraction (like hundredths or thousandths) of a second to fire up an AGI to write the call file, you’re likely to run into other problems soon. Try it – you might like it :slight_smile:

I think it would be simpler to create your call file from the cdr table rather than from a hangup handler. Just my 2 cents. And by the way you are already calling an external program (the linux shell) so dont think you are being fast and furiouse when calling system.

Good point. Actually, you are calling system() multiple times so calling agi() once could save resources.

Yeah, I suppose that’s a fair point about the System() calls. I really wish Originate() wasn’t so limited, it’s useless for 99% of applications so that’s the only way to spawn dialplan execution.

I don’t see how the CDR table would help, though. Then I would need yet another process continuously monitoring for that. I’m not sure it would contain the information needed, though.

Something that came to mind is using STRREPLACE to replace ; with a dummy character that would never appear in a channel name, like a ^ or & or something, and then converting it back on the other end. Another hacky workaround, but I’m not sure how else to get it to work using System().