[Asterisk] Advanced Dialplan

Basic Expressions

Asterisk 에서 모든 표현식(수식)은 $ [ ] 표시된다.

$[expression]

그리고 다음과 같이 사용할 수 있다.

$[${COUNT} + 1]$[${COUNT} / 2]

Asterisk Dialplan 내에서 표현식이 있다면 해당 구문은 먼저 모든 표현식이 계산된 이후에 진행된다.
다음의 예제를 생각해보자.

exten => 204,1,Noop()
        same => n,Answer()
        same => n,Set(COUNT=3)
        same => n,Set(NEWCOUNT=$[${COUNT} + 1])
        same => n,SayNumber(${NEWCOUNT})

2번째 priority 에서 COUNT 변수에 3을 입력했다.
그리고 3번째 priority 에서 하나의 Set appilication 만이 사용됐는데, 사실은 3가지 일이 벌어졌다.

1. ${COUNT} 를 3으로 대치한다. 즉, 표현식은 다음과 같이 변경된다.
same => n,Set(NEWCOUNT=$[3 + 1])

2. 표현식을 계산한다. 즉, 3과 1을 더한다. 결과값 4가 나온다.
same => n,Set(NEWCOUNT=4)

3. Set() Application이 NEWCOUNT 에 4를 입력한다.

Operators

Boolean operators

expr1 | expr2
or 연산자. expr1 혹은 expr2 둘 중 하나라도 참이라면 참.

expr1 & expr2
and 연산자. expr1 혹은 expr2 둘 중 하나라도 거짓이면 거짓.

expr1 {=, >, >=, <, <=, !=} expr2비교 연산자. expr1, expr2 각 숫자라면 숫자 비교를 하고, 아니라면 문자열 비교를 한다.

Mathematical operators

expr1 {+, -} expr2
더하기, 빼기.

expr1 {*, /, %} expr2
곱하기, 나누기, 나머지.

Regular expression operators

expr1 : expr2
정규 표현식 내용 참조.

expr1 =~ expr2
정규 표현식 내용 참조.

String
exten => 234,1,Set(NEWTEST=blah${TEST})

Dialplan Functions

Dialplan Function 들은 많은 기능들을 가지고 있다. 문자열 길이 계산, 시간 및 날짜 계산, MD5 체크섬 오류 확인 등등의 기능을 제공한다.

Syntax

Dialplan Function 은 다음과 같은 기본 Syntax 구조를 가진다.

FUNCTION_NAME(argument)

그리고 다음과 같이 Function 자체가 다른 Function 의 Argument 로 사용되는 것도 가능하다.

${FUNCTION_NAME(${FUNCTION_NAME(argument)})}

Conditional Branching

Asterisk 에서 제공하는 함수들을 이용하면 여러가지 조건에 따라 분기를 생성할 수 있다. 이를 Conditional Branching 이라고 한다.

The GotoIf() Application

Conditional Branching 의 핵심은 GotoIf() Application 이다. GotoIf() Application 은 입력된 조건 식의 참/거짓 여부에 따라 해당 콜을 다른 곳으로 분배시키는 역할을 한다.

GotoIf(expression?destination1:destination2)

만약 expression 이 참이면 destination1로, 거짓이면 destination2 로 해당 콜을 넘겨준다. 그렇다면 무엇이 참이고 거짓일까? 표현식의 결과값이 공백 String 이거나 0이면 거짓이고 그 외의 것들은 모두 참으로 판별한다.
destination 은 다음중 한가지로 지정할 수 있다.

– weasels 와 같이  동일한 extension 내의 priority label- 123,wesels 와 같이 동일한 context 내의 다른 extension 과 priority label
– incoming,123,weasels 와 같이 다른 context, 다른 extension, 다른 priority label

입력되는 destination 중 하나는 생략이 가능하다. 즉, 하나의 destination 만 입력을 해도 괜찮다는 뜻이다. 하지만 이는 역으로 반드시 하나는 입력해야 된다는 뜻이다. 입력되지 않은 destination 의 경우, 그냥 다음 priority 구문을 진행한다.

