Организация доступа к GSM-модему по сети

()

Некоторое время назад был рассмотрен вопрос использования модема SENSE GM02 в частности для отправки SMS. Сейчас немного усложним задачу: попробуем "расшарить" модем по сети, чтобы его можно было использовать одновременно с нескольких компьютеров.

Зачем это нужно? Ну например если есть несколько серверов мониторинга, каждый из которых должен иметь возможность отправлять SMS, а модем у нас только один и подключен к одному из серверов.

Немного конкретизируем задачу: есть Zabbix-сервер, работающий под управлением FreeBSD и сервер под управлением Ubuntu 11.04 с подключенным к нему модемом и имеющий IP-адрес 1.1.1.1.

Приступаем к решению задачи. Общая идея заключается в том что zabbix-сервер для отправки оповещений будет использовать внешний скрипт, который будет по сети передавать данные серверу и тот уже будет отправлять SMS. Автору этих строк не удалось найти готовых решений и потому пришлось создавать своё.

Начнём работу с создания сервера. Полноценную работу с GSM-модемом делать долго, потому в качестве промежуточного звена используем smstools. Установим их:

apt-get install smstools

Приведём файл /etc/smsd.conf к следующему виду:

devices = GSM1
outgoing = /var/spool/sms/outgoing
checked = /var/spool/sms/checked
incoming = /var/spool/sms/incoming
logfile = /var/log/smstools/smsd.log
infofile = /var/run/smstools/smsd.working
pidfile = /var/run/smstools/smsd.pid
outgoing = /var/spool/sms/outgoing
checked = /var/spool/sms/checked
failed = /var/spool/sms/failed
incoming = /var/spool/sms/incoming
sent = /var/spool/sms/sent
stats = /var/log/smstools/smsd_stats
receive_before_send = no
autosplit = 3



[GSM1]
device = /dev/ttyACM0
incoming = yes
baudrate = 19200
decode_unicode_text = yes

Перезапускаем сервис:

invoke-rc.d smstools restart

Теперь для отправки SMS достаточно создать файл определённого формата в директории "/var/spool/sms/outgoing", чем мы и воспользуемся при создании сервера. Описание формата есть в документации smstools.

Собственно код сервера (на Perl):

#!/usr/bin/perl

use strict;
use warnings;
use diagnostics;

use POSIX;

use IO::Socket;
use XML::Simple;
use Text::Iconv;

use Data::Dumper;

# Права, с которыми будет работать скрипт
# Важно понимать что файлы он так же будет создавать с этими правами
# Так что эти настройки должны учитывать аналогичные настройки для smsd
my $settings = {
    'user' => "smsd",
    'group' => "smsd",
    'port' => 32767,
    'outgoing_dir' => '/var/spool/sms/outgoing/',
    'logfile' => '/var/log/smstools/zabbix_smsd.log',
};


# Резолвим идентификаторы пользователя и группы
my $d_uid = getpwnam($settings->{'user'});
my $d_gid = getgrnam($settings->{'group'});

# Урезаем права
setuid($d_uid);
setgid($d_gid);

# Создаём сокет
my $sock = new IO::Socket::INET (
    LocalPort => $settings->{'port'},
    Proto => 'tcp',
    Listen => 1,
    Reuse => 1,
);

# Обрабатываем входящие соединения
while (my $client = $sock->accept()) {
    # IP-адрес клиента
    my $client_ip = $client->peerhost;
    # Считываем данные с клиента
    my $xml_data = "";
    while (<$client>) {
        $xml_data .= $_;
    }

    # Пытаемся распарсить данные
    eval {
        my $data = XMLin($xml_data);

        # Если данные указаны
        if (defined($data->{'number'}) && defined($data->{'message'})) {
            # Имя файла для записи SMS
            my $uniq_name = time();
            my $sms_file_name = $settings->{'outgoing_dir'} . $uniq_name;

            # Перекодируем сообщение
            my $converter = Text::Iconv->new('UTF-8', 'UCS-2BE');
            my $message = $converter->convert($data->{'message'});

            # Извлекаем номер
            my $number = $data->{'number'};

            # Формируем SMS-сообщение
            my $sms_data = "To: $number\nAlphabet: UCS2\n\n$message";

            # Печатаем сообщение в файл
            open SMSFILE, '>', $sms_file_name;
            print SMSFILE $sms_data;
            close SMSFILE;

            # Формируем сообщение для записи в лог
            my $logtime = strftime("%Y-%m-%d %H:%M:%S", localtime());
            my $logline = "$logtime $client_ip $number $uniq_name\n";

            # Пишем в лог
            open LOGFILE, '>>', $settings->{'logfile'};
            flock LOGFILE, 2;
            print LOGFILE $logline;
            close LOGFILE;
        }
    };

    # Если произошла ошибка то не расстраиваемся
    if (my $error = $@) {
    }
}

