Скрипт отправки смс сообщений (допилить не могу)

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Скрипт отправки смс сообщений

Сообщение Skyb »

Вообщем написал простенький скрипт до отправки сообщений, верней по кусочкам собрал. Да не суть дела

Код: Выделить всё

#! /bin/bash

# Указать узлы
nodes="gateway
       router
       billing
       nas2
       nas1
       ya.ru"
# Указать номера телефонов для рассылки
telephones="+79150000001
            +79150000002
            +79150000003"

# Перебираем все узлы
for node in $nodes
do
    # Пингуем узел
    /bin/ping -c 5 $node > /dev/null 2> /dev/null
    # Если ошибка:
    if [ $? -gt 0 ]; then
        # Если существует файл узла:
        if [ -e "/tmp/$node" ]; then
            # Пишем на stdout что сообщение было уже отослано
            /bin/echo Already sent
        # Если не существует файл узла:
        else
            # Проверяем были ли ошибки в других узлах
            if [ -z "$errors" ];then
                # Начинаем сообщение
                errors="Down: $node"
            else
                # Добавляем в сообщение
                errors="$errors,$node"
            fi
        fi
        # Создаем файл и пишем(дописываем) в файл дату
        /bin/echo `date` >> /tmp/$node
    # Если ошибка нет
    else
        # Проверяем наличие файла узла и удаляем его
        if [ -e "/tmp/$node" ]; then
            # Проверяем были ли исправленные ошибки в других узлах
            if [ -z "$fixed" ];then
                # Начинаем сообщение
                fixed="Fixed: $node"
            else
                # Добавляем в сообщение
                fixed="$fixed,$node"
            fi
            /bin/rm /tmp/$node
        fi
    fi

done

# Проверяем были ли ошибки:
if [ -z "$errors" ];then
    /bin/echo empty
else
    for telephone in $telephones
    do
        /usr/bin/scmxx --device=/dev/ttyACM0 --send --sms --direct --number=$telephone --text="$errors"
    done
fi

# Проверяем были исправились ли ошибки
if [ -z "$fixed" ];then
    /bin/echo empty
else
    for telephone in $telephones
    do
        /usr/bin/scmxx --device=/dev/ttyACM0 --send --sms --direct --number=$telephone --text="$fixed"
    done
fi

Что хотелось бы сделать --- запихать в шедулер этот скрипт на выполнения каждые 3 минуты, НО, чтоб был пинг и если он проподает то писалось в фаил до чего пропало, а если в течении часа восстановилось то смс чтоб не отправлялось, а если не восстановился пинг то отправлять у кого не восстановился. Плюс будет несколько узлов если они ложатся то смс отсылать сразу
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

продумал немного, хрень получяется с такой логикой.
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

ээ.. Я, возможно, туплю, но что вы хотели от публики? Чтобы исправили ваш скрипт? Но тогда, что конкретно в нем исправлять? Или чтобы написали новый? Если новый, то такой сойдет?

Код: Выделить всё

#!/bin/bash

# Переменные {{{

# Отладка (битовое ИЛИ для включения нескольких сразу)
declare -i debug_input_parse=1
declare -i debug_select_node=2
declare -i debug_check_node=4
#declare -i debug=$((debug_select_node | debug_check_node))
declare -i debug=0

# Для печати ошибок/предупреждений/инфо
declare PS_E="${0##*/}: Error"
declare PS_W="${0##*/}: Warning"
declare PS_I="${0##*/}: Info"

# Для всякого.
declare -i i=0
declare s=""

declare -a telephone=(
    01
    02
    03
)
# Для "текущих" значение в цикле
declare tel=""
declare msg=""

declare tmp_path="."
# Защита от опечаток (несколько слешей будем считать уже не опечаткой)
tmp_path="${tmp_path%/}"

# Переменные для вычисления "а не пора ли отправлять сообщение?"
declare -i cur_time=0
declare -i fail_time=0
declare -i delta_time=0

# Далее будет (не совсем правильная, возможно) аналогия для {{{
#
#   struct node_t {
#       int delay;              // задержка отправки сообщения
#       struct list_t *list;    // список узлов с такой задержкой
#   };
#
# Т.е узлы с одинаковой задержкой в отправке сообщений будут "содержаться" в
# одном "объекте" типа node_t.
#
# Как это все записано здесь: содержимое объекта распределено по двум массивам
#
#   node_tDelay[]
#   node_tList[]
#
# в первом записаны задержки, во втором - _имена_ массивов, содержащих списки
# узлов данного объекта (т.е для данных одного объекта индекс в node_tDelay[]
# и в node_tList[] одинаковый). Объекты адресуются индексом в массиве. Вот
# схема:
#
#       struct node_t A; здесь будет записано так
#
#       node_tDelay[node_t_A] ~~ A.delay
#       node_tList[node_t_A]  ~~ A.list
#
#       т.е
#
#           node_tDelay                 node_tList
#       ======+=========+=======      ======+========+=====
#       I     | A.delay |      I      I     | A.list |    I
#       ======+=========+=======      ======+========+=====
#       |     |                       |     |
#       |---->|                       |---->|
#        node_t_A                      node_t_A
# }}}

# Допустим, есть две категории узлов: для одних отправлять сообщение с без
# задержки - включим эти узлы в объект А, для других с задержкой в 20 секунд -
# эти в объект B.

# Индекс (адрес) объектов.
i=0
declare -i node_t_A=$((i++))
declare -i node_t_B=$((i++))
i=0

# Запишем имена всех объектов в массив, чтобы не перечислять их в циклах
# вручную.
declare -a node_t_obj=(
    node_t_A
    node_t_B
)

# Задержка (в секундах) в отправке сообщения (значение .delay).
declare -i -a node_tDelay=(
    [node_t_A]=0
    [node_t_B]=20
)
# Имена массивов, содержащих список узлов соответствующего объекта (указатель,
# значение .list).
declare -a node_tList=(
    [node_t_A]="node_tList_A"
    [node_t_B]="node_tList_B"
)
# Список узлов для объекта А (отправка без задержки)
declare -a node_tList_A=(
    'A1'
    'A2'
)
# Список узлов для объекта B (задержки 20с)
declare -a node_tList_B=(
    'B1'
    'B2'
    'B3'
)

# Для "текущего" объекта в циклах
declare -i node_t=0         # индекс (адрес) текущего объекта
declare -a node_t__list=( ) # элемент .list текущего объекта (копия)
declare node=""             # текущий элемент в .list

# }}}
# Небольшая проверка корректности определений. {{{
# Типы переменных не проверяется. Возможно, надо?

if [ "x${node_t_obj[*]}" == "x" ]; then
    echo "${PS_I}: Nothing to be done, no objects defined."
    exit 0
fi
# FIXME: perhaps, should check for empty elements in node_t_obj[] ?
if ((  ${#node_t_obj[*]} < ${#node_tDelay[*]}
    || ${#node_t_obj[*]} < ${#node_tList[*]})); then
    echo "${PS_W}: Elements count mismatch, perhaps some objects missed from 'node_t_obj[]' "
fi
for s in "${node_t_obj[@]}"; do
    eval "node_t=\$$s"
    ((debug & debug_input_parse)) && echo "Check: '$s' - '$node_t'"
    if [   "x${node_tDelay[node_t]:+x}" == x \
        -o "x${node_tList[node_t]:+x}"  == x ];
    then
        echo "${PS_E}: for object '$s' ($node_t) either member .delay or pointer to list undefined."
        exit -1
    fi
    if ! declare -p ${node_tList[node_t]} >/dev/null 2>&1; then
        echo "${PS_E}: for object '$s' ($node_t) member .list undefined."
        exit -1
    fi
done

# }}}
# Функции {{{

func_check_node() {
    # Параметры:
    # 1 - node

    # Для отладки:
    read -p'->' -n1 -r rep
    return $rep

    /bin/ping -c 5 $1 > /dev/null 2> /dev/null
    return $?
}

func_send_msg() {
    # Параметры:
    # 1 - telephone
    # 2 - message

    # Для отладки:
    echo - $1 - $2
    return $?

    /usr/bin/scmxx --device=/dev/ttyACM0 --send --sms --direct --number=$1 --text="$2"
    return $?
}

# }}}

for s in "${node_t_obj[@]}"; do
    eval "node_t=\$$s"
    eval "node_t__list=( \${${node_tList[node_t]}[@]} )"
    ((debug & debug_select_node)) \
        && echo "Select object: '$s' - '$node_t'" \
        && echo "Fetch member .list: '${node_t__list[*]}'"
    for node in "${node_t__list[@]}"; do
        node_file="$tmp_path/$node"
        ((debug & debug_select_node)) \
            && echo "  Select node from .list: '$node' - '$node_file'"
        func_check_node "$node"
        ret=$?
        if ((ret)); then
            ((debug & debug_check_node)) \
                && echo "  Node '$node' down"

            cur_time="$(date +%s)"
            if [ -e "$node_file" ]; then
                # Узел уже "лежал" в предыдущую проверку.
                fail_time="$(<"$node_file")"
                delta_time=$((cur_time - fail_time))
                ((debug & debug_check_node)) \
                    && echo "    over $delta_time seconds ($cur_time, $fail_time)"

                if ((delta_time > node_tDelay[node_t])); then
                    # Сообщение об ошибке, только если узел лежит дольше, чем
                    # ему можно (элемент .delay). Время падения сбрасываем в
                    # текущее, чтобы сообщения не отправлялись каждую
                    # следующую проверку.
                    [ -z "$errors" ] \
                        && errors="Down: $node" \
                        || errors="$errors, $node"
                    echo "$cur_time" >"$node_file"
                    # Сохраняем _предыдущее_ время модификации, - чтобы потом
                    # определить было ли отправлено хотя бы одно сообщение.
                    touch -m --date="@$fail_time" "$node_file"
                    ((debug & debug_check_node)) \
                        && echo "  Timeout reached, mark as error: '$errors'" \
                        && echo "  New fail time: '$(<"$node_file")'" \
                        && echo "  Previous fail time: '$(stat -t -c "%Y" "$node_file")'"
                fi
            else
                # Узел упал в первый раз. Записываем время, чтобы дальше
                # считать сколько он уже лежит. Если задержка 0, отправляем
                # сообщение сразу.
                echo "$cur_time" >"$node_file"
                ((debug & debug_check_node)) \
                    && echo "    for the first time"
                if ((!node_tDelay[node_t])); then
                    [ -z "$errors" ] \
                        && errors="Down: $node" \
                        || errors="$errors, $node"
                    ((debug & debug_check_node)) \
                        && echo "  Report immediately, mark as error: '$errors'"
                fi
            fi
        else
            ((debug & debug_check_node)) \
                && echo "  Node '$node' up"
            if [ -e "$node_file" ]; then
                # fail_time - время отправки сообщения _перед_ предыдущим,
                # если оно было, либо время первого падения интерфейса.
                cur_time="$(date +%s)"
                fail_time="$(stat -t --format="%Y" "$node_file")"
                delta_time=$((cur_time - fail_time))
                ((debug & debug_check_node)) \
                    && echo "    but was down over $delta_time seconds"

                if ((delta_time > node_tDelay[node_t])); then
                    # Если разница между текущем временем и временем
                    # модификации больше допустимой, сообщения (хотя бы одно)
                    # отправлено было.
                    [ -z "$fixed" ] \
                        && fixed="Fixed: $node" \
                        || fixed="$fixed, $node"
                    ((debug & debug_check_node)) \
                        && echo "  Error message was sent, mark as fixed: '$fixed'"
                fi
            fi
            /bin/rm -f "$node_file"
        fi
    done
done

for msg in "$errors" "$fixed"; do
    [ -z "$msg" ] && continue
    for tel in "${telephone[@]}"; do
        func_send_msg "$tel" "$msg"
    done
done

exit 0


Он получился немного длиннее и немного запутаннее, чем я планировал вначале (да и чем он мог бы быть, наверное) -)

PS. И не уверен, что в нем нету ошибок ^)

UPD: несколько небольших исправлений.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Ой, а распишите хотябы в крацие что он делает :)
fixed
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
t.t
Бывший модератор
Сообщения: 7390
Статус: думающий о вечном
ОС: Debian, LMDE

Re: Скрипт отправки смс сообщений

Сообщение t.t »

В какой рации?

P.S. Прошу прощения, не удержался.
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
Спасибо сказали:
MrClon
Сообщения: 838
ОС: Ubuntu 10.04, Debian 7 и 6

Re: Скрипт отправки смс сообщений

Сообщение MrClon »

Скрипт именно для отправки SMS где-то в инете видел, правда он кажется был написан для maemo (т.е. должен работать на телефоне с GNU/Linux и отправлять SMS средствами самого телефона).
Ещё для мониторинга серверов есть такая вещь nagios, в числе прочего умеет уведомлять о падении SMS-ками.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Немного неправильно сформулировал вопрос...(просто если нет аплинка, то через инет нагиосом неотправишь смс...мы зеносс используем)
Да нужен именно отправление телефоном!!! Несовсем понял как он работает, тоесть просто описать логику работы скрипта..и ещё где прописывать хосты узлов?? и что значит

Код: Выделить всё

# Для всякого.
declare -i i=0
declare s=""


Код: Выделить всё

# Для "текущих" значение в цикле
declare tel=""
declare msg=""

эти значения
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
MrClon
Сообщения: 838
ОС: Ubuntu 10.04, Debian 7 и 6

Re: Скрипт отправки смс сообщений

Сообщение MrClon »

nagios вроде может телефоном отправлять. Ну или во всяком случае может запускать внешнюю команду при определённых событиях, ну а она уже отправит SMS телефоном.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Нет, nagios ставить небудем, она нетакая удобная как zenoss да и привязано уже к zenoss много привязано...мнеб скриптик навоять и хватит :)
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
nickm
Сообщения: 203
ОС: RFRemix

Re: Скрипт отправки смс сообщений

Сообщение nickm »

MrClon писал(а):
16.09.2010 10:38
Ещё для мониторинга серверов есть такая вещь nagios, в числе прочего умеет уведомлять о падении SMS-ками.

Сам он неможет, только через smstools или что-нибудь подобное или свой велосипед.

/me сейчас как раз свой велосипед к нагиосу приделывает ;)
Спасибо сказали:
MrClon
Сообщения: 838
ОС: Ubuntu 10.04, Debian 7 и 6

Re: Скрипт отправки смс сообщений

Сообщение MrClon »

А zenoss разве не умеет внешние команды запускать? Кстати к нему вроде плагины от вагинуса подходят.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Харе оффтопить :) ... я смотрю не 1 админ тут сижу...делитесь скриптами отправки упавших узлов по смс :)
> Кстати к нему вроде плагины от вагинуса подходят.
От чего простите ?7 О_о .... умеет, только нада прикручивать...а хз как...подскажите...
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
MrClon
Сообщения: 838
ОС: Ubuntu 10.04, Debian 7 и 6

Re: Скрипт отправки смс сообщений

Сообщение MrClon »

От нагиоса.
Спасибо сказали:
Аватара пользователя
nickm
Сообщения: 203
ОС: RFRemix

Re: Скрипт отправки смс сообщений

Сообщение nickm »

Skyb писал(а):
16.09.2010 11:39
Харе оффтопить :) ... я смотрю не 1 админ тут сижу...делитесь скриптами отправки упавших узлов по смс :)

Ну пока "in progress", допилю, немного причешу, мож на gihub и выложу.
Самое вкусное - пул для сообщений сделать, чтобы не рассылалось over9k мессаг, смс они того, денег стоят да и телефоны одминов/техдира спамить если массовый падёж начался (свет например вырубили, куча коммутаторов в дауне) вот основная идея + оповещение в жаббер, если сеть есть.

PS пишется на Ruby, если что ;)
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

ух ты...и как долго ждать?? а как отправляться будет?? какой прогой??
смысле понятно что чемто самописным, но доступ то к телефону должен быть!!!
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
nickm
Сообщения: 203
ОС: RFRemix

Re: Скрипт отправки смс сообщений

Сообщение nickm »

> ух ты...и как долго ждать??
вопрос риторический ;) тут заметил тенденцию, как хочеш заняться, так что-то случается, отвлекаешся почти на целый день.
> а как отправляться будет?? какой прогой??
само будет отправлять. писать в /dev/ttyUSB0 - ума много не надо.
> смысле понятно что чемто самописным, но доступ то к телефону должен быть!!!
не понял фразы ;)

Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

Skyb писал(а):
16.09.2010 10:41
Несовсем понял как он работает, тоесть просто описать логику работы скрипта..

Вообще, самое лучшее (на мой взгляд) объяснение, как он работает написано в комментах, поэтому все повторять не буду - напишу только основное.

