SMS из Bash или учим Zabbix новым трюкам

    Данная статья посвящена организации СМС оповещения в очень бюджетном исполнении.
    Такой метод подходит для домашнего использования или использования в SOHO. На что-то большее данная схема не способна, имейте это ввиду.
    Ранее на Хабре уже были статьи на тему СМС информирования, но все сводилось к локальным USB-модемам или сервисам email2sms.
    В этой статье будет рассмотрена иная схема взаимодействия. А именно: оборудование Mikrotik выступит в роли GSM шлюза, а Zabbix будет отправлять СМС через терминал.

    Что понадобится:
    1) Mikrotik 951 серии (активный USB-хаб крайне рекомендуется)
    2) USB-модем с сим-картой
    3) и развернутый Zabbix-сервер.

    А работает это все согласно RFC2217.

    Вся конфигурация разбита на 3 этапа:
    A) Настройка Mikrotik
    B) Работа со скриптом отправки
    C) Настройка Zabbix

    Наcтройка Mikrotik


    К Mikrotik'у подключен USB-модем, произведены базовая настройка и проверка работоспособности модема.
    Сразу выясните каналы для отправки СМС. В моем случае это было сделано экспериментально, каналы 1 и 2 отвечают за эту функцию.

    В документации к ROS была найдена функция проброса COM-порта по TCP (RFC2217). Она позволяет обращаться к оборудованию за роутером через обычный терминал.
    Настройка в Winbox
    System -> Ports -> Remote Access


    Все параметры интуитивно понятны. Каналы данных SMS Settings и Remote Port совпадать не должны!
    Со стороны сетевого оборудования на этом настройка окончена.

    Скрипт отправки СМС


    В качестве гостевой системы ВМ в моем случае выступает Ubuntu 14.04.2. Так исторически сложилось, с этим приходится жить.
    Вы же можете использовать как «железный» Zabbix, так и виртуальный.

    Путем чтения тонны инструкций был написан скрипт отправки СМС на Bash сначала в текстовом формате, а следом и в формате PDU. PDU-формат позволяет отправлять СМС в Юникоде, т.е. латинские и кириллические символы (в данном случае только они нас интересуют).
    Окончательный вариант скрипта, прилагаемый здесь, позволяет отправлять «многостраничные» СМС любого содержания, т.е. более 70 символов.
    Для тех, кто хочет проникнуться, я оставлю несколько ссылок: тык и тык
    Скрипт на Bash'е
    Для работы скрипта требуется утилита Recode.
    В случае, если вы хотите проверить работу скрипта из терминала, уберите "-e" у «echo».

    #!/bin/bash
    #Переменные
    tel=$1
    header=$2
    mes=$3
    ip=XXX.XXX.XXX.XXX                                          #IP шлюза
    port=Y                                                 #Порт шлюза
    
    #Служебные переменные !!! НЕ ТРОГАТЬ !!!
    TP_MR0=0                                                #Начальный параметр TP-MR
    IED31=1                                                 #Начальный параметр IED3 для блока UDH
    
    #Начинается...
    ###########################################################################################################################################
    #Переменные для обоих блоков
    
    #Вычисление длины сообщения для определения использовать ли UDH
    UDH=`echo $mes | recode ..U2/x2`                        #Преобразование тела сообщения в UCS2
    UDH=`echo $UDH | sed 's/0x\|,\| //g' | sed 's/000A$//g'`
    TP_UD=$UDH                                              #TP-UD - кодированное сообщение
    UDH=`echo -n $UDH | wc -c | gawk '{print $1}'`
    UDH=$(($UDH/4))                                         #Подсчет символов в сообщении - определяет какой типа отправки использовать
    
    #Кодировка номера в нужный формат
    tel="$tel"F""
    i=`echo -n $tel | wc -c | gawk '{print $1}'`
    i=$(($i/2))
    
    while [ "$i" != "0" ]
    do
    R=`echo $tel | cut --complement -b '3-12' | rev`
    TPTEL="$TPTEL$R"
    tel=`echo $tel | cut --complement -b '1-2'`
    i=$(($i-1))
    done
    
    ###########################################################################################################################################
    #Если символов 70 и менее!
    if [ "$UDH" -le "70" ]; then
    
    #Кодировка сообщения и длины сообщения в UCS2
    TP_UDL=`echo -n $TP_UD | wc -c | gawk '{print $1}'`     #Вычисление длины сообщения в шестнадцатеричный формат вида XX
    TP_UDL=$((($TP_UDL+1)/2))
    TP_UDL=`printf '%02x' $TP_UDL | sed 's/[[:lower:]]/\u&/g'`      #Тут еще в верхний регистр загоняем для красоты
    
    
    #Собираем строку для >
    TPDU=""0011000B91"$TPTEL"0008AA"$TP_UDL$TP_UD"          #Собираем всю строку для >
    
    #Подсчет байтов для AT+CMGS=
    Byte=`echo -n $TPDU | cut --complement -b '1-2'`        #Убираем первые 2 символов, они не участвуют в подсчете
    Byte=`echo -n $Byte | wc -c | gawk '{print $1}'`
    Byte=$((($Byte)/2))
    
    #Сама процедура отправки на шлюз. Вроде как поддерживается и RFC2217, и RAW
    (
    sleep 2
    echo  "AT+CMGF=0"                                       #1 - Текстовый режим, 0 - PDU режим чтоб он сгорел!!!
    sleep 1
    echo "AT+CSCS=\"UCS2\""                                 #Кодировка
    sleep 1
    echo "AT+CMGS=$Byte"                                    #Передача байта в десятичном виде
    sleep 1
    echo -e "$TPDU\\032"                                       #Передача закодированной строки + Ctrl+Z
    sleep 3                                                 #Спим долго, отчет идет долго
    echo -e "\\033"                                            #ESC на всякий случай, чтоб модем не завис в случае ошибки
    sleep 3
    ) | telnet $ip $port                                    #Telnet на шлюз, параметры в самом верху
    exit 0
    
    ##################################################################################################################################
    #Если символов более 70!
    else
    
    #Тут временные переменные, нужны для цилка
    UDH=$((($UDH/67)+1))                                    #Превращаем UDH в количество циклов (на 1 больше, чем полных СМС по 67 символов)
    IED2=$UDH                                               #Посчитаем количество частей СМСок - параметр для UDH
    IED2=`printf '%02x' $IED2 | sed 's/[[:lower:]]/\u&/g'`
    
    #Сама процедура отправки на шлюз. Вроде как поддерживается и RFC2217, и RAW
    (
    sleep 2
    echo  "AT+CMGF=0"                                       #1 - Текстовый режим, 0 - PDU режим чтоб он сгорел!!!
    sleep 1
    echo "AT+CSCS=\"UCS2\""                                 #Кодировка
    sleep 1
    
    #Цикл отправки сообщений AT+CMGS=
    while [ $UDH -ne 0 ];
    do
    
    #Кодировка сообщения и длины сообщения в UCS2
    TPUD=`echo -n $TP_UD | cut --complement -b '269-100000000'`     #Отрезаем первые 67 символов для кодирования одного СМС
    TP_UD=`echo -n $TP_UD | cut --complement -b '1-268'`    #Оставшееся сообщение без 67 символов, будет отрезано в следующем цикле
    
    TP_MR=`printf '%02x' $TP_MR0 | sed 's/[[:lower:]]/\u&/g'`       #Преобразуем TP-MR (00, 01 и т.д.)
    IED3=`printf '%02x' $IED31 | sed 's/[[:lower:]]/\u&/g'` #Текущая часть СМС - параметр для UDH
    
    UDH_TP_UD=""050003FF"$IED2$IED3$TPUD"                   #Собираем строку для подсчета длины TP-UDL
    
    TP_UDL=`echo -n $UDH_TP_UD | wc -c | gawk '{print $1}'` #Вычисление длины в шестнадцатеричный формат вида XX
    TP_UDL=$(($TP_UDL/2))
    TP_UDL=`printf '%02x' $TP_UDL | sed 's/[[:lower:]]/\u&/g'`      #Тут еще в верхний регистр загоняем для красоты
    
    TPDU=""0041"$TP_MR"0B91"$TPTEL"0008"$TP_UDL$UDH_TP_UD"  #Собираем всю строку для >
    TP_MR0=$(($TP_MR0+1))                                   #Увеличивает $TP-MR0 на 1 для следующего СМС
    IED31=$(($IED31+1))                                     #Увеличиваем номер следующего СМС для UDH
    UDH=$(($UDH-1))                                         #Уменьшаем номер для следующего цикл на один
    
    #Подсчет байтов для AT+CMGS=
    Byte=`echo -n $TPDU | cut --complement -b '1-2'`        #Убираем первые 2 символов, они не участвуют в подсчете
    Byte=`echo -n $Byte | wc -c | gawk '{print $1}'`
    Byte=$((($Byte)/2))
    
    #Сама отправке нескольких сообщений на шлюз
    echo "AT+CMGS=$Byte"                                    #Передача байта в десятичном виде
    sleep 1
    echo -e "$TPDU\\032"                                       #Передача закодированной строки + Ctrl+Z
    sleep 3                                                 #Спим долго, отчет идет долго
    echo -e "\\033"                                            #ESC на всякий случай, чтоб модем не завис в случае ошибки
    sleep 3
    done
    ) | telnet $ip $port                                    #Telnet на шлюз, параметры в самом верху
    
    fi
    exit 0
    #В случае, если вы хотите проверить работу скрипта из терминала, уберите "-e" у "echo"
    


    Вам в скрипте необходимо изменить две переменные на ваши — IP и Port.
    В скрипт передаются 3 переменные по порядку: номер телефона, заголовок (не используется, просто Zabbix именно в такой последовательности передает данные в скрипт) и само сообщение. Дополнительно переменные экранировать не нужно, Zabbix это делает сам.
    По умолчанию скрипт должен лежать в:
    для версии 2.2 — /usr/local/share/zabbix/alertscripts
    для версии 2.4 — /usr/lib/zabbix/alertscripts.
    Не забывайте дать соответствующие права на файл скрипта!

    Настройка Zabbix


    На стороне Zabbix'а процедура настройки тривиальна, но я опишу ее еще раз для закрепления.
    1) Указываете Способ оповещения
    2) Указываете необходимый телефон в профиль пользователя
    Телефон вводится в формате 11-значном формате, т.е. 7**********
    3) Настраиваете действия на сработавший триггер
    Картинки для закрепления






    Не забывайте, что поле действия «Тема по умолчанию» не учитывается в скрипте, поэтому все необходимое выносите в «Сообщение по умолчанию». Я использую для этого следующую конструкцию: {TRIGGER.NAME} {TRIGGER.DESCRIPTION} {ITEM.NAME} — {ITEM.LASTVALUE}. Она более чем информативна.

    Послесловие


    Как я и предупреждал в начале статьи, все очень примитивно и для «продакшена» не годится. Но метод позволяет за совсем скромные деньги получать чуть больше оперативной информации от вашей системы мониторинга. За сим разрешите откланяться.

    Комментарии 38

      0
      Только слабое место в такой конфигурации — если microtick умрёт, или канал до него отвалится, то zabbix никому и ничего не расскажет.
      Потому, надёжнее всё-таки слать непосредственно с сервера.
      Но и этот вариант интересен для общего развития, спасибо.
        0
        Слабых мест у такой конфигурации куча: от проблем с локальной сетью до занятости модема самим микротиком (мне симулировать пока не удалось).
        Но, как я писал в самом начале, этот метод подходит мелким предприятиям особенно в наше время. Мелкий бизнес и так пытается любыми средствами сэкономить, не забывая конечно о хоть каком-то мониторинге и какой-то автоматизации.
        +1
        Что автор думает о gammu? А о Kannel?
          0
          Думает, что это отличные инструменты. Насколько мне известно, они не умеют работать по RFC2217, но все решается использованием ttyd.
          Такую связку я не тестировал.
            0
            А нет ttyd для 14.04!
            Если кто-нибудь знает утилиту аналогичную ttyd, буду очень признателен.
          –1
          А чего бы просто sms сервис не использовать? Тот же sms.ru. И проще и дешевле.
            +1
            Вероятность недоступности внешнего сервиса всё же куда выше, чем даже такой конфигурации.
            Но ещё лучше подключить модем к серверу с заббиксом…
              0
              Я не использовал этот сервис, как у него с оперативностью? В моем случае СМС доходит в несколько секунд после отправки.
              Ну и конечно же момент с отвалом канала связи. В случае со свистком, сам свисток можно научить поднимать свое подключение.
                +1
                Можно действительно поднять через свисток отдельный канал только для отправки SMS.
                Приходят обычно за те же несколько секунд.
                  0
                  Можно, но это уже какое-то… нагромождение что ли.
                    0
                    По-моему, лучше, чем AT-команды из bash-скрипта. ;)
                0
                Угу, пользовались. но:
                1. Внезапно сообщения перестали приходить на часть операторов (хотя деньги исправно списывались )
                2. Техподдержка стала не адекватной — на сайте не отвечают, а звонить на московский номер с 10 до 17 МСК как-то не очень удобно
                3. ОПСОСЫ спохватились и ну давай повышать тарифы на такую отправку (по ценам 2000-х)

                Пока перешли на другого «провайдера», но вообще перейдём на отправку через модем/android-смартфон.
                  0
                  Причём на столько дешевле, что аж бесплатно. Если конечно отправлять уведомления только себе. Хотя можно хранить табличку сопоставления номера телефона получателя и его api-key с сайта.

                  Но почему не воткнуть модем в сам сервере и не подключить модем локально? Если сервер находится удалённо, то при потери канала связи он и до модема по сети не достучится. В таком случае для мониторинга доступности сервера мониторинга можно воспользоваться услугами стороннего сервиса мониторинга доступности.
                    0
                    Если вопрос адресован мне, то метод статьи рассчитан на совместное использование модема или Заббикс, размещенной в ВМ.
                  0
                  Микротик же сам умеет смс отсылать, а кроме того у него есть API.
                  Может попробовать через API микротика рассылать?
                    0
                    Проблем у микротика 2: только текстовый режим отправки СМС, а значит о кириллице можно забыть, и ограничение в 1 СМС (160 латинских символов). У скрипта таких ограничений нет.
                      0
                      Не подскажете, какой именно модуль для этого нужен?
                      0
                      Хм, а пробросить можно вообще любой USB-COM конвертер? было бы неплохо «подцепить» что-то типа такого:
                      (4 реле 5/220 плюс входные/выходные линии TTL)
                      image
                      kernelchip.ru/Ke-USB24R.php
                      и перезагружать оборудование в серверной.
                      (Сам-то микротик вряд-ли создаст модель с одним-двумя 220В реле)
                        0
                        По идее, если микротик увидит оборудование, то и его можно пробросить.
                        И так же по идее с этим оборудованием можно будет работать через скрипты самого микротика (в вашем случае перезагрузка по СМС и т.д.)
                        Нужно пробовать, только такой ответ я могу дать.
                          0
                          Я понимаю, надо пробовать втыкать оборудование. Жаль, что если какой-то чип-конвертер не заработает, то с этим ничего не сделать — свои модули к закрытой RouterOS ведь не написать?

                          А как принять СМС-на микротике? Вычитывать из порта входящие СМС? Но ведь ка каждого Huawei/Zte и проч. свой формат?
                            0
                            Так глубоко я в дебри ROS не лез, скорее всего там ловить нечего.

                            А по поводу приему СМС — вот информация на их wiki. Прием придется включать после каждой перезагрузки микротика, но это легко решается скриптом на старте.
                        0
                        или учим Zabbix новым трюкам

                        вы серьезно? такой подход на моей памяти уже используется не меньше 10 лет, не новый он совсем
                        но за реализацию на баше, конечно, спасибо
                        по скрипту
                        это
                        UDH=`echo $UDH | sed 's/0x//g' | sed 's/,//g' | sed 's/ //g' | sed 's/000A$//g'`
                        

                        можно заменить на
                        UDH=`echo $UDH | sed 's/0x\|,\| //g' `
                        

                          0
                          Да, заголовок не ахти, признаю.
                          В сети я встречал только схему с GSM-шлюзом, работало это все так же через терминал. Но реализовано было на PHP и быстро под мои нужды заточить не удалось. Только вот не помню, был ли там PDU с UDH или нет.

                          Сейчас проверить не могу, но вполне возможно, что там хватит
                          sed 's/, 0x//g'

                          Перепроверю и отпишусь ниже.
                            0
                            в любом случае спасибо за интересную статью )
                              0
                              Внес в скрипт вашу поправку, большое спасибо за комментарий!
                          0
                          Спасибо за скрипт — моя реализация через API работает, но кирилица — это неплохо!
                          Но не подскажете, почему скрипт не отрабатывает?
                          root@zabbix:~/alertscripts# ./sms_all 7********** "" "123"
                          ./sms_all: line 18: recode: command not found
                          Trying 192.168.*.*...
                          Connected to 192.168.*.*.
                          Escape character is '^]'.
                          AT+CMGF=0
                          OK
                          AT+CSCS="UCS2"
                          OK
                          AT+CMGS=14
                          > 0011000B919732155403F30008AA00
                          +CMGS: 103
                          
                          OK
                          onnection closed by foreign host.
                          

                          Настройки поправлены на свой адрес и порт, что видно из лога, но обрывается. На микротике тихо, или нет?
                          echo: sertcp,info connection to serial remote-access 0.0.0.0:24 from 192.168.*.*:38899
                          echo: sertcp,debug end of file reading TCP port 24
                          


                          НОВОЕ: может от того что тестирую в терминале, не убрав -е?
                            0
                            У вас все отрабатывается на ура, +CMGS показывает номер текущего удачно отправленного сообщения.
                            А вот Recode вы не поставили. Ну и "-e" нужно убрать, иначе это "-е" полезет в строку >.
                              0
                              Хм, может я не достаточно внимательно прочел статью, но о каком Recode речь?
                              И может оно и отрабатывает на ура :) но смс мне не пришли, хотя через API прекрасно ходят, ровно как и через winbox если отправить.

                              НОВОЕ: и ведь невнимательно! Спасибо, что тыкнули носом — незаметная строка оказалась, Ctrl+F рулит )
                                0
                                Выделил жирным для «внимательных» :)
                                  0
                                  Возник вопрос, как сделать перенос строки, если через командную строку?
                                    0
                                    Поясните. Пытаетесь из винды сделать?
                                      0
                                      Нет, в терминале убунты, тестирую с русскими символами — всё ОК, интересно как перенести строку в сообщении
                                      ./sms_all 7******** "" "пошли<?>домой"
                                      

                                      Каой символ вводить, чтобы сработал перенос каретки?
                                        0
                                        Попробуйте
                                        echo -e "пошли\r\nдомой" | xargs ./sms_all 7******** ""
                                          0
                                          Пришло только слово «пошли». Я читал, что для смс есть спец-символ для переноса, здесь.
                                          Это веб-символ, аналог, по-моему, 0x0A, прочитано тут.
                                          Может это как-то в скрипте обрабатывать?
                                            0
                                            Это делается так:
                                            "bla-bla"\\n"bla-bla"
                                            


                                            Из Заббикса отправку сейчас проверю.
                                              0
                                              Не подходит. Через командную строку приходит
                                              ./sms_all 7********** "" "пошли\\nдомой"
                                              
                                              пошли\nдомой
                                              


                                              echo -e "пошли"\\n"домой" | xargs ./sms_all 7******** ""
                                              
                                              пошли
                                              
                                                0
                                                Ладно, напишу полностью
                                                ./sms_all 7********** "" "пошли"\\n"домой"
                                                

                                                Из Заббикса такая конструкция не работает. Но оно там и не надо :)
                                                  0
                                                  Обратно не работает
                                                  пошли\nдомой
                                                  

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое