Некоторое время назад был рассмотрен вопрос использования модема 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 и используют весьма примитивный протокол, к серверной части можно подключить многие другие программные продукты с минимальной доработкой.
На этом всё. Приятной работы!

Anonymous 2011-09-13 22:50:32 (#)
Так же можно через scp класть файл в папку /var/spool/sms/outgoing/ на сервере.
С авторизацией по ключам.