Мой скрипт - это переписанный ваш с другим способом хранения исходных данных (узлов и информации о задержек для отправки) и некоторыми изменениями в алгоритме выбора "отправлять/не отправлять". Он состоит из двух частей: первая часть - реализация выбранной мной схемы хранения. Эта часть может быть легко заменена на любую другую схему хранения, в том числе и более простую (и заменить, возможно, стоит: я выбрал такую схему хранения не потому, что она оптимальна, а потому, что хотел попробовать что получится). Вторая часть - это, собственно, алгоритм отправки. Эти две части независимы друг от друга (и, соответственно, могут быть изменены независимо).

Схема хранения.
Как уже было написано в комментах, я использую аналогию для

Код: Выделить всё

struct node_t {
       int delay;              // задержка отправки сообщения
       struct list_t *list;    // список узлов с такой задержкой
};
struct node_t A;
struct node_t B;
...


Детали реализации (даже с картинками ^) есть в комментах. Название node_t, как я немного поздно сообразил, неудачно, тк структура содержит информацию не об одном узле, а о всех узлах с одинаковой задержкой отправки.

Кроме того, если нужно иметь возможность получить доступ к определенному объекту "по имени", то надо определять

Код: Выделить всё

struct node_t A;
struct node_t B;


т.е в скрипте

Код: Выделить всё

i=0
declare -i node_t_A=$((i++))
declare -i node_t_B=$((i++))


Если же это не нужно (а в данном случае, это скорее всего не нужно), то можем просто использовать массив

Код: Выделить всё

struct node_t *nodes;


т.е в скрипте это

Код: Выделить всё

i=0
declare -i node_t_A=$((i++))
declare -i node_t_B=$((i++))
i=0

declare -a node_t_obj=(
    node_t_A
    node_t_B
)


можно стереть и просто перебирать все элементы массивов node_tList[] и node_tDelay[] (ну, просто стереть недостаточно - надо будет немного изменить циклы, перебирающие элементы).

Skyb писал(а):
16.09.2010 10:41
и ещё где прописывать хосты узлов??

Если ничего не менять в скрипте, то, допустим, у вас следующая конфигурация

Код: Выделить всё

Для узлов A1, A2, A3    - сообщения отправлять сразу
Для узлов B1, B2        - через 30 секунд.
Для узла C1             - через 10 минут.


Тогда узлы A1, A2, A3 будут описаны в объекте A (аналогия struct node_t A), узлы B1, B2 - в объекте B (struct node_t B), узел С - в объекте C (struct node_t C).

Код: Выделить всё

i=0
declare -i node_t_A=$((i++))
declare -i node_t_B=$((i++))
declare -i node_t_C=$((i++))
i=0


и еще массив

Код: Выделить всё

declare -a node_t_obj=(
    node_t_A
    node_t_B
    node_t_C
)


чтобы не перечислять все объекты каждый раз в циклах.

Это аналогия для

Код: Выделить всё

struct node_t A, B, C;


Теперь надо проинициализировать.

Задержка отправки

Код: Выделить всё

declare -i -a node_tDelay=(
    [node_t_A]=0
    [node_t_B]=30
    [node_t_C]=600
)


это аналогия для

Код: Выделить всё

A.delay = 0;
B.delay = 30;
C.delay = 600;


Списки узлов

Код: Выделить всё

declare -a node_tList=(
    [node_t_A]="node_tList_A"
    [node_t_B]="node_tList_B"
    [node_t_C]="node_tList_C"
)
declare -a node_tList_A=(
    'A1'
    'A2'
    'A3'
)
declare -a node_tList_B=(
    'B1'
    'B2'
)
declare -a node_tList_C=(
    'C1'
)


это аналогия для чего-то такого (не уверен, что ничего не напутал, но смысл понятен, я думаю)

Код: Выделить всё

Как-то определить A1, A2, B1, B2, B3, C1.
...
A.list = (list_t *) malloc (sizeof (struct list_t));
list_t_init(A.list, 3, &A1, &A2, &A3);
B.list = (list_t *) malloc (sizeof (struct list_t));
list_t_init(A.list, 2, &B1, &B2);
C.list = (list_t *) malloc (sizeof (struct list_t));
list_t_init(A.list, 1, &C1);



После строк

Код: Выделить всё

    for node in "${node_t__list[@]}"; do
        node_file="$tmp_path/$node"
        ...


реализация схемы хранения заканчивается и начинается алгоритм, определяющий "отпавлять или не отправлять". Переменная node - это имя узла (также, как у вас), а node_file - это временный файл, куда писать время. Проверки пинга и отправку смс я поместил в функции для удобства. Алгоритм "определения отправки" такой:

1. Проверяем что-то (вызывая функцию).
2. Если это что-то возвращает >0, то
2.1 Если файл с временем не существует, создаем и записываем туда текущее время, заодно проверяя если задержки отправки нету (0), то сразу регистрируем ошибку (в переменной errors).
2.2 Если файл с временем существует, то считаем сколько времени прошло с тех пор, как узлу стало плохо.
2.2.1 Если меньше допустимомго, то ошибку не регистрируем (не записываем в errors ничего).
2.2.2 Если больше, то добавляем в errors сообщение, затем обновляем время в файле на текущее (старое время стирается, оно уже не нужно). Если этого не сделать, сообщение об ошибке будет посылаться _каждый_ следующий запуск (если узел все еще лежит). Я думаю, что это неправилно. Затем, время изменения файла выставляем в _прошлое_ время падения. Это нужно, чтобы правильно опеделять было ли отправлено сообщение об ошибке и посылать ли сообщение "Fixed:" (см. ниже).
3. Если вовзращает 0, то
2.1 Если файл с временем существует, считаем разницу между текущим временем и временем модификации файла, чтобы определить нужно ли отпавлять сообщение с "Fixed".
2.1.1 Если меньше допустимого для этого узла времени "лежания", то не отправляем.
2.1.2 Если больше - отправляем.
Удаляем файл в любом случае.

