Automating the turning on/off the mail forwarding in Zimbra
Система состоит из 6 исполняемых файлов-скриптов, 5 на shell-script, 1 на #awk, разбитых на две группы: ввод данных и обработка.
Ввод (и частичная обработка) выполняется по цепочке:
zimtest --> zimtest1
zimtest1 вызывает rrgtest и при необходимости zmonoff
Автоматическая обработка по расписанию выполняется zimparser, который вызывает zimawk.awk и zmonoff.
ZIMTEST
Сценарий-обёртка. При запуске без параметров выдает наикратчайшую справку. Запускается с параметрами:
zimtest дата_начала дата_окончания кого на_кого
Все параметры обязательны.
Обе даты задавать в русском формате дд.мм.гггг, год обязательно 4 цифрами. В качестве разделителя полей даты можно использовать точку, прямой слэш или их комбинацию - все разделители потом приводятся к слэшам.
В качестве кого можно указывать как полный емайл (напр. pupkin_vasya@наш_домен), так и только именную его часть (pupkin_vasya) - при проверке имени зимброй работают оба варианта.
На_кого - только полный емайл, поскольку эта строка просто будет вписана в соответствующее поле LDAP для кого. (однако, валидность этого адреса тоже проверяется и вписать несуществующего получателя не удастся)
Емайлы должны принадлежать нашему домену - автоматическая настройка автоматической пересылки на другие домены не разрешается.
ZIMTEST1
Основной скрипт ввода. Использует два скрипта - rregtest и zmonoff - для выполнения отдельных операций. rregtest используется как подключаемая библиотека, содержащая функцию проверки допустимости даты. Поскольку со времени начальной разработки эта функция сильно упростилась (я отдал все лишние проверки операционной системе), то возможно, что функция будет включена в состав zimtest1, а rregtest будет исключен из "комплекса".
zmonoff (ZiMbra ON/OFF) - скрипт, выполняющий, собственно, включение/выключение переадресации. Используется здесь в случаях, когда дата начала переадресации уже прошла или наступила сегодня для немедленного включения переадресации, либо когда дата отключения уже прошла или наступила сегодня, для немедленного отключения.
Переданные даты отдаются на проверку сценарию rregtest, а емайлы проверяются внутри скрипта силами зимбры. Изначально, опять же, я писал валидатор для емайлов, но потом отказался от него и запускаю zmprov ga. Чтобы не делать пустую проверку, для красоты получаю в ее ходе от зимбры человеческое имя пользователя. Пока оно просто выводится на экран, но в теории может пригодиться для чего-нибудь полезного.
Для тех же красивостей вычисляется количество дней между начальной и конечной датами действия переадресации. Сходный алгоритм используется чуть позже для вычисления времени, оставшегося до включения/отключения переадресации.
После проверки "а не указана ли конечная дата раньше начальной?" начинается собственно формирование файла данных для обработки по крону.
Если выясняется, что переданная дата уже наступила, то действие выполняется немедленно и в файл данных для дальнейшей обработки ничего не заносится. Проверка на случай, когда обе даты в прошлом, не делается и если так и есть, то сначала переадресация включается, а потом сразу выключается. Не очень оптимально, но уже лень было делать лишнюю проверку.
Если же переданная дата еще не наступила, то информация о ней дописывается в конец файла данных zmsched.dat в виде строки:
@секунды:действие:аккаунт_кого:емайл_на_кого:дата_по-русски
Например:
Переадресовывать почту Васи Пупкина на Петю Попкина с 25.09.2013
@1380027600:on:pupkin_vasya:popkin_petya@my.domain:25.09.2013
Отменить с 25.09.2013 переадресацию почты Васи Пупкина. Поле емайл_на_кого пусто, но оно есть - это строка нулевой длины между третьим и четвертым двоеточиями.
@1380027600:off:pupkin_vasya::25.09.2013
@секунды - дата выполнения действия в формате
date -d "дата 0:00" +@%s
то есть, количество секунд с начала "эпохи" до нуля часов нужного дня. Такой формат выбран для удобства сравнения в обработчике.
действие - принимает одно из трех возможных значений: on, off, skip. Действие skip будет описано ниже в описании обработчика zimawk.awk. Действия on и off самоочевидны: включить или выключить переадресацию для аккаунт_кого, указав, в случае включения емайл_на_кого.
аккаунт_кого - либо имя пользователя либо емайл пользователя, чью почту переадресовывать. Как говорилось выше, в этом поле достаточно использовать только имя аккаунта. Емайл тоже можно, но это целесообразно только при наличии на сервере нескольких доменов. Когда домен один, достаточно имени пользователя.
емайл_на_кого - емайл и только емайл, на который будет переадресовываться почта. Это просто строка, которая будет помещаться в соответствующее поле в LDAP-записи про аккаунт_кого. Если указано действие off, то содержимое этого поля может быть пустым - оно никак не анализируется в такой ситуации, хотя и обязано присутствовать.
дата_по-русски - то же самое, что @секунды, но в формате дд.мм.гггг. Поле добавлено только для удобочитаемости человеком. Теоретически возможно отказаться от первого поля @секунды, чтобы файл можно было править вообще вручную.
RREGTEST
Подключаемая библиотека-сценарий. Содержит только одну функцию validdate(), которая изначально задумывалась как полноценный валидатор дат, но позже была упрощена.
Дата проверяется на минимальную правильность по шаблону, примерно соответствующему дате в русском формате дд.мм.гггг. Причем этот шаблон очень нечеткий и допускает, например, 39.00.2016 (тридцать девятое число нулевого месяца). Это сделано умышленно, чтобы просто удостовериться, что в параметре передана именно дата, а не что-то другое: при любом большем несоответствии скрипт вываливается с exit 1, останавливая работу вызвавших его скриптов.
Дальше дата разбирается на запчасти и день меняется местами с месяцем, образуя дату в американском формате мм/дд/гггг: увы и ах, но юниксы были написаны американцами и штатная date никак не воспринимает на входе русский формат. Японский гггг/мм/дд, кстати, тоже.
Сформированную дату пытаемся вывести на экран, и тут уже пусть date сама проверяет ее допустимость. Неправильно что-то? Вываливаемся!
На случай, если дата допустимая, отдельно проверяется год - он должен быть не меньше предыдущего и не больше следующего. Сейчас просто выводится сообщение, если год неправильный, но это можно использовать для каких-нибудь более жестких проверок
ZIMPARSER
Скрипт-обёртка, запускаемый по крону. Устанавливает некоторые служебные переменные и вызывает собственно обработчик файла настроек.
Файл со скриптом обработчика и файл настроек должны находиться в одном с ним каталоге. Возможно, в будущем их местоположение будет передаваться в параметрах.
ZIMAWK.AWK
Основной обработчик файла данных zimsched.dat. Файл считывается построчно и если строка проходит ряд проверок, выполняется указанное в ней действие.
Проверки:
1. если в строке не пять полей, то строка считается неправильной и не обрабатывается, о чем делается запись в логе
2. если указано действие skip, то эта строка была обработана на прошлом проходе и ее обработка не требуется. Строка пропускается.
Прошедшая эти проверки строка считывается в массив для последующего переформирования файла данных. Если дата действия еще не наступила, то строка дальше не обрабатывается.
В подходящих для обработки строках действие заменяется с on или off на skip, чтобы удалить их на следующем проходе, и вызывается zmonoff для выполнения нужной операции.
После обработки всего файла создается временный файл zmsched.new, в который первой строкой выводится дата/время его создания, а далее построчно дописывается содержимое массива.
Старый файл данных переименовывается в zmsched.DOW, где DOW - номер дня недели, одна цифра, что позволяет вести историю изменений файла данных за последнюю неделю.
Файл zmsched.new копируется под именем zmsched.dat и система готова к новому циклу.
---
Использовались материалы:
Bash Scripting Guide
AWK
Еще awk и sed.
Под катом исходники.
ZIMTEST
#!/bin/bash
if [ "$1" = "" ]
then
echo $(basename $0) START_date FINISH_date FROM_account TO_account
exit
fi
IAmHere=$(pwd)
# формируем файл с планировками включений-отключений
~/zimtest1 $1 $2 $3 $4 $5
cd $IAmHere
ZIMTEST1
#!/bin/bash
MYPATH=`dirname $0`
. $MYPATH/rregtest
SCHED_FILE=$MYPATH/zimsched.dat
echo checking email $3
su -c "zmprov ga $3>/dev/null" - zimbra
if [ $? != 0 ]; then echo Account NOT exists!; exit 1;
else su -c "zmprov ga $3" - zimbra|grep displayName|sed -e "s/displayName/$3/"
fi
echo checking email $4
su -c "zmprov ga $4>/dev/null" - zimbra
if [ $? != 0 ]; then echo Account NOT exists!; exit 1
else su -c "zmprov ga $4" - zimbra|grep displayName|sed -e "s/displayName/$4/"
fi
if [[ "$1" =~ ^[0-9][0-9]\.[0-9][0-9]\.20[1-9][0-9] ]]
then echo $?:$1 seems to be OK
else echo $?:$1 suspected
fi
validdate "$1" #все проверки правильности введенных дат, в т.ч. и на правильность года в функции validdate в файле rregtest
d1=$(date -d $reparsed_date +@%s) #reparsed_date формируется в файле rregtest
if [[ "$2" =~ ^[0-9][0-9]\.[0-9][0-9]\.20[1-9][0-9] ]]
then echo $?:$2 seems to be OK
else echo $?:$2 suspected
fi
validdate "$2"
d2=$(date -d $reparsed_date +@%s)
# чтобы не делать кучу таких преобразований по ходу работы
d1s=$(date -d $d1 +%d.%m.%Y)
d2s=$(date -d $d2 +%d.%m.%Y)
# Для сведения, а может и для дальнейшего использования: количество дней между этими датами
echo Date span: $d2s-$d1s=$(( ($(echo $d2|sed -e 's/@//')-$(echo $d1|sed -e 's/@//'))/3600/24 )) days
# А не перепутаны ли даты?
if [[ $d2 < $d1 ]]
then
echo ERROR: Finish date $d2s BEFORE start date $d1s
exit 1
fi
dtoday=$(date -d "today 0:00" +@%s)
dtodays=$(date -d $dtoday +%d.%m.%Y)
# начинаем формировать команды для исполнения по крону
if [[ $d1 > $dtoday ]]
# сообщаем дату начала переадресации и сколько до нее осталось дней
then
echo Scheduling forward start on $d1s \(days before: $(( ($(echo $d1|sed -e 's/@//')-$(echo $dtoday|sed -e 's/@//'))/3600/24 ))\)
echo $d1:on:$3:$4:$d1s>>$SCHED_FILE
# просроченное активируем сейчас, если оно еще актуально
else
if [[ $d2 > $dtoday ]] #время отмены переадресации еще не наступило?
then
echo Start date $d1s is due. Setting it up right now!
$MYPATH/zmonoff on $3 $4
else
echo Forwarding is overdue: finish date $d2s is in the past. Do NOT set forwarding at all
fi
fi
if [[ $d2 > $dtoday ]]
# сообщаем дату окончания переадресации и сколько до нее осталось дней
then
echo Scheduling forward finish on $d2s \(days before: $(( ($(echo $d2|sed -e 's/@//')-$(echo $dtoday|sed -e 's/@//'))/3600/24 ))\)
echo $d2:off:$3::$d2s>>$SCHED_FILE
# просроченное и сегодняшнее деактивируем сейчас
else
echo Finish date $d2s is due. Resetting it right now!
$MYPATH/zmonoff off $3
fi
echo Finished
exit
RREGTEST
#!/bin/bash
validdate(){
a=$1
if [ "$a" = "" ]; then
echo USAGE: `basename $0` date
echo date = d/m/yyyy
echo \".\" also allowed as date separator
exit 1;
a="3.08/2013";
fi
# проверяем на общее соответствие шаблону
if [[ "$a" =~ [0-3]{0,1}[0-9]{1}(\.|\/)[0-1]{0,1}[0-9]{1}(\.|\/)20(11|12|13|14|15|16) ]]
then echo Pattern matched $BASH_REMATCH;
else echo Pattern NOT matched $?; exit 1;
fi
# Заменяем все точки на слеши, если есть
a=$(echo $a|sed -e 's/\./\//g')
# Разбиваем переданную дату на части и переставляем их местами, дописывая ведущие нули ко дню и месяцу, если надо
reparsed_date=$(awk -F / -v mydate=$a 'BEGIN {$0=mydate;
if (length($2) == 1) {$2="0" $2};
if (length($1) == 1) {$1="0" $1};
print $2 "/" $1 "/" $3}')
# Год пригодится потом
dyo=$(awk -F / -v mydate=$a 'BEGIN {$0=mydate; print $3}') #'
echo reparsed_date $reparsed_date
date -d $reparsed_date +%d.%m.%Y>/dev/null
if [ $? -ne 0 ]; then echo "WRONG DATE PASSED";exit 1;fi
#год в диапазоне текущий-1..текущий+1?
if [ "$dyo" -lt "$[$(date +%Y)-1]" -o "$dyo" -gt "$[$(date +%Y)+1]" ]; then echo BAD YEAR; exit 1;else echo GOOD YEAR; fi
}
ZIMPARSER
#!/bin/bash
# Обрабатываем по крону файл данных, сформированный zimtest-ом
MYPATH=`dirname $0`
SCHED_FILE=$MYPATH/zimsched.dat
AWK_PROG=$MYPATH/zimawk.awk
LOGEXT=`date -d today +%u`
echo $MYPATH>/var/log/zimparser.log.$LOGEXT
awk -F \: -v MYP=$MYPATH -f $AWK_PROG $SCHED_FILE >>/var/log/zimparser.log.$LOGEXT
ZIMAWK.AWK
#!/usr/bin/awk
# формат полей во входном файле:
# дата действия в @-формате
# действие: on/off/skip (действие skip используется как метка для удаления строки при следующем проходе)
# кого переадресовать
# кому переадресовать (игнорируется для действия off. может быть пустым в этом случае)
# дата в формате дд.мм.гггг - просто для удобочитаемости человеком
# возможно стоит рассмотреть вариант, когда первое поле будет игнорироваться, а вместо него будет парситься только $5
# тогда будет возможна правка файла человеком без пересчета даты в @-формат
BEGIN {
print "AWK Started from " MYP
i = 0 #счетчик цикла на всякий случай
s[i,i]="" # массив прочитанных строк - будем из него формировать новый файл. Наверное.
# вот так хитро получаем дату (gawk.pdf p.75 4.9.6)
"date -d \"today 0:00\" +@%s" | getline today
sub(/\@/,//,today)
# получаем имя переданного файла без расширения - его потом будем подставлять при переименовании
("basename " ARGV[1] " " substr(ARGV[1],length(ARGV[1])-3))|getline myfile
# и его же - с расширением, чтбы не было некрасивых заморочек с путями при вызове не из текущего каталога при использовании ARGV
("basename " ARGV[1])|getline myfileExt
}
# красиво печатаем номер строки. Ага, для понтов.
{i++;printf "%0.3d:",i}
# пропускаем все строки, где нет 5 полей
NF != 5 {print "Неправильная строка: [" $0 "]"; next}
# пропускаем отработанные (== - сравнение, = - присвоение)
# не факт, что это понадобится, но пусть пока будет
$2 == "skip" {print $5 " уже отработано";next}
# обрабатываем правильные строки
{
# строку поэлементно запихиваем в массив - из него будем формировать новый файл
# для удобства отработанным ставим skip, чтобы удалить их при следующем проходе
# for (j=1;j<=5;j++) {s[i,j]=$j; printf "[%s]",s[i,j]}
# print
for (j=1;j<=5;j++) {s[i,j]=$j}
# отрезаем лидирующий @ из поля даты, чтобы не мешал сравнениям
sub(/\@/,//,$1)
}
# пропускаем те, чья дата еще не наступила
# если будем выводить массив в файл, то еще необработанные строки надо сохранить в массиве, а уж потом пропустить
$1 > today {print $5 " еще рано, пропускаем";next}
{
sub(/on/, "ВКЛЮЧИТЬ", $2)
sub(/off/, "ВыКЛЮЧИТЬ", $2)
print $2 " " $5 " " $3
# вызов скрипта включения/отключения
system(MYP "/zmonoff " s[i,2] " " s[i,3] " " s[i,4])
# помечаем строку как обработанную
s[i,2]="skip"
}
END {
mynewfile=MYP "/" myfile ".new"
"date"|getline JustNow
print "New results: [" JustNow "]" >mynewfile
for (k=1;k<=i;k++) {
if (s[k,1] != "") {
printf "%0.3d:",k
for (j=1;j<=4;j++) {
# printf "%s:",s[k,j]
printf "%s:",s[k,j]>>mynewfile
}
# print s[k,5]
print s[k,5]>>mynewfile
}
}
# для переименования получаем номер текущего дня недели
"date -d today +%u"|getline dayext
print "\nMoving " MYP "/" myfileExt " to " MYP "/" myfile "." dayext
mvcmd="mv " MYP "/" myfileExt " " MYP "/" myfile "." dayext
print mvcmd
system(mvcmd)
print "Copying new file " MYP "/" myfile ".new to " MYP "/" myfileExt
cpcmd="cp " mynewfile " " MYP "/" myfileExt
print cpcmd
system(cpcmd)
print "AWK Finished"}
ZMONOFF
#!/bin/bash
PrintResult(){
if [ "$1" = 0 ]
then echo OK;
else echo ERROR: $1;
fi
}
if [ "$1" = $"on" ]
then
echo Turning forwarding ON
su -c "zmprov ma $2 zimbraFeatureMailForwardingEnabled TRUE" - zimbra
PrintResult $?
echo Setting forwarding address
su -c "zmprov ma $2 zimbraPrefMailForwardingAddress $3" - zimbra
PrintResult $?
else
echo Turning forwarding OFF
su -c "zmprov ma $2 zimbraFeatureMailForwardingEnabled FALSE" - zimbra
PrintResult $?
echo Removing forwarding address
su -c "zmprov ma $2 zimbraPrefMailForwardingAddress \"\"" - zimbra
PrintResult $?
fi
Система состоит из 6 исполняемых файлов-скриптов, 5 на shell-script, 1 на #awk, разбитых на две группы: ввод данных и обработка.
Ввод (и частичная обработка) выполняется по цепочке:
zimtest --> zimtest1
zimtest1 вызывает rrgtest и при необходимости zmonoff
Автоматическая обработка по расписанию выполняется zimparser, который вызывает zimawk.awk и zmonoff.
ZIMTEST
Сценарий-обёртка. При запуске без параметров выдает наикратчайшую справку. Запускается с параметрами:
zimtest дата_начала дата_окончания кого на_кого
Все параметры обязательны.
Обе даты задавать в русском формате дд.мм.гггг, год обязательно 4 цифрами. В качестве разделителя полей даты можно использовать точку, прямой слэш или их комбинацию - все разделители потом приводятся к слэшам.
В качестве кого можно указывать как полный емайл (напр. pupkin_vasya@наш_домен), так и только именную его часть (pupkin_vasya) - при проверке имени зимброй работают оба варианта.
На_кого - только полный емайл, поскольку эта строка просто будет вписана в соответствующее поле LDAP для кого. (однако, валидность этого адреса тоже проверяется и вписать несуществующего получателя не удастся)
Емайлы должны принадлежать нашему домену - автоматическая настройка автоматической пересылки на другие домены не разрешается.
ZIMTEST1
Основной скрипт ввода. Использует два скрипта - rregtest и zmonoff - для выполнения отдельных операций. rregtest используется как подключаемая библиотека, содержащая функцию проверки допустимости даты. Поскольку со времени начальной разработки эта функция сильно упростилась (я отдал все лишние проверки операционной системе), то возможно, что функция будет включена в состав zimtest1, а rregtest будет исключен из "комплекса".
zmonoff (ZiMbra ON/OFF) - скрипт, выполняющий, собственно, включение/выключение переадресации. Используется здесь в случаях, когда дата начала переадресации уже прошла или наступила сегодня для немедленного включения переадресации, либо когда дата отключения уже прошла или наступила сегодня, для немедленного отключения.
Переданные даты отдаются на проверку сценарию rregtest, а емайлы проверяются внутри скрипта силами зимбры. Изначально, опять же, я писал валидатор для емайлов, но потом отказался от него и запускаю zmprov ga. Чтобы не делать пустую проверку, для красоты получаю в ее ходе от зимбры человеческое имя пользователя. Пока оно просто выводится на экран, но в теории может пригодиться для чего-нибудь полезного.
Для тех же красивостей вычисляется количество дней между начальной и конечной датами действия переадресации. Сходный алгоритм используется чуть позже для вычисления времени, оставшегося до включения/отключения переадресации.
После проверки "а не указана ли конечная дата раньше начальной?" начинается собственно формирование файла данных для обработки по крону.
Если выясняется, что переданная дата уже наступила, то действие выполняется немедленно и в файл данных для дальнейшей обработки ничего не заносится. Проверка на случай, когда обе даты в прошлом, не делается и если так и есть, то сначала переадресация включается, а потом сразу выключается. Не очень оптимально, но уже лень было делать лишнюю проверку.
Если же переданная дата еще не наступила, то информация о ней дописывается в конец файла данных zmsched.dat в виде строки:
@секунды:действие:аккаунт_кого:емайл_на_кого:дата_по-русски
Например:
Переадресовывать почту Васи Пупкина на Петю Попкина с 25.09.2013
@1380027600:on:pupkin_vasya:popkin_petya@my.domain:25.09.2013
Отменить с 25.09.2013 переадресацию почты Васи Пупкина. Поле емайл_на_кого пусто, но оно есть - это строка нулевой длины между третьим и четвертым двоеточиями.
@1380027600:off:pupkin_vasya::25.09.2013
@секунды - дата выполнения действия в формате
date -d "дата 0:00" +@%s
то есть, количество секунд с начала "эпохи" до нуля часов нужного дня. Такой формат выбран для удобства сравнения в обработчике.
действие - принимает одно из трех возможных значений: on, off, skip. Действие skip будет описано ниже в описании обработчика zimawk.awk. Действия on и off самоочевидны: включить или выключить переадресацию для аккаунт_кого, указав, в случае включения емайл_на_кого.
аккаунт_кого - либо имя пользователя либо емайл пользователя, чью почту переадресовывать. Как говорилось выше, в этом поле достаточно использовать только имя аккаунта. Емайл тоже можно, но это целесообразно только при наличии на сервере нескольких доменов. Когда домен один, достаточно имени пользователя.
емайл_на_кого - емайл и только емайл, на который будет переадресовываться почта. Это просто строка, которая будет помещаться в соответствующее поле в LDAP-записи про аккаунт_кого. Если указано действие off, то содержимое этого поля может быть пустым - оно никак не анализируется в такой ситуации, хотя и обязано присутствовать.
дата_по-русски - то же самое, что @секунды, но в формате дд.мм.гггг. Поле добавлено только для удобочитаемости человеком. Теоретически возможно отказаться от первого поля @секунды, чтобы файл можно было править вообще вручную.
RREGTEST
Подключаемая библиотека-сценарий. Содержит только одну функцию validdate(), которая изначально задумывалась как полноценный валидатор дат, но позже была упрощена.
Дата проверяется на минимальную правильность по шаблону, примерно соответствующему дате в русском формате дд.мм.гггг. Причем этот шаблон очень нечеткий и допускает, например, 39.00.2016 (тридцать девятое число нулевого месяца). Это сделано умышленно, чтобы просто удостовериться, что в параметре передана именно дата, а не что-то другое: при любом большем несоответствии скрипт вываливается с exit 1, останавливая работу вызвавших его скриптов.
Дальше дата разбирается на запчасти и день меняется местами с месяцем, образуя дату в американском формате мм/дд/гггг: увы и ах, но юниксы были написаны американцами и штатная date никак не воспринимает на входе русский формат. Японский гггг/мм/дд, кстати, тоже.
Сформированную дату пытаемся вывести на экран, и тут уже пусть date сама проверяет ее допустимость. Неправильно что-то? Вываливаемся!
На случай, если дата допустимая, отдельно проверяется год - он должен быть не меньше предыдущего и не больше следующего. Сейчас просто выводится сообщение, если год неправильный, но это можно использовать для каких-нибудь более жестких проверок
ZIMPARSER
Скрипт-обёртка, запускаемый по крону. Устанавливает некоторые служебные переменные и вызывает собственно обработчик файла настроек.
Файл со скриптом обработчика и файл настроек должны находиться в одном с ним каталоге. Возможно, в будущем их местоположение будет передаваться в параметрах.
ZIMAWK.AWK
Основной обработчик файла данных zimsched.dat. Файл считывается построчно и если строка проходит ряд проверок, выполняется указанное в ней действие.
Проверки:
1. если в строке не пять полей, то строка считается неправильной и не обрабатывается, о чем делается запись в логе
2. если указано действие skip, то эта строка была обработана на прошлом проходе и ее обработка не требуется. Строка пропускается.
Прошедшая эти проверки строка считывается в массив для последующего переформирования файла данных. Если дата действия еще не наступила, то строка дальше не обрабатывается.
В подходящих для обработки строках действие заменяется с on или off на skip, чтобы удалить их на следующем проходе, и вызывается zmonoff для выполнения нужной операции.
После обработки всего файла создается временный файл zmsched.new, в который первой строкой выводится дата/время его создания, а далее построчно дописывается содержимое массива.
Старый файл данных переименовывается в zmsched.DOW, где DOW - номер дня недели, одна цифра, что позволяет вести историю изменений файла данных за последнюю неделю.
Файл zmsched.new копируется под именем zmsched.dat и система готова к новому циклу.
---
Использовались материалы:
Bash Scripting Guide
AWK
Еще awk и sed.
Под катом исходники.
ZIMTEST
#!/bin/bash
if [ "$1" = "" ]
then
echo $(basename $0) START_date FINISH_date FROM_account TO_account
exit
fi
IAmHere=$(pwd)
# формируем файл с планировками включений-отключений
~/zimtest1 $1 $2 $3 $4 $5
cd $IAmHere
ZIMTEST1
#!/bin/bash
MYPATH=`dirname $0`
. $MYPATH/rregtest
SCHED_FILE=$MYPATH/zimsched.dat
echo checking email $3
su -c "zmprov ga $3>/dev/null" - zimbra
if [ $? != 0 ]; then echo Account NOT exists!; exit 1;
else su -c "zmprov ga $3" - zimbra|grep displayName|sed -e "s/displayName/$3/"
fi
echo checking email $4
su -c "zmprov ga $4>/dev/null" - zimbra
if [ $? != 0 ]; then echo Account NOT exists!; exit 1
else su -c "zmprov ga $4" - zimbra|grep displayName|sed -e "s/displayName/$4/"
fi
if [[ "$1" =~ ^[0-9][0-9]\.[0-9][0-9]\.20[1-9][0-9] ]]
then echo $?:$1 seems to be OK
else echo $?:$1 suspected
fi
validdate "$1" #все проверки правильности введенных дат, в т.ч. и на правильность года в функции validdate в файле rregtest
d1=$(date -d $reparsed_date +@%s) #reparsed_date формируется в файле rregtest
if [[ "$2" =~ ^[0-9][0-9]\.[0-9][0-9]\.20[1-9][0-9] ]]
then echo $?:$2 seems to be OK
else echo $?:$2 suspected
fi
validdate "$2"
d2=$(date -d $reparsed_date +@%s)
# чтобы не делать кучу таких преобразований по ходу работы
d1s=$(date -d $d1 +%d.%m.%Y)
d2s=$(date -d $d2 +%d.%m.%Y)
# Для сведения, а может и для дальнейшего использования: количество дней между этими датами
echo Date span: $d2s-$d1s=$(( ($(echo $d2|sed -e 's/@//')-$(echo $d1|sed -e 's/@//'))/3600/24 )) days
# А не перепутаны ли даты?
if [[ $d2 < $d1 ]]
then
echo ERROR: Finish date $d2s BEFORE start date $d1s
exit 1
fi
dtoday=$(date -d "today 0:00" +@%s)
dtodays=$(date -d $dtoday +%d.%m.%Y)
# начинаем формировать команды для исполнения по крону
if [[ $d1 > $dtoday ]]
# сообщаем дату начала переадресации и сколько до нее осталось дней
then
echo Scheduling forward start on $d1s \(days before: $(( ($(echo $d1|sed -e 's/@//')-$(echo $dtoday|sed -e 's/@//'))/3600/24 ))\)
echo $d1:on:$3:$4:$d1s>>$SCHED_FILE
# просроченное активируем сейчас, если оно еще актуально
else
if [[ $d2 > $dtoday ]] #время отмены переадресации еще не наступило?
then
echo Start date $d1s is due. Setting it up right now!
$MYPATH/zmonoff on $3 $4
else
echo Forwarding is overdue: finish date $d2s is in the past. Do NOT set forwarding at all
fi
fi
if [[ $d2 > $dtoday ]]
# сообщаем дату окончания переадресации и сколько до нее осталось дней
then
echo Scheduling forward finish on $d2s \(days before: $(( ($(echo $d2|sed -e 's/@//')-$(echo $dtoday|sed -e 's/@//'))/3600/24 ))\)
echo $d2:off:$3::$d2s>>$SCHED_FILE
# просроченное и сегодняшнее деактивируем сейчас
else
echo Finish date $d2s is due. Resetting it right now!
$MYPATH/zmonoff off $3
fi
echo Finished
exit
RREGTEST
#!/bin/bash
validdate(){
a=$1
if [ "$a" = "" ]; then
echo USAGE: `basename $0` date
echo date = d/m/yyyy
echo \".\" also allowed as date separator
exit 1;
a="3.08/2013";
fi
# проверяем на общее соответствие шаблону
if [[ "$a" =~ [0-3]{0,1}[0-9]{1}(\.|\/)[0-1]{0,1}[0-9]{1}(\.|\/)20(11|12|13|14|15|16) ]]
then echo Pattern matched $BASH_REMATCH;
else echo Pattern NOT matched $?; exit 1;
fi
# Заменяем все точки на слеши, если есть
a=$(echo $a|sed -e 's/\./\//g')
# Разбиваем переданную дату на части и переставляем их местами, дописывая ведущие нули ко дню и месяцу, если надо
reparsed_date=$(awk -F / -v mydate=$a 'BEGIN {$0=mydate;
if (length($2) == 1) {$2="0" $2};
if (length($1) == 1) {$1="0" $1};
print $2 "/" $1 "/" $3}')
# Год пригодится потом
dyo=$(awk -F / -v mydate=$a 'BEGIN {$0=mydate; print $3}') #'
echo reparsed_date $reparsed_date
date -d $reparsed_date +%d.%m.%Y>/dev/null
if [ $? -ne 0 ]; then echo "WRONG DATE PASSED";exit 1;fi
#год в диапазоне текущий-1..текущий+1?
if [ "$dyo" -lt "$[$(date +%Y)-1]" -o "$dyo" -gt "$[$(date +%Y)+1]" ]; then echo BAD YEAR; exit 1;else echo GOOD YEAR; fi
}
ZIMPARSER
#!/bin/bash
# Обрабатываем по крону файл данных, сформированный zimtest-ом
MYPATH=`dirname $0`
SCHED_FILE=$MYPATH/zimsched.dat
AWK_PROG=$MYPATH/zimawk.awk
LOGEXT=`date -d today +%u`
echo $MYPATH>/var/log/zimparser.log.$LOGEXT
awk -F \: -v MYP=$MYPATH -f $AWK_PROG $SCHED_FILE >>/var/log/zimparser.log.$LOGEXT
ZIMAWK.AWK
#!/usr/bin/awk
# формат полей во входном файле:
# дата действия в @-формате
# действие: on/off/skip (действие skip используется как метка для удаления строки при следующем проходе)
# кого переадресовать
# кому переадресовать (игнорируется для действия off. может быть пустым в этом случае)
# дата в формате дд.мм.гггг - просто для удобочитаемости человеком
# возможно стоит рассмотреть вариант, когда первое поле будет игнорироваться, а вместо него будет парситься только $5
# тогда будет возможна правка файла человеком без пересчета даты в @-формат
BEGIN {
print "AWK Started from " MYP
i = 0 #счетчик цикла на всякий случай
s[i,i]="" # массив прочитанных строк - будем из него формировать новый файл. Наверное.
# вот так хитро получаем дату (gawk.pdf p.75 4.9.6)
"date -d \"today 0:00\" +@%s" | getline today
sub(/\@/,//,today)
# получаем имя переданного файла без расширения - его потом будем подставлять при переименовании
("basename " ARGV[1] " " substr(ARGV[1],length(ARGV[1])-3))|getline myfile
# и его же - с расширением, чтбы не было некрасивых заморочек с путями при вызове не из текущего каталога при использовании ARGV
("basename " ARGV[1])|getline myfileExt
}
# красиво печатаем номер строки. Ага, для понтов.
{i++;printf "%0.3d:",i}
# пропускаем все строки, где нет 5 полей
NF != 5 {print "Неправильная строка: [" $0 "]"; next}
# пропускаем отработанные (== - сравнение, = - присвоение)
# не факт, что это понадобится, но пусть пока будет
$2 == "skip" {print $5 " уже отработано";next}
# обрабатываем правильные строки
{
# строку поэлементно запихиваем в массив - из него будем формировать новый файл
# для удобства отработанным ставим skip, чтобы удалить их при следующем проходе
# for (j=1;j<=5;j++) {s[i,j]=$j; printf "[%s]",s[i,j]}
for (j=1;j<=5;j++) {s[i,j]=$j}
# отрезаем лидирующий @ из поля даты, чтобы не мешал сравнениям
sub(/\@/,//,$1)
}
# пропускаем те, чья дата еще не наступила
# если будем выводить массив в файл, то еще необработанные строки надо сохранить в массиве, а уж потом пропустить
$1 > today {print $5 " еще рано, пропускаем";next}
{
sub(/on/, "ВКЛЮЧИТЬ", $2)
sub(/off/, "ВыКЛЮЧИТЬ", $2)
print $2 " " $5 " " $3
# вызов скрипта включения/отключения
system(MYP "/zmonoff " s[i,2] " " s[i,3] " " s[i,4])
# помечаем строку как обработанную
s[i,2]="skip"
}
END {
mynewfile=MYP "/" myfile ".new"
"date"|getline JustNow
print "New results: [" JustNow "]" >mynewfile
for (k=1;k<=i;k++) {
if (s[k,1] != "") {
printf "%0.3d:",k
for (j=1;j<=4;j++) {
# printf "%s:",s[k,j]
printf "%s:",s[k,j]>>mynewfile
}
# print s[k,5]
print s[k,5]>>mynewfile
}
}
# для переименования получаем номер текущего дня недели
"date -d today +%u"|getline dayext
print "\nMoving " MYP "/" myfileExt " to " MYP "/" myfile "." dayext
mvcmd="mv " MYP "/" myfileExt " " MYP "/" myfile "." dayext
print mvcmd
system(mvcmd)
print "Copying new file " MYP "/" myfile ".new to " MYP "/" myfileExt
cpcmd="cp " mynewfile " " MYP "/" myfileExt
print cpcmd
system(cpcmd)
print "AWK Finished"}
ZMONOFF
#!/bin/bash
PrintResult(){
if [ "$1" = 0 ]
then echo OK;
else echo ERROR: $1;
fi
}
if [ "$1" = $"on" ]
then
echo Turning forwarding ON
su -c "zmprov ma $2 zimbraFeatureMailForwardingEnabled TRUE" - zimbra
PrintResult $?
echo Setting forwarding address
su -c "zmprov ma $2 zimbraPrefMailForwardingAddress $3" - zimbra
PrintResult $?
else
echo Turning forwarding OFF
su -c "zmprov ma $2 zimbraFeatureMailForwardingEnabled FALSE" - zimbra
PrintResult $?
echo Removing forwarding address
su -c "zmprov ma $2 zimbraPrefMailForwardingAddress \"\"" - zimbra
PrintResult $?
fi
Комментариев нет:
Отправить комментарий
Пожалуйста, воздержитесь от грубостей и персональных нападок.
Я не против матерщины, но она должна быть уместной и использоваться для выражения эмоций, а не в качестве основного средства выражения мыслей.