SMStools i smsd, czyli automat do wysyłania SMS

Spis treści

Pod linux'em jest całe mnóstwo oprogramowania, które może realizować zadanie odbierania i wysyłania wiadomości SMS. Są również narzędzia, dzięki którym cały proces związany z przetwarzaniem SMS'ów można zautomatyzować. Jakiś czas temu opisywałem tego typu funkcjonalność na przykładzie gammu-smsd. Nadal uważam, że jest to przyzwoite narzędzie ale jakby nie patrzeć wymaga ono wielu zależności. Właśnie przez nie gammu-smsd nie nadaje się do zastosowań, gdzie ma się do dyspozycji niewiele miejsca. Niemniej jednak, w przypadku OpenWRT mamy tam możliwość zainstalowania pakietu smstool3 , w którym jest dostępny demon smsd . Tak się też składa, że debian również ma swoich repozytoriach to narzędzie posiada, z tym, że w pakiecie smstools. W tym wpisie skonfigurujemy sobie działającą bramkę SMS, która będzie automatycznie odbierać wiadomości SMS i podejmować stosowne działanie w zależności od numeru czy treści otrzymanego komunikatu.

Usługa dla systemd i reguła dla udev'a

Instalujemy zatem pakiet smstools . W nim dostarczany jest min. demon smsd i to on będzie zajmował się przetwarzaniem SMS'ów. Trzeba wspomnieć o fakcie braku natywnej usługi dla systemd. Zatem tego demona trzeba albo odpalać przez skrypt init, albo też można się pokusić o stworzenie pliku usługi. Niżej mamy zawartość, którą musimy umieścić w pliku /etc/systemd/system/smstools.service :

[Unit]
Description=SMS daemon
Documentation=man:smsd(1)
After=network-online.target
Before=multi-user.target
ConditionPathExists=/dev/huawei-E3372-1

[Service]
User=smsd
Group=dialout
Type=forking
ExecStart=/usr/sbin/smsd
PIDFile=/var/run/smstools/smsd.pid

[Install]
WantedBy=multi-user.target

Widoczny wyżej warunek ConditionPathExists można usunąć. Niemniej jednak, przydaje się on w momencie, gdy do komputera nie jest podpięty modem. Standardowo ta nazwa modemu wskazuje na /dev/ttyUSB0 ale można ją zmienić. Tak czy inaczej, jako że demon smsd będzie operował na skryptach, to musimy wziąć pod uwagę jakie operacje w tym skrypcie są wykonywane. Standardowo ten demon pracuje z uprawnieniami użytkownika smsd i grupy dialout . Jeśli jednak nasz skrypt wymaga uprawnień administratora, musimy przepisać powyższe parametry.

Mając z głowy usługę dla systemd, napiszmy także regułę dla udev'a, która będzie startować i stopować demona smsd w zależności od tego czy modem USB został podpięty do portu czy też z niego wyciągnięty. Tworzymy zatem plik /etc/udev/rules.d/80-modem.rules i wrzucamy do niego ten poniższy kod:

ACTION=="add", KERNEL=="ttyUSB?", SUBSYSTEM=="tty", \
    ENV{ID_VENDOR_ID}=="12d1", \
    ENV{ID_MODEL_ID}=="15b6", \
    ENV{ID_USB_INTERFACE_NUM}=="00", \
    SYMLINK+="huawei-E3372-0", \
    ENV{REMOVE_CMD}="/bin/systemctl stop smstools.service" , \
    RUN+="/bin/systemctl start smstools.service"

ACTION=="add", KERNEL=="ttyUSB?", SUBSYSTEM=="tty", \
    ENV{ID_VENDOR_ID}=="12d1", \
    ENV{ID_MODEL_ID}=="15b6", \
    ENV{ID_USB_INTERFACE_NUM}=="01", \
    SYMLINK+="huawei-E3372-1"

ACTION=="add", KERNEL=="ttyUSB?", SUBSYSTEM=="tty", \
    ENV{ID_VENDOR_ID}=="12d1", \
    ENV{ID_MODEL_ID}=="15b6", \
    ENV{ID_USB_INTERFACE_NUM}=="02", \
    SYMLINK+="huawei-E3372-2"