Все временные метки в секундах с 1970-01-01 UTC.

Про определение "было ли отпрвлено сообщение об ошибке".

Картинка..

Код: Выделить всё

    прошлое время
    падения узла
        |                       Узел "исправился"
        |                            |
        v   (интервал)               v
    ----+-------------------+--------*
        E1                  E2       F1
                            ^
                            |
                    сообщение об ошибке
                    отправлено сейчас и это
                    время записано в файл
                    (время модификации установлено в E1).


В момент времени F1 в файле записано время E2, и время до следующей отправки еще не истекло. Поэтому, возможны два случая:
- файл был создан в момент времени E2 и сообщение об ошибке отправлено не было.
- файл был создан раньше, сообщение об ошибке уже было отправлено один или больше раз, и время в файле было обновлено (то, что на картинке).

Чтобы различить эти случаи, при обновлении времени в файле (после отправки сообщения) я сбрасываю время модификации на предыдущее время отправки (или время создания, если это первое сообщение об ошибке). Поэтому, получается, что если сообщение еще не было отправлено ни разу,

Код: Выделить всё

E1 == E2 и
(F1 - E1) < интервала отправки.


и сообщение с "Fixed" не посылаем.
Если сообщение было отправлено хотя бы раз,

Код: Выделить всё

(F1 - E1) > интервала отправки


и надо посылать сообщение с "Fixed".

Skyb писал(а):
16.09.2010 10:41
и что значит

Код: Выделить всё

# Для всякого.
declare -i i=0
declare s=""


Код: Выделить всё

# Для "текущих" значение в цикле
declare tel=""
declare msg=""

эти значения


Первое - это просто временные переменные для чего попало. Второе - ну, как и написано, "индексы" цикла по телефонам и сообщениям.

PS. Думаю это должно быть и так понятно: мой скрипт не будет работать под sh (только bash).


UPD. Скрипт немного обновлен.
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

Итак, представляю вам.. "Сухарики "Воронцовские" - пекутся о традициях забавы ради" xD

Или вторая версия скрипта, более user-friendly, как мне кажется ^)

Изменена структура хранения данных (см. комменты), а также теперь объекты могут быть адресованы только через массив (то, о чем я писал в предыдущем посте). Добавлен '-r' флаг для многих переменных. Место, где писать имена узлов и соответствующую задержку, выделено так

Код: Выделить всё

### Редактируется пользователем (начало).
...
### Редактируется пользователем (конец)


Ну, и, собственно, скрипт

Код: Выделить всё

#!/bin/bash

# v2.1.00
#   Node check:
#       [modified]:
#           - Report error, when down time is _equal_or_greater_, than delay.
#           (Before was only when _greater_).
#       [improved]:
#           - Timeout checks merged.  When host go down for the first time, we
#           check is delay equal to zero to report error immediately. This
#           check merged with regular timeout checks, when host was down for a
#           while.
# v2.0.04
#   General:
#       [improved]:
#           - Remove any number of trailing slashes from 'tmp_path'.
# v2.0.03
#   ....


# Переменные {{{

# Временные переменные.
declare -i i=0
declare -i j=0
declare s=""

# Отладка (битовое ИЛИ для включения нескольких сразу)
i=1
declare -r -i debug_input_parse=$i && ((i<<=1))
declare -r -i debug_init_node_t=$i && ((i<<=1))
declare -r -i debug_select_node=$i && ((i<<=1))
declare -r -i debug_check_node=$i  && ((i<<=1))
declare -r -i debug_all=-1
declare -r -i debug=$debug_check_node

# Для печати ошибок/предупреждений/инфо
declare -r PS_E="${0##*/}: Error"
declare -r PS_W="${0##*/}: Warning"
declare -r PS_I="${0##*/}: Info"

# Список телефонов
declare -r -a telephone=(
    01
    02
    03
)
# Для "текущих" значений в циклах
declare tel=""
declare msg=""

declare tmp_path="."
# Удаляем все '/' на конце.
tmp_path="$(echo "$tmp_path" | sed -e'/^\/$/q; s/\/\+$//')"
declare -r tmp_path


# Переменные для вычисления "а не пора ли отправлять сообщение?"
declare -i cur_time=0
declare -i fail_time=0
declare -i delta_time=0

# Принцип хранения данных аналогичен (не совсем) такой структуре {{{
#
#   struct node_t {
#       int delay;              // задержка отправки сообщения
#       struct list_t list;     // список узлов для этой задержки
#   };
#
# (.list теперь не указатель, в отличие от первой версии). Как и в 1-ой
# версии, узлы, имеющие одинаковую задержку для отправки сообщений,
# содержаться в одном объекте типа 'struct node_t'.
#
# Как это все реализовано здесь:
#
# Все объекты последовательно записаны в массив
#
#   node_t__data[] (аналогия с памятью)
#
# и смещение до начала каждого объекта записано в массиве
#
#   node_t__off[] (аналогия с массивом указателей 'struct node_t **')
#
# Смещение элементов объекта (.delay и .list) от его начала записано в
# переменных
#
#   node_tDelay
#   node_tList
#
# В отличие от 1-ой версии, кроме другого принципа хранения данных, здесь уже
# объекты не имеют имен (т.е к ним можно обратиться только по индексу в
# массиве - аналогия с 'struct node_t *').
#
# Вот картинка
#
#                                     node_t__data[]
#                                 ------============
#       node_t__off[]                |  I          I
#       I===========I                |  I          I
#       I           I                |  I          I
#       I  ...      I                |  I          I
#       I-----------I                v  I          I
#       I смещение  I------------> -----I----------I------------ объект А
#       I объекта А I                   I          I | | node_tDelay
#       I-----------I                   I          I | v
#       I смещение  I------+            I----------I-|---
#       I объекта Б I      |            I задержка I | node_tList
#       I-----------I      |            I          I v
#       I           I      |            I----------I---
#       I           I      |            I список   I
#       I           I      |            I узлов    I
#       I           I      |            I ...      I
#       I           I      +-----> -----I----------I------------- объект Б
#       I           I      |            I ...      I
#
# (на самом деле, node_tDelay == 0, а не так, как нарисовано на картинке)
#
# }}}

