Отправляет email-рассылки с помощью сервиса Sendsay

FreeBSD: полезные заметки

  Все выпуски  

FreeBSD: полезные заметки. Работа с USB-модемом (запрос баланса, смс и т.д.).


Сегодня, друзья, возникла такая потребность, как узнать баланс на своём USB-модеме, с которого я подключаюсь к ОАО "Мегафон". Не являясь счастливым обладателем MS Windows с родными драйверами или Ubuntu Linux (с wvdial & vodafone mobile), пришлось с этим вопросом разбираться дополнительно.

Сразу предупрежу, что мой любимый Huawei E220 ответов по запросу баланса не прислал, несмотря на все мои ухищрения (в т.ч. stty -f /dev/ttyU0.1.init raw clocal). Но все остальные Huawei - а их было достаточное количество, в т.ч. E173, E1820 - заработали.

Часть первая. Баланс.

Для начала, нам требуется

$ sudo kldstat -v | grep 'u[pl]*com'
392 uhub/uplcom

что в принципе, на FreeBSD-9.0 RC3 уже присутствует, впрочем, можно наверное и по-другому:

$ sudo kldload ucom.ko
kldload: can't load ucom.ko: File exists
[uglev@localhost /usr/ports]$ sudo kldload uplcom.ko
kldload: can't load uplcom.ko: File exists

(у меня, понятное дело, модули уже подгружены).

Как правило, модем создаёт в /dev несколько устройств:

$ ls /dev | grep cuaU && ls /dev | grep ttyU
cuaU0.0
cuaU0.0.init
cuaU0.0.lock
cuaU0.1
cuaU0.1.init
cuaU0.1.lock
cuaU0.2
cuaU0.2.init
cuaU0.2.lock
cuaU0.3
cuaU0.3.init
cuaU0.3.lock
ttyU0.0
ttyU0.0.init
ttyU0.0.lock
ttyU0.1
ttyU0.1.init
ttyU0.1.lock
ttyU0.2
ttyU0.2.init
ttyU0.2.lock
ttyU0.3
ttyU0.3.init
ttyU0.3.lock

Нас интересуют файлы cuaU0.*. Нам необходимо выяснить, через какой из них мы будем отсылать запросы оператору.
Сразу предупрежу, что большинство модемов используют PDU-encoded USSD messages, поэтому зайдём на сайт http://smstools3.kekekasvi.com/topic.php?id=288 и узнаем новую форму нашего запроса. В поле "PDU SMS message creator" -> "Text:" вводим свой код (например у меня код баланса - *100#) и нажимаем на "Convert >".
У меня получилось: AA180C3602.
Таким образом, в дальнейшем мы будем вводить фразу: at+cusd=1,"AA180C3602",15.

Устанавливаем из портов sudo (/usr/ports/security/sudo) и screen (/usr/ports/sysutils/screen) — так нам удобней. Проверяем последовательно все устройства cuaU0.* в разных окнах терминала:

$ sudo screen /dev/cuaU0.0
$ sudo screen /dev/cuaU0.1
$ sudo screen /dev/cuaU0.2
$ sudo screen /dev/cuaU0.3

В одном из окон обнаруживаем диагностические сообщения:

^BOOT:32965268,0,0,0,99

^RSSI:0

^CSNR:-111,-13

^RSSI:4

^CSNR:-104,-10

Всё, это оно и есть! Вставляем туда нашу команду и получаем:

at+cusd=1,"AA180C3602",15

+CUSD: 0,"0031003400350034002E003000360440002E00200420043004370432043
B0435043A04300439044204350441044C0020043F043E002D043204370440
043E0441043B043E043C044300210020002A003500300036002A003200230
02000200028003100300020044004430431002F044104430442043A043800
29",72

Всю эту строку, только без +CUSD: 0, в начале, вводим в поле "USSD Entry/Display" (чекбокс "UCS2"), и читаем чуть ниже сообщение о балансе. Ура!

Часть вторая. Отсылка смс.

Для отсылки смс нам потребуется порт /usr/ports/comms/smstools3 (не забудьте в /etc/rc.conf добавить smsd_enable="YES"). Однако, требуется его дополнительная настройка.

Заводим группу smsd и юзера smsd, включённого в эту группу, а также группы dialer и uucp. А также включить и себя в эти группы (sudo pw groupmod dialer -m user).

$ pw groupadd smsd -g 151
$ pw useradd -n smsd -u 151 -c 'SMS Gate worker' -g smsd \
? -G uucp,dialer -w no -s /sbin/nologin -d /nonexistent

Сразу предупрежу, что эти данные я брал из старой статьи. Может быть, это и не потребуется, так как всё равно мы будем допиливать одну утилиту, связанную с отправкой смс. Но — что сделано, то сделано.

ee /usr/local/etc/smsd.conf:

# Example smsd.conf. Read the manual for a description

checkhandler=/home/user/ucsautoconvert

devices = GSM1
#logfile = /var/log/smsd/smsd.log
loglevel = 5

USER = smsd
GROUP = dialer

[GSM1]
device = /dev/cuaU0.3
incoming = yes
#pin = 1111

Этого пока хватит, хотя по мануалу есть ещё около десятка строк. Не забываем создать и /home/user/ucsautoconvert (скрипт делаем исполняемым!):

#!/bin/bash
status="$1"
file="$2"

case "$1" in
RECEIVED)
FILE=`mktemp /tmp/smsd_XXXXXX`