exten => 207,1,NoOp()
        same => n,Set(TEST=1)
        same => n,GotoIf($[${TEST} = 1]?weasels:iguanas)
        same => n(weasels),Playback(weasels-eaten-phonesys)
        same => n,Hangup()
        same => n(iguanas),Playback(office-iguanas)
        same => n,Hangup()

기능은 정상적으로 작동하지만 뭔가 알아보기 힘들다. 다음과 같이 작성해보자.

exten => 208,1,NoOp()
        same => n,Answer()
        same => n,Set(TEST=1)
        same => n,GotoIf($[${TEST} = 1]?weasels,1:iguanas,1)    ; now we're going to extension,priority
exten => weasels,1,NoOp()
        same => n,Playback(weasels-eaten-phonesys)      ; this is NOT a label. It is different extension
        same => n,Hangup()
exten => iguanas,1,NoOp()
        same => n,Playback(office-iguanas)
        same => n,Hangup()

다음은 10부터 0까지 카운트 다운을 하는 Dialplan 이다.

exten => 209,1,NoOp()
        same => n,Answer()
        same => n,Set(COUNT=10)
        same => n(start),GotoIf($[${COUNT} > 0]?:goodbye)
        same => n,SayNumber(${COUNT})
        same => n,Set(COUNT=$[${COUNT} - 1])
        same => n,Goto(start)
        same => n(goodbye),Hangup()

 Time-Based Conditional Branching with GotoIfTime()

GotoIf() Application 이 표현식에 따른 분기였다면, GotoIfTime() Application 은 system time 에 따른 분기라고 말할 수 있다.

GotoIfTime(times,days_of_week,days_of_month,months?label)

짧게 말해서, GotoIfTime() 은 지정된 시간에 콜을 지정된 label 로 보내는 Application 이라고 할 수 있다.

times:
시간대 범위. 하나 혹은 여러개를 지정할 수 있다. 24-hour 형식을 사용함. 예를 들어 09:00 AM 부터 05:00 PM 까지의 시간대를 지정한다면 09:00-17:00 으로 표시할 수 있다. 하루를 통째로 입력한다면 00:00-23:59 로 표시한다.

days_of_week:
요일 범위. 하나 혹은 여러개를 지정할 수 있다. mon,tue,wed,thu,fri,sat,sun 으로만 표시가 가능하다. 월요일부터 금요일까지를 표시하고자 한다면 mon-fri 로 표시한다. 화요일과 목요일만 표시하고자 한다면 tue&thu 로 표시한다.

days_of_month:
일 범위. 하나 혹은 여러개를 지정할 수 있다. 1 부터 31 까지 지정하고자 하는 날짜를 입력하면 된다. 7일부터 12일까지라면 7-12, 15일과 30일 이틀만 지정하고자 한다면 15&30 으로 표시한다.

month:
달 범위. 하나 혹은 여러개를 지정할 수 있다. jan-apr 과 같이 지정 가능하며 jan&mar&jun 과 같은 지정도 가능하다. 물론 jan-apr&jun&oct-dec 와 같은 지정도 가능하다.

모든 범위:
만약 모든 날짜(1년 내내와 같이..)를 지정하고자 한다면 해당 입력 부분에 * 표시를 입력하면 된다.

label:
같은 extension 부분에 있는 priority. ex) time_has_passed
같은 context 에 있는 다른 extension, priority. ex) 123,time_has_passed
다른 context, extension, priority. ex) incoming,123,time_has_passed

예제를 보자. 매주 월요일부터 금요일까지 오전 9:00 부터 오후 05:59 사이에 오는 콜을 처리하는 Dialplan 이다.

exten => s,1,NoOp()
    same => n,GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)

만약 발신자가 해당 시간에 전화를 하게 되면 open context 내의 s extension 의 첫번째 priority 구문을 실행한다.

아래 예제는 참고로 보길 바란다.

exten => 212,1,NoOp()
        same => n,GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)

; If it's any hour of the day, on any day of the week,
; during the fourth day of the month, in the month of July,
; we're closed
        same => n,GotoIfTime(*,*,4,jul?closed,s,1)