# Смещения элементов структуры от начала самой структуры.
i=0
declare -r -i node_tDelay=$((i++))  # смещение элемента .delay (размер == 1)
declare -r -i node_tList=$((i++))   # смещение элемента .list (размер
                                    # неопределен)
declare -r -i minsize_node_t=$i     # минимальный размер правильного объекта
i=0

# Для "текущего" объекта в циклах
declare node_t=""           # имя (указатель) текущего объекта
declare -i sizeof_node_t=0  # размер текущего объекта.

declare -i node_t__delay=0      # значение элемента .delay текущего объекта

declare -i node_t__list_start=0 # индекс в node_t__data[], где начинается
                                # элемент .list текущего объекта

declare -i node_t__list_end=0   # индекс в node_t__data[], где кончается
                                # элемент .list текущего объекта (последний
                                # элемент .list-а находится по предыдущему
                                # индексу)

declare node=""                 # текущий элемент в .list

# Определения объектов.
### Редактируется пользователем (начало).
# Определения не соответствующие по форме (например, без использования
# '[node_tDelay]=..' и тд) могут обойти проверки на ошибки, поэтому лучше
# писать, как в примере -)

declare -a node_t_A=(
    [node_tDelay]='0'
    [node_tList]='A1' 'A2'
)
declare -a node_t_B=(
    [node_tDelay]='20'
    [node_tList]='B1' 'B2' 'B3'
)
# список всех объектов (объекты, не указанные здесь, игнорируются).
declare -a node_t__obj=(
    node_t_A
    node_t_B
)

### Редактируется пользователем (конец)

# Определения внутренней структуры хранения.
if declare -p node_t__off node_t__data >/dev/null 2>&1; then
    echo "${PS_E}: one of internal variable names - 'node_t__off' or 'node_t__data' - already used"
    exit -1
fi
# Все объекты записаны сюда последовательно.
declare node_t__data=( )
# Смещения в node_t__data[] до начала соответствующего объекта.
declare -i node_t__off=( 0 )


# }}}
# Инициализация внутренней структуры. {{{

# Копируем (а заодно проверяем) данные из объектов, определенных
# пользователем, в node_t__data[] и записываем соответствующие смещения в
# node_t__off[]. Скопированы будут только объекты, указанные в node_t__obj[].
# Последовательность объектов в node_t__data[] будет такая же, как в
# node_t__obj[]. После завершения копирования node_t__obj[] и все объекты,
# указанные в нем, будут удалены. Т.е далее получить доступ к объектам можно
# будет _только_ через node_t__off[] и node_t__data[].

# FIXME: check type of user-defined variables.
# '-r' flag will be detected during `unset`. '-a' flag is implicitly checked
# through number of elements in user-defined object. Probably, that's enough.
# FIXME: check content of user-defined objects (.delay is integer, .list
# containt only not empty elements).
# We really need this?

if [ "x${node_t__obj[*]}" == "x" ]; then
    echo "${PS_I}: Nothing to be done, no objects defined."
    exit 0
fi

i=0
for node_t in "${node_t__obj[@]}"; do
    if ! declare -p ${node_t} >/dev/null 2>&1; then
        echo "${PS_W}: object '$node_t' is not defined, skipped."
        continue
    fi
    eval "sizeof_node_t=\${#$node_t[*]}"
    if ((sizeof_node_t < minsize_node_t)); then
        echo "${PS_W}: size of element '$node_t' is lesser, than minimal, skipped."
        continue
    fi
    node_t__off[++i]=$((node_t__off[i] + sizeof_node_t))
    ((debug & debug_init_node_t)) \
        && echo "Init: object '$node_t', sizeof = '$sizeof_node_t'" \
        && echo "  offset = '${node_t__off[i]}', index = '$i'"
    eval "
        node_t__data=(
            \"\${node_t__data[@]}\"
            \"\${$node_t[@]}\"
        )
    "
    if ! unset $node_t; then
        echo "${PS_E}: can not unset variable '$node_t'"
        exit -1
    fi
done
((debug & debug_init_node_t)) \
    && echo "Init: completed, environment" \
    && declare -p node_t__off node_t__data \
    && (
        IFS=','
        declare -p "${node_t__obj[@]}" >/dev/null 2>&1 \
        || echo "${node_t__obj[*]} unset"
    )
if ! unset node_t__obj; then
    echo "${PS_E}: can not unset variable 'node_t__obj'"
    exit -1
fi

# }}}
# Функции (начало) {{{

func_check_node() {
    # Параметры:
    #   1   - node
    # Возвращаемое значение:
    #   0   - успех,
    #   >0  - неудача.

    # Для отладки:
    declare -i repl=0
    read -p'->' -n1 -r repl
    return $repl

    /bin/ping -c 5 $1 > /dev/null 2> /dev/null
    return $?
}

func_send_msg() {
    # Параметры:
    # 1 - telephone
    # 2 - message
    # Возвращаемое значение:
    #   0   - успех,
    #   >0  - неудача.

    # Для отладки:
    echo - $1 - $2
    return $?

    /usr/bin/scmxx --device=/dev/ttyACM0 --send --sms --direct --number=$1 --text="$2"
    return $?
}

# Функции (конец) }}}

# Последний элемент в node_t__off[] - это смещение на начало пустой области в
# node_t__data[], поэтому его пропускаем.