Oczywiście te powyższe reguły tworzą także dowiązania symboliczne dla modemu. Wszystkie użyte w nich opcje zostały dokładnie omówione przy okazji zmiany nazwy modemu, zatem odsyłam do podlinkowanego powyżej artykułu.

Konfiguracja demona smsd

Mamy z głowy uruchamianie demona smsd . Musimy jednak jeszcze skonfigurować to narzędzie. Do tego celu służy plik /etc/smsd.conf . Na dobrą sprawę potrafi on być bardzo obszerny i składać się z całej masy parametrów, przez co nie ma możliwości dokładnie opisać wszystkich możliwych do określenia rzeczy. Poniżej zostanie wykorzystana tylko niewielka część opcji ale wszystkie pozostałe przyjmą wartości domyślne. Jeśli chcemy się zagłębić nieco bardziej w strukturę konfiguracji smsd , to odsyłam do manuala znajdującego się na stronie projektu.

Trzeba mieć na uwadze, że modemy czy telefony są różne i nie wszystkie użyte poniżej parametry znajdą zastosowanie przy każdym możliwym sprzęcie. W tym przypadku mamy do czynienia z modemem Huawei E3372 (E3372s-153) w wersji NON-HiLink. Dla niego plik /etc/smsd.conf wygląda mniej więcej tak (aktualna wersja pliku zawsze na moim github'ie):

devices = GSM1
outgoing = /var/spool/sms/outgoing
checked = /var/spool/sms/checked
incoming = /var/spool/sms/incoming
failed = /var/spool/sms/failed
sent = /var/spool/sms/sent
incoming_utf8 = yes
#logfile = /var/log/smstools/smsd.log
loglevel = 5
delaytime = 15
errorsleeptime = 30
executable_check = yes
eventhandler = /opt/skrypty/lte-modem/lte


[GSM1]
pre_init = yes
#init =
#init2 =
device = /dev/huawei-E3372-1
pin = ignore
baudrate = 115200
check_network = 2
check_memory_method = 2
primary_memory = SM
secondary_memory = SM
secondary_memory_max = 25
memory_start = 1
device_open_retries = -1
keep_open = no

Przede wszystkim, mamy tutaj dwie sekcje: globalną i urządzenia. Globalna sekcja zawiera opcje, które są aplikowane do każdego urządzenia. Natomiast w przypadku sekcji urządzenia (określona za pomocą [GSM1] ) możemy ustawić szereg specyficznych opcji dla modemu czy telefonu. Przy pomocy tej sekcji jesteśmy w stanie także przepisać szereg globalnych opcji.

Na początek omówmy sobie parametry globalne. W devices umieszczamy listę nazw urządzeń. To na podstawie tych nazw są nazywane sekcje urządzeń. W opcjach outgoing , checked , incoming , failed oraz sent mamy określone ścieżki do katalogów, w których będą przechowywane SMS. Jeśli chcemy coś robić z wiadomościami, które do nas przyjdą, np. korzystać z grep , to musimy zatroszczyć się, aby te pliki z formy binarnej przekonwertować do formy tekstowej o kodowaniu UTF-8. Za to właśnie odpowiada opcja incoming_utf8 . Jeśli chcemy by logi szły do syslog'a, a nie tak jak smsd ma w standardzie do pliku, to pomijamy opcję logfile . Warto też sobie dostosować poziom logowania przy pomocy loglevel . W opcji delaytime jest określony przedział czasu w sekundach, przez który demon smsd zostanie uśpiony po wykonaniu wszystkich zdań. Podobnie sprawa wygląda w przypadku parametru errorsleeptime , gdzie demon zostanie uśpiony przez jakiś czas po wystąpieniu błędu. Opcja executable_check sprawdza czy skrypt określony w eventhandler jest w stanie się wykonać (o tym będzie w dalszej części artykułu).

W sekcji urządzenia mamy na początku opcję pre_init , która przesyła polecenia AT do modemu przed wystartowaniem demona smsd . Są to dwa polecenia ATE0 oraz AT+CMEE=1 . W init oraz init2 jesteśmy w stanie określić dodatkowe polecenia AT. Zwykle jednak nie będą nam te pozycji potrzebne. Dalej mamy device i tutaj podajemy interfejs modemu. Jeśli nie chcemy aby smsd zajmował urządzenie dla siebie przez cały czas swojego działania, to możemy ustawić również keep_open . W takim przypadku może się czasem zdarzyć tak, że smsd będzie chciał używać chwilowo niedostępnego modemu. Dlatego też powinniśmy ustawić opcję device_open_retries , która sprawi, że smsd będzie mógł się próbować podłączyć w nieskończoność. Jeśli nie korzystamy z kodu PIN, to najlepiej jest dezaktywować cały mechanizm za niego odpowiedzialny. W baudrate ustawiamy prędkość komunikacji z modemem. Obecnie modemy są w stanie działać poprawnie z 115200 ale czasem trzeba zjechać do 19200 czy nawet 9600 . Następnie mamy opcję check_network , która odpowiada za sprawdzanie czy sieć GSM jest dostępna. Spowalnia to trochę działanie smsd . Dlatego też ustawiona została tutaj wartość 2 , która sprawdza dostępność sieci tylko w przypadku wysyłania wiadomości SMS. Dalej mamy parametr check_memory_method określający polecenia AT, które są wykorzystywane do sprawdzania nowych wiadomości SMS (w tym przypadku CMGD ). Opcje primary_memory , secondary_memory oraz secondary_memory_max odpowiadają za wybór pamięci do sprawdzania pod kątem nowych SMS'ów. Ostatnia opcja, tj. memory_start odpowiada za pozycję, od której zacznie się czytanie wiadomości. Zwykle jest to 1 ale niektóre urządzenia wymagają, by tutaj ustawić 0 .

Skrypt obsługujący wiadomości SMS

Mamy zatem przygotowaną konfigurację dla demona smsd . Wyżej jednak skorzystaliśmy z parametru eventhandler , który, jak nazwa wskazuje, odpowiada za obsługę zdarzeń. Mamy tutaj określony skrypt shell'owy. W nim znajdują się akcje, które są wywoływane, o ile określone warunki zostaną spełnione. Można skorzystać z mojego skryptu. Taki skrypt z grubsza przyjmuje dwa argumenty. Pierwszym z nich jest rodzaj zdarzenia. Mamy tutaj do wyboru: SENT , RECEIVED , FAILED , REPORT lub CALL . Drugim zaś jest nazwa pliku z otrzymaną wiadomością. Poniżej znajduje się log ze zdarzenia odebrania SMS'a:

2016-05-06 14:50:16,5, GSM1: SMS received, From: 48600123456
2016-05-06 14:50:16,6, GSM1: Wrote an incoming message file: /var/spool/sms/incoming/GSM1.H00yfm
2016-05-06 14:50:16,7, GSM1: Running eventhandler: /opt/skrypty/lte-modem/lte RECEIVED /var/spool/sms/incoming/GSM1.H00yfm
2016-05-06 14:50:16,7, GSM1: Done: eventhandler, execution time 0 sec., status: 0 (0)
2016-05-06 14:50:16,6, GSM1: Deleting message 0

Widzimy wyżej, że wiadomość została zapisana w pliku /var/spool/sms/incoming/GSM1.H00yfm oraz, że smsd wywołał powyższy skrypt i podał mu dwa argumenty. Niżej znajduje się format otrzymanej wiadomości SMS:

From: 48600123456
From_TOA: 91 international, ISDN/telephone
From_SMSC: 4860654321
Sent: 16-05-06 14:50:00
Received: 16-05-06 14:50:16
Subject: GSM1
Modem: GSM1
IMSI: 123456789011121
Report: no
Alphabet: UTF-8
Length: 19

Wiadomosc dla Morfitronik

Mamy tutaj szereg informacji opisujących tego SMS. Mamy numer, z którego wiadomość nadeszła ( From: ) oraz na samym dole treść wiadomości. W oparciu o te dane można zrobić szereg warunków. I to w zasadzie cała magia. To jak zaprogramujemy sobie smsd zależy głównie od naszej wyobraźni.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.