; During business hours, send calls to the open context
        same => n,GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)
        same => n,GotoIfTime(09:00-11:59,sat,*,*?open,s,1)

Macro

만약 여러개의 Dialplan 을 편집해야 한다면? 그 수가 굉장히 많다면? Macro 가 그 해답일 수 있다.

모든 Macro 는 macro- 구문으로 시작한다. 만약 voicemail 을 위한 Macro 를 생성한다면 다음처럼 생성하면 된다.

[macro-voicemail]

다음은 예제로 작성된 voicemail macro 이다.

[macro-voicemail]
exten => s,1,NoOp()
        same => n,Dial(${JOHN},10)
        same => n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy:unavail)
        same => n(unavail),VoiceMail(101@default,u)
        same => n,Hangup()
        same => n(busy),VoiceMail(101@default,b)
        same => n,Hangup()

Calling Macros from the Dialplan

Dialplan 에서 Macro 를 사용하기 위해서는 Macro() application 이 필요하다. Macro() application 은 Macro 를 호출하고 인자값들을 넘겨주는 역할을 한다.
예를 들어 방금 생성한 voicemail macro 를 호출하기 위해서는 다음과 같이 작성하면 된다.

exten => 213,1,Macro(voicemail)

Macro() application 은 특수 변수들을 가지고 있다.

${MACRO_CONTEXT}
Macro 를 호출한 extension 의 context

${MACRO_EXTEN}Macro 를 호출한 Extension

${MACRO_PRIORITY}
Macro 를 호출한 Priority

${ARG n}
Macro 를 호출하면서 넘겨진 인자 값들. 첫번째 인자값은 ${ARG1}, 두번째 인자값은 ${ARG2} 와 같다.

자, 이제 조금전 생성한 voicemail macro 를 수정해보자. 조금전 생성한 voicemail macro 는 오직 101 extension 에 대해서만 voicemail 을 수행했었다.
이를 호출되는 extension 의 voicemail 로 수정해보자.

[macro-voicemail]
exten => s,1,NoOp()
        same => n,Dial(${ARG1},20)
        same => n,Goto(s-${DIALSTATUS},1)

exten => s-NOANSWER,1,VoiceMail(${MACRO_EXTEN}@default,u)
        same => n,Goto(incoming,s,1)

exten => s-BUSY,1,VoiceMail(${MACRO_EXTEN}@default,b)
        same => n,Goto(incoming,s,1)

exten => _s-.,1,NoOp()
        same => n,Goto(s-NOANSWER,1)

GoSub

GoSub() application 은 Macro() application 과 비슷하다. 하지만 Macro() 와는 달리 별도의 Naming rule 이 필요하지 않다. 하지만 GoSub 루틴임을 알리기 위해 prefix 를 붙이는 것이 관례적이다.
다음은 Subroutine 예제이다.

[subVoicemail]
exten => start,1,NoOp()
        same => n,Dial(${ARG1},10)
        same => n,VoiceMail(${ARG2}@default,${IF($[DIALSTATUS} = BUSY]?b:u)})
        same => n,Hangup()

[LocalSets]
exten => 217,1,GoSub(subVoicemail,start,1(${JOHN},${EXTEN}))
exten => 218,1,GoSub(subVoicemail,start,1(${JANE},${EXTEN}))
exten => 219,1,GoSub(subVoicemail,start,1(${JACK},${EXTEN}))

 

 

[Freeswitch] How to change Freeswitch ip address

Freeswtich 설치 이후, 테스트를 진행하는데 SIP 클라이언트 접속이 안되는 문제가 발생했다.

telnet/netstat 으로 확인해보니 포트가 안뚫려있었다. Freeswitch listen IP가 localhost 로만 설정되어 있는 상황.

따라서, Freeswitch Server Ip 설정을 변경해주니 잘 작동하였다.