for ((i = 0; i < (${#node_t__off[*]} - 1); i++)); do
    node_t__delay="${node_t__data[node_t__off[i] + node_tDelay]}";
    node_t__list_start=$((node_t__off[i] + node_tList));
    node_t__list_end=$((node_t__off[i + 1]));
    ((debug & debug_select_node)) \
        && echo "Select: object [$i] with offset '${node_t__off[i]}'" \
        && echo "  delay = '$node_t__delay', list_start = '$node_t__list_start', list_end = '$node_t__list_end'"
    for ((j = node_t__list_start; j < node_t__list_end; j++)); do
        node=${node_t__data[j]};
        node_file="$tmp_path/$node"
        ((debug & debug_select_node)) \
            && echo "  Select node from .list: '$node' with path '$node_file'"
        func_check_node "$node"
        ret=$?
        if ((ret)); then
            ((debug & debug_check_node)) \
                && echo "  Node '$node' down"

            cur_time="$(date +%s)"
            if [ -e "$node_file" ]; then
                # Узел уже "лежал" в предыдущую проверку.
                fail_time="$(<"$node_file")"
            else
                # Узел упал в первый раз.
                echo "$cur_time" >"$node_file"
                fail_time=$cur_time
            fi
            delta_time=$((cur_time - fail_time))
            ((debug & debug_check_node)) \
                && echo "    over $delta_time seconds ($cur_time, $fail_time)"

            if ((delta_time >= node_t__delay)); then
                # Узел лежит дольше, чем ему можно (>= элемента .delay). Время
                # падения сбрасываем в текущее, чтобы сообщения не
                # отправлялись каждую следующую проверку.
                [ -z "$errors" ] \
                    && errors="Down: $node" \
                    || errors="$errors, $node"
                echo "$cur_time" >"$node_file"
                # Сохраняем _предыдущее_ время модификации, - чтобы потом
                # определить было ли отправлено хотя бы одно сообщение.
                touch -m --date="@$fail_time" "$node_file"
                ((debug & debug_check_node)) \
                    && echo "  Timeout ('$node_t__delay') reached, mark as error: '$errors'" \
                    && echo "  New fail time: '$(<"$node_file")'" \
                    && echo "  Previous fail time: '$(stat -t -c "%Y" "$node_file")'"
            fi
        else
            ((debug & debug_check_node)) \
                && echo "  Node '$node' up"
            if [ -e "$node_file" ]; then
                # fail_time - время отправки сообщения _перед_ предыдущим,
                # если оно было, либо время первого падения интерфейса.
                cur_time="$(date +%s)"
                fail_time="$(stat -t --format="%Y" "$node_file")"
                delta_time=$((cur_time - fail_time))
                ((debug & debug_check_node)) \
                    && echo "    but was down over $delta_time seconds"

                if ((delta_time >= node_t__delay)); then
                    # Если разница между текущем временем и временем
                    # модификации больше допустимой, сообщения (хотя бы одно)
                    # отправлено было.
                    [ -z "$fixed" ] \
                        && fixed="Fixed: $node" \
                        || fixed="$fixed, $node"
                    ((debug & debug_check_node)) \
                        && echo "  Error message was sent, mark as fixed: '$fixed'"
                fi
            fi
            /bin/rm -f "$node_file"
        fi
    done
done

for msg in "$errors" "$fixed"; do
    [ -z "$msg" ] && continue
    for tel in "${telephone[@]}"; do
        func_send_msg "$tel" "$msg"
    done
done

exit 0



PS. В скрипте могут быть ошибки, а также я не уверен в его совместимости со старыми версиями bash -)
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Я в баше прям новечек новечком...поставил так

Код: Выделить всё

declare -a node_t_A=(
    [node_tDelay]='0'
    [node_tList]='10.0.0.1'
)
declare -a node_t_B=(
    [node_tDelay]='20'
    [node_tList]='10.0.0.1'

для теста и просмотра работы скрипта, после ручного запуска вылетело

Код: Выделить всё

[root@localhost smsalarm]# ./smsalarm.sh
->               # здесь я нажимал энтр
->
[root@localhost smsalarm]# ./smsalarm.sh
->9->1- 01 - Down: 10.0.0.1   # здесь я нажимал кнопки "9" "1"
- 02 - Down: 10.0.0.1
[root@localhost smsalarm]# ./smsalarm.sh
->     # здесь я нажимал энтр
->
- 01 - Fixed: 10.0.0.1
- 02 - Fixed: 10.0.0.1
[root@localhost smsalarm]# ./smsalarm.sh
->
->
[root@localhost smsalarm]# ./smsalarm.sh
->9->1- 01 - Down: 10.0.0.1 # здесь я нажимал кнопки "9" "1"
- 02 Down: 10.0.0.1


Что делал скрип, и почему не пришла смс??
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

Skyb писал(а):
17.09.2010 10:58
Я в баше прям новечек новечком...


Не "новечек", а "новичок" все-таки.. но это так, о птичках.


Skyb писал(а):
17.09.2010 10:58
Что делал скрип, и почему не пришла смс??


Я подозреваю, что скрипт сделал все правильно -) Если вы не хотите разбираться в скрипте полностью, то для нормального использования вам придеться разобраться хотя бы в двух вещах:
- где писать свои данные,
- как определить "собственно действия".

Насчет "где писать свои данные" я уже писал выше. Думаю, здесь проблем быть не должно.

Под "собственно действиями" я имею в виду проверку пинга и отправку смс. Эти действия вынесены в функции, поэтому для изменения вызовов команд пинга и смс надо всего лишь исправить функции func_check_node() и func_send_msg() (параметры, которые они должны принимать, а также, что должны вовзращать, описано в комментах), которые выделены вот так

Код: Выделить всё

# Функции (начало) {{{
...
# Функции (конец) }}}


Если вы посмотрите на то, что там написано сейчас (в том варианте, что я выложил)

Код: Выделить всё

func_check_node() {
    # Параметры:
    #    1   - node
    # Возвращаемое значение:
    #    0   - успех,
    #    >0  - неудача.

    # Для отладки:
    declare -i repl=0
    read -p'->' -n1 -r repl
    return $repl

    /bin/ping -c 5 $1 > /dev/null 2> /dev/null
    return $?
}

func_send_msg() {
    # Параметры:
    # 1 - telephone
    # 2 - message
    # Возвращаемое значение:
    #    0   - успех,
    #    >0  - неудача.

    # Для отладки:
    echo - $1 - $2
    return $?

    /usr/bin/scmxx --device=/dev/ttyACM0 --send --sms --direct --number=$1 --text="$2"
    return $?
}


Т.е, как вы видите, тут включена отладка вместо настоящей проверки пинга и отправки смс. Для настоящей проверки пинга и отправки смс, сотрите (или закомментируйте) все содержимое функции и напишите туда нужные команды (там, впрочем, уже написаны команды из вашего скрипта, просто они не выполняются, тк перед ними стоит return). Например, вот так должно работать

Код: Выделить всё

func_check_node() {
    # Параметры:
    #    1   - node
    # Возвращаемое значение:
    #    0   - успех,
    #    >0  - неудача.

    /bin/ping -c 5 $1 > /dev/null 2> /dev/null
    return $?
}

func_send_msg() {
    # Параметры:
    # 1 - telephone
    # 2 - message
    # Возвращаемое значение:
    #    0   - успех,
    #    >0  - неудача.

    /usr/bin/scmxx --device=/dev/ttyACM0 --send --sms --direct --number=$1 --text="$2"
    return $?
}



PS. Советую использовать вторую версию, она все-таки более user-friendly -) И на забудьте поменять телефоны ^)

UPD. Небольшие изменения во второй версии скрипта.

UPD2. Второй скрипт обновлен до версии 2.1 .
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Запустил в тест, о результатах буду отписывать!!!
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Насколько я понял

Код: Выделить всё

declare -a node_t_B=(
    [node_tDelay]='20'
    [node_tList]='B1' 'B2' 'B3'
здесь '20' -> это время задержки отправки сообщений???? если да, то в чем оно измеряется??
Почему спрашиваю - просто если допустим ставить 10 минут - 600 сек. то он отправляет не через 10 а через 20 минут...
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

Время измеряется в секундах. Но фактическое время отправки может быть больше:

Код: Выделить всё

    узел
    недоступен   S1       T1         S2
    --|--------------|--------|----------|
      |---------------------->|
        10 минут


Сообщение будет отправлено, когда скрипт заметит, что интервал прошел, а не когда пройдет ровно 10 минут. Т.е если интервал стоит 10 минут, а скрипт был запущен (по cron-у или как вы его запускаете) в моменты времени S1 и S2, то сообщение будет отправлено только в S2 (тк в S1 еще рано), а не в T1. Т.е фактический интервал получится

Код: Выделить всё

(10 минут + (S2 - T1))


PS. Картинка немного кривая.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Ясно, и последний момент, а можно как то за логировать то что выполняет скрипт??? тоесть хотелось бы видеть что он делает, когда и какие команды шлет. Мало ли....потом понадобиться для разбора чего-нибудь.
Верней как, перенаправить вывод в файл то можно и в кроне, но тогда будет непонятно что где, можно как то сделать нечто вида
Птн Сен 24 09:29:39 YAKST 2010 10.0.0.1 up
Птн Сен 24 09:29:57 YAKST 2010 10.0.0.2 down
чтоб понятно было когда событие произошло :)
UPD
Вопрос отпал, поставил в самом начале скрипта команду date и все заработало :)
Правда я так думаю нужно ловить вывод и там где-то вставлять date. Если подскажите будет прикольней :)
Так, вопрос отпал