# Закрываем сокет
close($sock);

Сохраним скрипт под именем "/usr/local/scripts/smsd.pl" и запустим его командой:

/usr/local/scripts/smsd.pl &

Эту же команду нужно добавить в файл "/etc/rc.local" для автоматического запуска сервера при загрузке. Поскольку скрипт не содержит средств авторизации нужно ограничить с помощью файрволла доступ к порту, который прослушивает сервер.

Переходим к написанию клиента. Поскольку в первую очередь он будет "заточен" для использования совместно с zabbix он должен принимать параметры командной строки в совместимом с zabbix-формате. А именно:

ПараметрЗначение
1Адресат
2Тема сообщения
3Тело сообщения

Поскольку SMS характеризуется только адресатом (номер телефона) и телом сообщения - тему мы будем просто отбрасывать. Листинг клиента:

#!/usr/bin/perl

use strict;
use warnings;
use warnings;

use IO::Socket;
use XML::Simple;

use Data::Dumper;


# Настройки
my $settings = {
    'host' => '1.1.1.1',
    'port' => 32767,
};

# Если количество аргументов не равняется трём:
exit 1 if @ARGV != 3;

# Извлекаем данные
my $number = shift @ARGV;
my $subject = shift @ARGV;
my $body = shift @ARGV;

# Создаём структуру данных
my $data = {
    'number' => $number,
    'message' => $body,
};

# Создаём строку данных
my $data_line = XMLout(
    $data,
    'RootName' => 'xml',
    'NoAttr' => 1
);


# Создаём сокет
my $sock = new IO::Socket::INET (
    PeerAddr => $settings->{'host'},
    PeerPort => $settings->{'port'},
    Proto => 'tcp',
);

# Пишем строку данных в сокет
print $sock $data_line;

# Закрываем сокет
close $sock;

Скрипт сохраним на сервере мониторинга под именем "zabbix_smsc.pl" в директорию, указанную параметров "AlertScriptsPath" в файле "/usr/local/etc/zabbix/zabbix_server.conf". Затем заходим в веб-интерфейс zabbix-сервера, выбираем в меню "Администрирование" -> "Способы оповещений" -> "Создать способ оповещения" и заполняем таблица следующим образом:

ПараметрЗначение
ОписаниеRemote SMS
ТипСкрипт
Название скриптаzabbix_smsc.pl

Результат будет выглядеть примерно так:

Теперь можно в настройках пользовательских аккаунтов использовать новый способ оповещений - "Remote SMS".

Разумеется клиентскую часть системы можно использовать и с другими системами мониторинга. А так же, поскольку клиент с сервером обмениваются данными в формате XML и используют весьма примитивный протокол, к серверной части можно подключить многие другие программные продукты с минимальной доработкой.

На этом всё. Приятной работы!

Ключевые слова: perl, zabbix, sms, smstools, sense gm02, gm02, sense, smsd, usb modem.

Подписаться на обновления: RSS-лента Канал в TamTam Telegram канал Канал в ICQ

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

Anonymous 2011-09-13 22:50:32 (#)

Ага.
Так же можно через scp класть файл в папку /var/spool/sms/outgoing/ на сервере.
С авторизацией по ключам.

MooSE 2011-09-14 11:14:39 (#)

Ага.
Так же можно через scp класть файл в папку /var/spool/sms/outgoing/ на сервере.
С авторизацией по ключам.


Через SCP не так гибко:)

Anonymous 2011-11-01 13:55:03 (#)

Спасибо, очень красивое решение.

Anonymous 2017-12-06 16:05:49 (#)

Внесу поправку небольшую:
Если у вас будет множество получателей, то можно столкнуться с ситуацией когда время создания фигурирующее в имени файла может быть идентичным.
Как вариант решения замена строки:
my $uniq_name = time();
На:
my ($s, $usec) = Time::HiRes::gettimeofday;
my $uniq_name = $s . "." . $usec;
Новый комментарий

Жирный текстКурсивный текстПодчёркнутый текстЗачёркнутый текстПрограммный кодСсылкаИзображение




© 2006-2024 Вадим Калинников aka MooSE
Политика конфиденциальности