/usr/local/freeswitch/conf/vars.xml 파일 내용에서 다음 부분을 수정해주었다.(local_ip_v4 항목을 추가)

  <X-PRE-PROCESS cmd="set" data="sound_prefix=$${sounds_dir}/en/us/callie"/>

  <!--
      This setting is what sets the default domain FreeSWITCH will use if all else fails.

      FreeSWICH will default to $${local_ip_v4} unless changed.  Changing this setting does
      affect the sip authentication.  Please review conf/directory/default.xml for more
      information on this topic.
  -->
  <X-PRE-PROCESS cmd="set" data="local_ip_v4=192.168.200.10"/>
  <X-PRE-PROCESS cmd="set" data="domain=$${local_ip_v4}"/>
  <X-PRE-PROCESS cmd="set" data="domain_name=$${domain}"/>
  <X-PRE-PROCESS cmd="set" data="hold_music=local_stream://moh"/>
  <X-PRE-PROCESS cmd="set" data="use_profile=internal"/>

보통은 위의 설정이 없이도, 자동으로 local_ip_v4 에 IP 가 설정이 된다. 그러나, 만약 두개 이상의 NIC 가 달려 있는 서버라면.. 문제가 발생한다. 이 경우 위의 설정이 필요하다.

참조: http://wiki.freeswitch.org/wiki/Getting_Started_Guide#Some_stuff_to_try_out.21

 

lsof anon_inode

사내에서 개발한 리눅스 플랫폼 프로그램을 테스팅하면서 File descriptor 를 감시해야 하는 경우가 생겼다.

Socket 을 열고 정상적으로 종료를 하는지 확인하는 내용이었는데.. 이를 위해서 lsof 유틸리티를 사용했다.

$ lsof -p [PID] | wc -l

의 명령어로 갯수를 확인하면서 open file descriptor 수를 감시했다.

그러던 중, lsof 의 결과 출력을 분석했는데 이상한 것이 있었다.

cbapid  5560 root    3u  0000                0,7        0       521 anon_inode
cbapid  5560 root    4u  0000                0,7        0       521 anon_inode
cbapid  5560 root    5r  0000               0,11        0 271110595 eventpoll
cbapid  5560 root    6u  0000                0,7        0       521 anon_inode
cbapid  5560 root    7r  0000               0,11        0 271110596 eventpoll
cbapid  5560 root    8u  0000                0,7        0       521 anon_inode
cbapid  5560 root    9u  0000                0,7        0       521 anon_inode
cbapid  5560 root   10r   CHR                1,9      0t0  97762892 /dev/urandom
cbapid  5560 root   11r  0000               0,11        0 271110599 eventpoll

anon_inode 와 같이 나타난 file descriptor 들이 많이 있는 것을 확인한 것이다.

anon_inode 가 무엇이고 왜 나타나는 것일까?

해답은 다음과 같다.

anon_inode 는 anonymouse_inode 의 약자이며, epoll 을 사용할 경우, 감시대상 file descriptor 들을 관리하기 위해서 생성하고 사용하는 file descriptor 인 것이다.
즉, 내부적으로 epoll 을 사용하는 library 가 있다면 나타나는 정상적인 현상인 것이다.

참조 :  http://stackoverflow.com/questions/8170902/why-is-the-jdk-nio-using-so-many-anon-inode-file-descriptors

 

 

mtp mount in gentoo

Gentoo 머신에 안드로이드 폰을 연결해서 사용할 일이 있었다. 그냥 될 줄 알고 케이블을 연결했는데 인식이 안되는 것이었다. 역시… 뭔가를 해주어야 했다.

Gentoo wiki 에서는 mtp mount 하는 방법을 세가지 소개하고 있다. 바로 mtpfs, go-mtpfs, kde 이다. 이중 나는 mtpfs 와 go-mtpfs 를 사용했다.

결론적으로 mtpfs 는 원활한 동작이 되지 않았다. Mount 까지는 성공한 듯 싶었으나, 실제 마운트된 디렉토리에서 파일들에 접근할 수가 없었다. go-mtpfs 의 경우 완벽하게 동작했다.

먼저 go 를 설지하자.

$ sudo emerge dev-lang/go

그리고 mtpfs 를 위한 추가 작업을 진행해주자.

pchero@localhost ~ $ mkdir go
pchero@localhost ~ $ export GOPATH=/home/pchero/go
pchero@localhost ~ $ go get github.com/hanwen/go-mtpfs