Код: Выделить всё

echo `date` " Node '$node' up"
вот так нада :)
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

Skyb писал(а):
24.09.2010 03:04
Ясно, и последний момент, а можно как то за логировать то что выполняет скрипт??? тоесть хотелось бы видеть что он делает, когда и какие команды шлет.

Возможно, будет еще 3-я версия скрипта и там будет мнооогоо изменений -) Я учту ваши пожелания.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Что я не победил так это размер логов, и чтоб их было допустим 3 штуки. Как в системных логах, тоесть 1 актуальный, а 2 других это предыдущие логи в заархивированном состояни, а все остальное бы просто удалялось :)
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
allez
Сообщения: 2223
Статус: Не очень злой админ :-)
ОС: SuSE, CentOS, FreeBSD, Windows

Re: Скрипт отправки смс сообщений

Сообщение allez »

Так вы бы и пользовались системным механизмом ведения и ротации логов. В данном случае вам могут оказаться полезны man logger и man logrotate.
Спасибо сказали:
Аватара пользователя
Skyb
Сообщения: 967
ОС: RFremix 18

Re: Скрипт отправки смс сообщений

Сообщение Skyb »

Теструю, спасибо за ссылки :) век живи век учись
....ещё такой вопрос, в каком месте указать путь для файлов узлов в которых вписывается время??
UPD
allez Все заработало, спасибо
C:\windows> ifconfig
"ifconfig" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: Скрипт отправки смс сообщений

Сообщение sgfault »

Skyb писал(а):
27.09.2010 09:40
....ещё такой вопрос, в каком месте указать путь для файлов узлов в которых вписывается время??

Прошу прощения за запоздалый ответ -) Измените значение этой переменной на то, что вам нужно

Код: Выделить всё

declare tmp_path="."

Спасибо сказали: