Sometimes life gives strange tasks.
Imagine that you have a dialstring in Asterisk like
Dial(PJSIP/dest1&PJSIP/dest2...)
But you need to stop the call at the moment when one of the destinations cancels the call. It's against Asterisk logic, but may be useful sometimes, if all destinations (devices) belongs to a same person.
So, the solution is to use a combination of pre-dial handlers and hangup handlers.
The code actually - extensions.conf
MAX_CHILD=10
[dial_uas]
exten => _X.,1,NoOp(Call from UAS received)
same => n,Dial(PJSIP/${EXTEN}@sipp_uas1&PJSIP/${EXTEN}@sipp_uas2,,b(arm_simultaneous_dial^s^1))
[arm_simultaneous_dial]
exten => s,1,NoOp(I am on channel:${CHANNEL})
same => n,Set(CHANNEL(hangup_handler_push)=simultaneous_dial_hangup_channel,s,1)
same => n,Set(CHILD_COUNT=1)
same => n,While($[("x${MASTER_CHANNEL(CHILD_${CHILD_COUNT})}" != "x" && $[${CHILD_COUNT} <= ${MAX_CHILD}])])
same => n,Set(CHILD_COUNT=$[${CHILD_COUNT} + 1])
same => n,EndWhile()
same => n,Set(MASTER_CHANNEL(CHILD_${CHILD_COUNT})=${CHANNEL})
same => n,Return
[simultaneous_dial_hangup_channel]
exten => s,1,NoOp(Hangup on channel:${CHANNEL})
; you may put here analyze of hangup code, so not to hangup on failed destination, etc.
same => n,Set(CHILD_COUNT=1)
same => n,While($[("x${MASTER_CHANNEL(CHILD_${CHILD_COUNT})}" != "x" && $[${CHILD_COUNT} <= ${MAX_CHILD}])])
same => n,GoToIf($[ "x${CHANNEL}" = "x${MASTER_CHANNEL(CHILD_${CHILD_COUNT})}"]?skip_myself)
same => n,SoftHangup(${MASTER_CHANNEL(CHILD_${CHILD_COUNT})})
same => n(skip_myself),Set(CHILD_COUNT=$[${CHILD_COUNT} + 1])
same => n,EndWhile()
same => n,Return
Idea is to save child channels id's in sequential CHILD_X variables of the master channel and on hangup one of child's - softhangup other child's.
Full proof of concept is available as docker-compose containing Asterisk and SIPP's on my GitHub.