그리고 그룹을 추가해주고…

$ sudo gpasswd -a pchero plugdev

마운트할 디렉토리를 생성하고 휴대폰에 케이블을 연결한 뒤, 마운트를 해주자.

$ mkdir ~/Android$ ~/go/bin/go-mtfs ~/Android

언마운트를 할 때는 아래와 같이..

$ fusermount -u ~/Android/

출처: https://wiki.gentoo.org/wiki/MTP

Asterisk – sip.conf

/etc/asterisk/sip.conf 파일은 Asterisk 에서 sip 채널을 설정하는 파일이다. 즉, Asterisk 에서 관리하는 SIP 단말기 정보를 입력하는 설정파일이다.

/etc/asterisk/sip.conf 파일 예제

[general]
context = unauthenticated               ; default context for incoming calls
allowguest = no                 ; disable unauthenticated calls
srvlookup = no                  ; disable DNS SRV record lookup on outbound calls
                                ;   (unless you have a reliable DNS connection,
                                ;   in which case yes)
udpbindaddr = 0.0.0.0           ; listen for UDP requests on all interfaces
port = 5060
tcpenable = no                  ; disable TCP support
register => 123123123:123123123@192.168.0.1  ; External trunk register information
;tlsenable = yes
;tlsbindaddr = ::

[teset_phone_ibk87ns4gj]
type = friend
host = dynamic
secret = tb9hibvp9c
context = demo

[messagewaiting](!)             ; a template to handle the settings common
                                ; to all mailboxes
type=peer
subscribecontext = voicemailbox ; the dialplan context on the voicemail server
context = voicemailbox          ; the dialplan context on the voicemail server
host = 192.168.200.10           ; ip address of presence server

[office-phone](!)       ; create a template for our devices
type = friend           ;   the channel driver will match on username first,
                        ;   IP second
context = LocalSets     ; this is where calls from the device will enter
                        ; the dialplan
host = dynamic          ; the device will register with asterisk
nat = force_rport,comedia       ; assume device is behind NAT
                        ;   *** NAT stands for Network Address Translation,
                        ;   which allows multiple internal devices to share an
                        ;   external IP address.
dtmfmode = auto ; accept touch-tone from the devices, negotiated
                ; automatically
disallow = all  ; reset which voice codec this device will accept or offer
allow = g722    ; audio codecs to accept from, and request to, the device
allow = ulaw    ; in the order we prefer

; define a device name and use the office-phone template
[0000FFFF0001](office-phone)
secret = helloworld      ; a unique password for this device --
                        ; DON'T USE THE PASSWORD WE'VE USED IN THIS EXAMPLE!

; define another device name using the same template
[0000FFFF0002](office-phone)
secret = helloworld ; a unique password for this device --
                        ; DON'T USE THE PASSWORD WE'VE USED IN THIS EXAMPLE!

[0000FFFF0003](office-phone)
secret = helloworld   ; DON'T USE THE PASSWORD WE'VE USED IN THIS EXAMPLE!
allow = gsm

[0000FFF0004](messagewaiting)   ; this will need to match the subsriber
                                ; name on the proxy
mailbox = 0000FFFF0004@DIR1     ; must be in the form mailbox@mailboxcontext
defaultuser = 0000FFFF0004      ; this will need to match the subscriber
                                ; name on the proxy

[general] Section

가장 기본적인 섹션이며, Default 값들을 설정하는 곳이다.
모든 채널 모듈 설정시, 기본적으로 설정의 제일 앞부분에 [general] 섹션을 포함한다고 생각하면 되며,  [general] 섹션의 이름은 바뀌어서는 안된다.
[general] 섹션에서는 기본적인 프로토콜 연결 방식과 Default Parameter 들을 설정할 수 있다.

context=unauthenticated         ; default context for incoming calls

위의 예제에서는 default context 로 unauthenticated 를 설정했다. 이것은, 별도로 context를 설정하지 않은 sip 채널들에 대해서는 unauthenticated 방식으로 처리하겠다는 뜻이다.
참고로 unauthenticated 에 관한 설정 내용은 /etc/asterisk/extension.conf 에 설정할 되어있어야 한다.

allowguest=no                   ; disable unauthenticated calls

Guest call 허용 여부. 명시적으로 계정을 가진 사람들만 통화가 가능하도록 할지 말지에 관한 옵션이다. No 로 설정시, sip.conf 에 등록된 계정만 통화가 가능하게 된다.

srvlookup=no                    ; disable DNS SRV record lookup on outbound calls
;   (unless you have a reliable DNS connection,
;   in which case yes)

DNS lookup 설정 여부

udpbindaddr=0.0.0.0             ; listen for UDP requests on all interfaces

NIC 가 여러개 있을 경우, 어느 IP 주소로 Listen 소켓을 열어둘지를 설정하는 옵션. 0.0.0.0 으로 설정할 경우, 사용가능한 모든 IP 주소로 Listen Port 를 열어놓는다.
Asterisk 의 NIC 사용방식은 all-or-one 방식이다. 즉, 하나를 사용하거나 모두를 사용하거나 이다.
예를 들어, 만약 NIC가 3개가 있을 경우 이중 2개만 사용할 수는 없다는 것이다.

tcpenable=no                    ; disable TCP support

TCP 사용 여부를 설정한다.

[office-phone](!) 섹션

 [office-phone](!)       ; create a template for our devices

템플릿 섹션 설정. [office-phone] 뒤에 붙은 (!) 표시는 이 섹션은 템플릿로 사용된다는 것을 가리킨다. 템플릿으로 만들어진 섹션은 추후 device 추가시 적용하여 사용가능 하다.

type=friend             ;   the channel driver will match on username first,
;   IP second

peer, user, friend 로 설정가능하다. 각각의 의미는 다음과 같다.
peer : Source IP 와 Port number 로 요청을 확인한다.
user : SIP Request 에서 From header 안의 username 으로 요청을 확인한다. sip.conf 에서 설정된 내용중에서 일치하는 device 정보를 찾는다.
friend : peer/user 두가지 방식 모두 사용해서 요청을 확인한다.

host=dynamic            ; the device will register with asterisk

연결되는 SIP 장비의 IP 주소를 설정한다. dynamic 또는 직접 IP 주소 입력 방식이 있는데, 만약 SIP 단말기가 고정 IP 가 아니라면 dynamic 으로 설정해야 한다.

 nat=force_rport,comedia ; assume device is behind NAT
;   *** NAT stands for Network Address Translation,
;   which allows multiple internal devices to share an
;   external IP address.

단말장치가 NAT 안쪽에 있는 경우 설정한다.

dtmfmode=auto   ; accept touch-tone from the devices, negotiated
; automatically

Asterisk 가 DTMF(touch-tone)을 보낼 때, 어떤 형식의 Dial tone을 보낼지 설정한다. info, inband, rfc2833, auto 중 한가지를 설정할 수 있다. info 설정은 SIP INFO method 를 사용하고, inband는 inband audio tone 을 전달한다. 그리고 rfc2833은 RFC-2833 에서 제정된 out-of-band method 를 전달한다.

disallow=all    ; reset which voice codec this device will accept or offer
allow=g722      ; audio codecs to accept from, and request to, the device
allow=ulaw      ; in the order we prefer
allow=alaw

사용/불가능 audio codec 을 지정한다. 제일 첫라인 disallow=all  은 general 에서 이미 설정된 코덱 내용들을 모두 버린다른 뜻이며, 바로 아랫 줄 부터 어떤 코덱들을 사용할 것인지 하나씩 설정한다.
ulaw의 경우, Canada 와 US를 제외한 거의 대부분의 국가에서 사용하며, alaw 는 주로 Canada 와 US 에서 사용한다.

[0000FFFF0003](office-phone) 섹션

[0000FFFF0003](office-phone)

장비 이름(username) 0000FFFF0003 과 office-phone 템플릿을 사용한다고 선언한 부분이다.

secret=helloworld     ; DON’T USE THE PASSWORD WE’VE USED IN THIS EXAMPLE!

password 를 설정한다.