head -12 $file | grep -e "^From: " -e "^Sent: " -e "^Received: " >> /var/tmp/sms.log
if grep "Alphabet: UCS2" $file >/dev/null; then
tail +13 $file | iconv -f UCS-2 -t UTF-8 >> /var/tmp/sms.log
else
tail +13 $file >> /var/tmp/sms.log
fi
echo >> /var/tmp/sms.log
echo >> /var/tmp/sms.log
;;
esac

Это необходимо для русификации. Впрочем, есть и ещё один способ, без создания /var/tmp/sms.log, описанный здесь. Но он у меня почему-то не заработал.

Добавляем в /etc/syslog.conf

!+smsd,GSM1
*.*

И в /etc/newsyslog.conf

/var/log/smsd/smsd.log smsd:dialer 644 7 100 * JC

У меня появилась проблема с правами:

2011-12-26 00:37:37,3, smsd: Cannot handle /var/spool/sms/outgoing/send_NbenhU: Access denied. Check the file and direсtory

Правим /usr/local/bin/sendsms. Вместо строчки chown $owner $TMPFILE указываем (так как проблему с правами я решить не смог):

# chown $owner $TMPFILE
chown smsd:dialer $TMPFILE
chmod 777 $TMPFILE

В дальнейшем для отслеживания лога и настройки программы удобно сделать:

$ sudo tail -f /var/log/smsd/smsd.log

Запускаем!

$ sudo /usr/local/etc/rc.d/smsd restart

$ sudo sendsms +7921xxxxxxx "Hello, world"

В дальнейшем фразу "Hello, world" заменяем на "Привет, мир" и ещё раз проверяем корректность отображения русских букв.

Часть третья. Автоматизация.

Устанавливаем порт /usr/ports/comms/p5-Device-Gsm (ну и разумеется, Perl), и пишем скрипт (с правами на исполнение).

#!/usr/bin/perl

use Getopt::Std;
use Device::Gsm::Pdu;

# defaults
$opt_r = "/dev/cuaU0.3";
$opt_s = "/dev/cuaU0.3";

my $USAGE = <<__EOU;

Usage: $0 [-i input_port] [-o output_port] [-n] [-h] [-v] ussd_msg

Description:
Send and receive 7-bit PDU-encoded USSD messages.
Written and tested for Huawei E1550 GSM/UMTS USB modem.

Options:
-r port Port to receive data from. Default: $opt_r
-s port Port to send AT commands to. Default: $opt_s
-n Do not send any data to port. Useful with -v.
-h Print this help.
-v Be verbose.
__EOU

sub HELP_MESSAGE {print "$USAGE\n"; exit;}
sub VERSION_MESSAGE {};
getopts ('i:o:hnv');
HELP_MESSAGE() and exit if (! $ARGV[0]) or defined($opt_h);

print "USSD MSG: $ARGV[0]\n" if $opt_v;
my $ussd_req = Device::Gsm::Pdu::encode_text7($ARGV[0]);
$ussd_req =~ s/^..//;
print "PDU ENCODED: $ussd_req\n" if $opt_v;

my $ussd_reply;
if (! $opt_n) {
open (SENDPORT, '+<', $opt_s) or die "Can't open '$opt_s': $!\n";
print SENDPORT 'AT+CUSD=1,',$ussd_req,",15\r\n";
close SENDPORT;
open (RCVPORT, $opt_r) or die "Can't open '$opt_r': $!\n";
print "Waiting for USSD reply...\n" if $opt_v;
while () {
chomp;
die "USSD ERROR\n" if $_ eq "+CUSD: 2";
if (/^\+CUSD: 0,\"([A-F0-9]+)\"/) {
$ussd_reply = $1;
print "PDU USSD REPLY: $ussd_reply\n" if $opt_v;
last;
}
print "Got unknown USSD message: $_\n" if /^\+CUSD:/ and $opt_v;
}
}

if ($ussd_reply) {
$decoded_ussd_reply = Device::Gsm::Pdu::decode_text7('00'.$ussd_reply);
print STDOUT "USSD REPLY: $decoded_ussd_reply\n";
}
else {print "No USSD reply!\n";}

И проверяем, как работает:

$ ./script *100#

Если лень вбивать команды в текстовой строке, пишем небольшой wrapper по мануалу:

#/bin/sh

mnt="/media/HUAWEI"
ussd_cmd="${HOME}/bin/ussd"

op="$1"
logpath="~/"
if [ -d "${mnt}" ]; then
logpath="${mnt}"
fi

case $op in
utel)
cmd="*100# *121#"
;;
megafon)
cmd="*100# *115*1*1#"
;;
*)
cmd="*101#"
;;
esac

ussd () {
if [ -w /dev/cuaU0.0 -a -r /dev/cuaU0.3 ]; then
date=`date`
ussd=`${ussd_cmd} "${1}"`
if [ ! -z "$ussd" ]; then
echo "${date}: ${op} ${1} ${ussd}" | tee -a ${logpath}/gsm_balance.log
fi
fi
}


for i in ${cmd}
do
ussd "$i"
done

Часть четвёртая. Благодарности.

Ну и разумеется, не могу не дать ссылки на статьи и утилиты, которые мне весьма и весьма пригодились в процессе написания статьи:

http://smstools3.kekekasvi.com/topic.php?id=288
http://www.lissyara.su/articles/freebsd/programms/smstools_3/
http://theapplegeek.ru/archives/5253
http://metal-solid.livejournal.com/7408.html
http://yuri-kurenkov.livejournal.com/127118.html

В избранное