Tuesday, February 9, 2021

Cancel a calls in an Asterisk dialsting

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.

Thursday, February 4, 2021

sexpect - expect alternative in shell

While looking for solution on terminal window resize using expect, found an alternative tool - sexpect. Despite the naming, a great tool actually.

Actually helps me to solve many issues and more of all - combine shell and expect-like scripts in 1 file.

Just to show an simple example (yes, I'm aware of ssh keys):

shell + expect:

#!/bin/bash

...
SSH_PASS=XXXXX
SSH_ADDR=YYYYY

expect <(cat << EOD
    spawn ssh $SSH_ADDR
    expect "Password:"
    send -- "${SSH_PASS}\n"
    interact
EOD
)

shell + sexpect

#!/bin/bash

...
SSH_PASS=XXXXX
SSH_ADDR=YYYYY

export SEXPECT_SOCKFILE=/tmp/sexpect-$$.sock

type -P sexpect >& /dev/null || exit 1

echo "Connecting to $SSH_ADDR..."

sexpect spawn -idle 120 -t 60 $SSH_ADDR

 if [[ $? != 0 ]]; then
    echo "Spawn failed!"
    exit 1
fi

sexpect expect -ex "Password:"
if [[ $? != 0 ]]; then
    echo "Nowhere to enter password!"
    exit 1
fi

sexpect send -enter "$SSH_PASS"

sexpect set -idle 5
sexpect interact

As a bonus - terminal window resize work as expected.