среда, сентября 03, 2014

Perl. Не используйте конструкцию "return $a or $b"

На компе недавно обновился перл до версии 5.20 и в рабочем коде словил варнинг "Possible precedence issue with control flow operator". Гугление показало, то, оказывается, перл код вида
sub mysub {
    my ( $a, $b ) = @_;
    return $a or $b;
}
интерпретатором будет воспринят как
sub mysub {
    my ( $a, $b ) = @_;
    ( return $a ) or $b;
}
т.е. функция/метод всегда будет возвращать $a в не зависимости от того, истиннен он или ложен. Поэтому в данном случае надо использовать оператор || вместо or хотя я всегда считал, что два обозначения одного и того же оператора. И в итоге наша функция будет выглядеть так:
sub mysub {
    my ( $a, $b ) = @_;
    return $a || $b;
}

воскресенье, июля 06, 2014

Next Gen PBX

Возвращаюсь к теме создания софта для ip-телефонии. Тема для меня не перестала быть интересной даже спустя год после смены деятельности с администрирования на программирование.
Уже больше года вынашиваю идею создания платформы для ip-телефонии с реализацией виртуализации на уровне самой платформы. Т.е., например, есть оператор связи мелкой или средней руки, который получил лицензию на телематические услуги и взял у ФАС пул городских номеров. И вот хочется этому оператору со своих клиентов стричь деньги не только за предоставление городского номера и телефонных линий, а, например, за услугу виртуальной АТС. Это чтобы клиент вообще не заморачивался с оборудованием, а только купил/взял в аренду телефонные аппараты.
И вот тут выходит на сцену платформа для предоставления услуг виртуальных АТС. Таких решений на рынке пока немного и все они, как правило, платные и очень(просто космически) дорогие или топорно сделанные(или и то и другое). Я хочу создать опенсорсную систему на базе IP PBX FreeSWITCH. Выбор данной pbx(а не того же астериска) обусловлен некоторыми факторами, главными из которых являются более серьёзный подход к разработке системы(этой мой взгляд со стороны), использование в качестве SIP-модуля библиотеки sofia, возможность(вроде как неиллюзорная) использовать платы расширения digium|sangoma|openvox, ну и вообще, за 8 лет астериск уже как-то поприелся, хочется чего-то свежего.
В настоящий момент пилю систему для аудио-конференций, планирую в будущем включить данную систему в состав телефонной платформы. Сейчас же просто, как проба пера, чтобы сформировать окончательный образ будущей системы и понабивать себе руку в написании кода асинхронного приложения.

среда, июля 02, 2014

Perl. Добавляем инкапсуляцию методам класса

Перл - замечательный язык с очень простой и понятной ООП-парадигмой. Из коробки мы имеем полиморфизм и наследование, но, к сожалению, не можем похвастаться инкапсуляцией.
Инкапсуляция, для тех кто не знаком с терминами, это сокрытие методов класса от посторонних лиц. Другими словами, мы в классе создаём различные методы, часть из которых доступна для вызова другими модулями/скриптами. Другая часть не должна быть вызвана нигде, кроме как в самом классе(private-метод) или в классе, который наследуется от данного класса(protected-метод).
В перле, как и в питоне, нельзя штатными средствами перекрыть доступ к методам класса. Применяется негласное соглашение, по которому методы(или функции), имена которых начинаются с нижнего подчёркивания, считаются приватными.

С этим в принципе можно мириться и ваять код на pure-перл, не прибегая к ООП-фреймворкам типа Moose, что я в принципе всегда и делал - меня слегка пугает перспектива тянуть в своё приложение 100500 модулей ради одного функционала.
К счастью, благодаря статье в журнале pragmaticperl я узнал о модуле Attribute::Protected. Данный модуль написал Tatsuhiko Miyagawa, очень известный разработчик в мире Perl.
Модуль достаточно легковесный и имеет в зависимостях только Attribute::Handlers, который давно является модулем ядра perl.
Рассмотрим на примере, как можно с помощью указанного модуля добавить в наш класс инкапсуляцию.
Для наглядности создадим 2 модуля и один скрипт, который будет вызывать оба модуля. Один модуль будет наследоваться от другого.

MyModuleParent.pm :
package MyModuleParent;

use strict;
use warnings;
use Attribute::Protected;

sub new {
    my $class = shift;

    return bless {}, $class;
}

sub _private_method : Private {
    print "This is private method in package " . __PACKAGE__ . "\n";
}

sub _protected_method : Protected {
    print "This is protected method in package " . __PACKAGE__ . "\n";
}

sub public_method {
    print "This is public method in package " . __PACKAGE__ . "\n";
}

1;

MyModule.pm :
package MyModule;

use strict;
use warnings;

use parent 'MyModuleParent';

sub mymethod {
    print "This is simple method in package " . __PACKAGE__ . "\n";
}

sub call_protect_method {
    my $self = shift;
    $self->_protected_method;
}

sub call_private_method {
    my $self = shift;
    $self->_private_method;
}

1;

script.pl :
#!/usr/bin/perl

use strict;
use warnings;

use MyModule;
use MyModuleParent;

my $parent_object = MyModuleParent->new;
my $object = MyModule->new;

$object->public_method;         # OK
$object->mymethod;              # OK
$object->call_protect_method;   # OK
$object->call_private_method;   # NOT OK

$parent_object->public_method;       # OK
$parent_object->_protected_method;   # NOT OK
$parent_object->_private_method;     # NOT OK
Объект "$object" может вызвать публичный метод класса "MyModuleParent", т.к. от него наследуется класс "MyModule". Также, данный объект вызывает методы "call_protect_method" и "call_private_method", вызывающие в свою очередь protect и private методы класса "MyModuleParent" соответственно. Вызов метода "call_private_method" завершится с ошибкой _private_method() is a private method of MyModuleParent!, вызов метода "call_protect_method" завершится успешно. Прямой вызов из скрипта private и protected методов класса "MyModuleParent"(через объект $parent_object) завершится с ошибкой.

Таким образом, мы получаем малой кровью(а оверхед там действительно ничтожный, проверенно в дебаггере) надёжную инкапсуляцию методов, очень похожую на сишную и явовску, да к тому же сделанную почти полностью на базовых средствах языка. Модулю Attribute::Protected уже 13 лет, удивитильно, что его так мало используют.

воскресенье, июня 29, 2014

yet another conference server

Начал пилить демона для управления аудио-конференциями на базе ip-pbx FreeSWITCH. Сам демон будет выступать прослойкой между пользователями и сервером PBX.
В бытность свою работы с ip-телефонией вцелом и астериском в частности, не раз собирался заняться фрисвичом, да всё как-то руки не доходили и задач соответствующих не было. Зато теперь, в чине разработчика, полёт фантазий в плане приложений для телефонии уносит меня за дальние горизонты)) Специально для этих целей даже c++ начал изучать.
Но пока что пишу на перле и anyevent. Проект ещё в начальной стадии, готово процентов 20.
Думаю, что к осени будет готова хотя бы альфа-версия.

суббота, июня 28, 2014

Perl. Получить в модуле полный путь до файла модуля

Возник давеча такой вопрос: надо было в модуле, в не зависимости от того, кто и откуда его запускает, получать текущий PATH данного модуля.
Инструменты типа FindBin и Cwd не работали, т.к. они показывали путь до скрипта, который использовал данный модуль. Недолгое гугление выдало вот такое решение:
use File::Spec;

my $mod_dir = (File::Spec->splitpath( __FILE__ ))[1];
Вызов
File::Spec->splitpath( __FILE__ )
возвращает массив, в котором первым аргументом является имя файла, а вторым как раз будет полный путь от "/" до модуля.

среда, мая 28, 2014

perl 5.20

Сегодня релизнулся. В perlbrew себе собрал, играюсь.
На ЛОР новость ожидаемо вызвала срач адептов, неосилянтов и простомимопроходящих ))

суббота, апреля 19, 2014

*sh: перевод списка пакетов из стобца в строку

Иногда требуется удалить и вычистить определённый список пакетов, притом, хочется получить и список пакетов по-быстрому и без лишних тыканий мышкой. Например, потребовалось удалить все пакеты, относящиеся к libreoffice. Если делать долго, то сначала надо получить список пакетов командой
dpkg -l | grep libreoffice
потом вручную из списка выбрать пакеты и вставить их в команду удаления. Но есть способ ускорить эту операцию переведя список пакетов, относящихся к libreoffice(или к нужному вам пакету) из столбца в строку воспользовавшись командой
dpkg -l | grep libreoffice | awk '{print $2}' | tr '\n' ' '
данная команда переведёт список пакетов из столбца в строку, которую затем можно вставить в команду aptitude purge

вторник, декабря 10, 2013

Git-шпаргалка


1. Переключиться на удалённую ветку минуя master:
1.1. Для начала обновим список веток на удалённом сервере:
git remote update origin --prune
1.2. Затем переключимся на ветку
git checkout --track origin/<имя ветки>
или для старых версий Git
git checkout -b <имя ветки> origin/<имя ветки>
или так
git checkout master
git pull
git checkout <имя ветки>

2. Убрать файл/папку из индексации
git rm -r --cached FILE
git commit -m "delete file from index"
git push origin master
3. Посмотреть файл командой blame до указанного коммита
git blame commitID~1 filename
4. Удалить ветку на сервере
git push -d origin [имя ветки]
5. Посмотреть историю между коммитами
git log --oneline commitID1..commitID2

вторник, мая 07, 2013

Gnome shell

После очередного обновления дебьяна на ноуте перестал запускаться gdm3. Вместо стартового окна наблюдаю картинку о том, что произошла ошибка. Ну да ладно, мы не пальцем деланные - переключился в консоль, погасил gdm3, стартанул иксы через startx.
Вместо привычного fallback-режима запустился стандартный Gnome Shell. После некоторого времени плевания и матюгов вдруг ощутил, что пользоваться им можно и даже с удовольствием. Да, непривычно устроено расположение элементов, до тех, что раньше запускались с одного щелчка, приходится щёлкать дополнительные разы. Но к этому привыкаешь.
Поставил себе несколько расширений с сайта https://extensions.gnome.org и моё рабочее окружение заиграло новыми красками)
Само оформление мне очень нравится, всё такое плавное и изящное, выглядит гораздо удобнее, нежели compiz в своё время.
В общем, для себя решил, что даже когда починят баг с gdm, на fallback-режим уже не вернусь. Надо идти в ногу со временем)

четверг, апреля 18, 2013

Perl. Сортировка ключей хеш-массива по значениям этих ключей



В процессе выполнения тестовых заданий для новой работы столкнулся с задачей вывода сортированных данных.
Итак, имеем структуру:
$VAR1 = bless( {
                 'DOMAINS' => {
                                'yandex.ru' => 1,
                                'INVALID' => 5,
                                'rambler.ru' => 1,
                                'vk.com' => 1,
                                'mail.ru' => 2
                              }, 
               }, 'MyModule' );
необходимо выводить данные хеш-массива DOMAINS, отсортированные по значениям(values) данного массива.
Недолго думая обратился за ответом к замечательной книге Perl: изучаем глубже, в которой было найдено решение:
foreach my $domain (sort { $self->{DOMAINS}->{$b} cmp $self->{DOMAINS}->{$a} } 
                keys $self->{DOMAINS}) {
        print $domain . "\t" . $self->{DOMAINS}->{$domain} . "\n";
    }
В результате получаем то, что требовалось:
INVALID 5
mail.ru 2
yandex.ru 1
rambler.ru 1
vk.com 1

Perl. Перехват warn'ов и carp'ов



Сейчас немного про перл.
В перле есть замечательная функция eval, которая умеет перехватывать критические ошибки, которые были вызваны die и/или croak. Но эта функция не умеет работать с предупреждениями, которые генерируются с помощью функций warn и carp. Ну и что такого, спросите вы, предупреждения для того и существуют, чтобы информировать пользователя/разработчика о некорректной работе скрипта или модуля. Но порой всё-же требуется, чтобы наш скрипт, написанный на перле, умел перехватывать подобные сообщения, сгенерированные модулями или функциями, чтобы, например, обрабатывать подобные сообщения особым образом.

Отображение киррилицы(cp1251/win1251) в Sublime Text 2


Файлы в редакторе Sublime Text 2 по умолчанию открываются в кодировке UTF8, но иногда требуется работать с файлами, которые были созданы в кодировке cp1251(кодировка, используемая по умолчанию в операционных системах семейства Windows).
Для этих целей вы можете переназначить fallback-кодировку, которую будет использовать сублим текст, если файл не в кодировке UTF8.
Для этой цели откройте меню Preferences -> Settings - Default. В открывшемся окне воспользуйтесь поиском(ctrl-f), найдите строку
"fallback_encoding": "Western (Windows 1252)",
и замените её на
"fallback_encoding": "Cyrillic (Windows 1251)", (запятая в конце строки обязательна!).
Сохраните файл. Отлично! Теперь сублим текст умеет открывать файлы в кодировке cp1251 корректно и ваша жизнь станет чуточку проще)

воскресенье, апреля 14, 2013

Добавление в Sublime Text 2 горячих клавиш для работы с языком Perl


В повседневной работе использую этот замечательный редактор. Всем он хорош - подсветка синтаксиса, скорость работы, автоподстановка. Только сильно не хватало мне горячих клавиш для проверки синтаксиса и запуска скрипта на perl. Sublime Text 2 позволяет в данном случае забиндить на стандартную комбинацию(ctrl-b) только одно действие.
После недолгого гугления было найдено решение, написанное для языка Go и без проблем адаптированное для Perl.
Для начала необходимо создать свой плагин. Для этого открываете Tool->New Plugin. Вводите следующий код:
import sublime, sublime_plugin 
   
class RunBuildCommand(sublime_plugin.WindowCommand): 
    def run(self, build_system): 
        self.window.run_command( "set_build_system", {"file": build_system } )
        self.window.run_command( "build" )

Сохраняете данный плагин под именем RunBuild.py
После этого в папке ~/.config/sublime-text-2/Packages/User создаёте два файла следующего содержания:
~/.config/sublime-text-2/Packages/User/Perl-build.sublime-build :
{
    "cmd": ["/usr/bin/perl", "$file"],
    "file_regex": ".* at (.) line ([0-9])",
    "selector": "source.perl"
}

~/.config/sublime-text-2/Packages/User/Perl-check.sublime-build :
{
    "cmd": ["/usr/bin/perl", "-c", "$file"],
    "file_regex": ".* at (.) line ([0-9])",
    "selector": "source.perl"
}

После этого открываете Preferences->Key Binding - User и добавляете следующие строчки:
[
    {
        "keys": ["alt+c"],
        "command": "run_build",
        "args": {
                "build_system": "Packages/User/Perl-check.sublime-build"
        }
    },
    {
        "keys": ["alt+b"],
        "command": "run_build",
        "args": {
                "build_system": "Packages/User/Perl-build.sublime-build"
        }
    }
]
После этого у вас на комбинациях alt-c и alt-b появляются проверка синтаксиса и выполнение скрипта соответственно.

среда, апреля 10, 2013

Меняю направление деятельности


Решил поменять сферу деятельности, переквалифицироваться в программиста.
Если честно, давно уже думал соскочить с админства, т.к. всё это обслуживание телефонного парка порядком приелось, хочется чего-то нового.
Сегодня(уже вчера, получается) было небольшое собеседование по телефону. Отныне буду перловым разработчиком в одной крупной компании. Для того, чтобы меня окончательно взяли на работу необходимо выполнить 2 тестовых задания. Одно уже сделал, со вторым разбираюсь, курю мануалы по фреймворку catalyst. Но с телефонией я не прощаюсь, хочу лишь подойти к ней с другой стороны, с позиции разработчика. Для этого сейчас активно прокачиваю свои навыки в программировании, в частности, читаю умные книги по C/C++. В общем, посмотрим, что будет дальше. Удачи мне.

понедельник, апреля 08, 2013

Проблема с работой Mantis после обновления Postgresql


Столкнулся на работе с такой неприятностью - после обновления СУБД Postgresql с версии 8.4 до версии 9.1 перестали подгружаться прикреплённые файлы в тикетах мантиса. После того, как спала паника начался судорожный поиск решения проблемы. Был даже запилен трабл-тикет на багтрекере мантиса. Но в итоге всё оказалось проще.
В 9-й версии поменяли формат вывода данных типа bytea. Для того, чтобы решить проблему потребовалось ввести в базе мантиса следующий запрос:
ALTER DATABASE bugtracker SET bytea_output='escape'
Данный запрос устанавливает формат вывода bytea таким же, какой был в версии 8.4. После проделанной процедуры всё успешно заработало.

четверг, июня 14, 2012

K&R Язык программирования C. Упражнение 1.19. Напишите функцию reverse(s), которая переписывает свой строковый аргумент в обратном порядке

Занялся на досуге изучением языка си по книжке K&R. Дошёл до задания 1.19. По итогу выполнения решил написать данное сообщение, как памятку себе. Итак, программа выглядит следующим образом:
/* программа, реверсирующая входные строки */
#include <stdio.h>
#define MAXLINE 1000

void reverse(char s[]);
int my_getline(char s[], int);

int main()
{
    int len;
    char line[MAXLINE];

    while((len = my_getline(line, MAXLINE)) > 0) {
        printf("Исходная строка выглядит так: %s", line);
        reverse(line);
        printf("Реверсированная строка выглядит так: %s", line);
    }
    return 0;
}

int my_getline(char s[], int lim)
{
    int i, c;

    for(i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; ++i) {
        s[i] = c;
    }
    if(c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

/* функция reverse: переписывает строки в обратном порядке. Суть работы в следующем:
 т.к. мы знаем, что любая строка заканчивается символом '\0', можно на время удалить
 из строки данный символ и занять его место первым элементом массива. После этого можно
 осуществить последовательный перенос всех элементов. В последствии необходимо будет
 "сдвинуть" полученную строку влево */
void reverse(char s[])
{
    int n, i, c, j;

    c = n = 0;
    while(s[n] != '\0') {
        ++n;
    }
    if(s[n-1] == '\n') {
        c = s[n-1]; //c теперь содержит символ перехода на новую строку
        n = n - 2; //не будем подставлять в качестве первого символа переход на новую строку
    }
    /* выясняем, какое у нас число символов в строке: чётное или нечётное */
    if((n%2) == 0) {
        j = n/2;
    } else {
        j = n/2 + 1;
    }
    /* переносим крайние элементы в массиве */
    s[n+1] = s[0];
    s[0] = s[n];
    /* переносим остальные элементы */
    for(i = 0; i < j; ++i) {
        s[i] = s[n-i];
        s[n-i] = s[i+1];
    }
    /* нужно "сдвинуть" символы влево, начиная с середины */
    for(i = j; i < n + 1; ++i) {
        s[i] = s[i+1];
    }
    if(c == '\n') {
        n = n + 1;
        s[n] = '\n';
    }
    s[n+1] = '\0';
}

понедельник, мая 14, 2012

Генерация ложных гудков в трубку после перевода звонка в отвеченное состояние


Настраивал недавно одному клиенту Asterisk. До этого у них стоял тоже астериск, который был настроен каким то "умельцем" - настолько запутанного диалплана я, наверное, не видел ни разу за свою шестилетнюю практику работы. Но пост не об этом. В общем, переводили мы клиента с "их" астериска на "наш" астериск. Вроде бы всё перенесли и система удачно запустилась, но возник один нюанс - у клиента есть 2 GSM-модема, подключенных к FXO-портам шлюзов Linksys SPA3102. И через эти модемы осуществляются звонки на Московские мобильные. На старой системе, при звонке через модемы, в трубку абоненту генерился ложный гудок до тех пор, пока не начинал генерироваться настоящий гудок вызываемой стороной. Ничего сложного, подумал я - поставим опцию r и дело с концом. Но всё оказалось несколько сложнее.Оказывается, при звонке на порт FXO линксис, GSM-модем, подключенный к этому порту сразу же переводил канал в отвеченное состояние и давал в трубку тишину в течение ~20 секунд до тех пор, пока не начинала "гудеть" вызываемая сторона. Скажу честно, с такой проблемой я столкнулся впервые, а клиент говорил, что на старой системе всё работало.
После нескольких дней курения диалпланов предыдущей системы и мануалов в интернете я был в тупике, но, к счастью, интуиция и опыт подсказали ответ - опция G в команде Dial. В описании этой опции говорится:
G(context^exten^pri): If the call is answered, transfer both parties to the specified context and extension. The calling party is transferred to priority x, and the called party to priority x+1. This allows the dialplan to distinguish between the calling and called legs of the call (new in v1.2). You cannot use any options that would affect the post-answer state if this option is used.
что в примерном переводе на русский язык означает следующее:
Если вызов отвечен, он переводится на специальный контекст и указанный экстеншен. Притом, звонящий канал будет переведён на приоритет x, а вызываемый канал на приоритет x+1
В итоге, был составлен "специфичный" контекст следующего содержания:
[custom-test-out-ring]
exten => s,1,Goto(chan_a)
exten => s,n,Goto(chan_b)
exten => s,n(chan_a),Ringing()
exten => s,n,Wait(20)
exten => s,n,Bridge(${DIALEDPEERNAME})
exten => s,n,Hangup()
exten => s,n(chan_b),Wait(50)
exten => s,n,Hangup()
Вызов данного контексте осуществляется следующим образом:

exten => _8XXXXXXXXXX,n,Dial(SIP/linksys/${EXTEN},3,tTG(custom-test-out-ring^s^1))

В итоге, когда осуществляется вызов на указанное направление, при ответе на вызов звонок перекидывается в контекст custom-test-out-ring, в котором звонящий канал переводится на именованный приоритет chan_a, а вызываемый канала - на chan_b. Опытным путём была измерена задержка, которую даёт модем перед тем, как начинает посылать сигналы вызова в трубку и, соответствующее значение было установлено в команду Wait для канала А. Вот таким бесхитростным, вобщем то способом, была решена довольно нетривиальная задача.

воскресенье, июля 11, 2010

Приём/отправка факсов в asetrisk с помощью Hylafax

Встала задача - настроить приём и отправку факсов через астериск. Была выбрана связка iaxmodem + hylafax. Причина такого выбора банальна: в ветке 1.4 астериска нет встроенного аппликейшена для приёма/отправка факсов. С rxfax/ReceiveFax заморачиваться не хотелось.
Итак, имеем сервер с Debian Lenny, asterisk-1.4.33.1 + freepbx-2.7. Ставим доп пакеты:
ip-pbx:~# aptitude install g++ libtiff-tools libtiff4 libtiff4-dev dpkg-dev
Заводим iax-абонентов на астериске, в моём случае это были номера 8001-8007. Не забываем также в конфиге iax_general_custom.conf указать calltokenoptional=<ваша подсеть>. Далее, качаем и ставим iaxmodem:
ip-pbx:~# cd /usr/src
ip-pbx:/usr/src# wget http://sourceforge.net/projects/iaxmodem/files/iaxmodem/iaxmodem-1.2.0/iaxmodem-1.2.0.tar.gz/download
ip-pbx:/usr/src# tar xzvf iaxmodem-1.2.0.tar.gz
ip-pbx:/usr/src# cd iaxmodem-1.2.0
ip-pbx:/usr/src/iaxmodem-1.2.0# ./configure && make
ip-pbx:/usr/src/iaxmodem-1.2.0# cp iaxmodem /usr/local/bin
ip-pbx:/usr/src/iaxmodem-1.2.0# mkdir /etc/iaxmodem

Здесь пропишу настройки для одного иаксмодема, остальные заводятся аналогично:
ip-pbx:/usr/src/iaxmodem-1.2.0# touch /etc/iaxmodem/ttyIAX0
ip-pbx:/usr/src/iaxmodem-1.2.0# echo "iax1:2345:respawn:/usr/local/bin/iaxmodem ttyIAX0" >> /etc/inittab
ip-pbx:/usr/src/iaxmodem-1.2.0# cd /etc/iaxmodem

Далее, прописываем в ttyIAX0 параметры для модема:
ip-pbx:/etc/iaxmodem# vim ttyIAX0

device /dev/ttyIAX0
owner uucp:uucp
mode 660
port 4570
refresh 50
server [ip астериска]
peername [peername]
secret [secret]
codec alaw


Здесь, [ip астериска] - айпишник, на котором стоит астериск, [peername] - имя иакспира, которого мы завели на астериске, [secret] - это, соответственно, пароль для авторизации этого пира.
Ставим и настраиваем hylafax:
ip-pbx:/etc/iaxmodem# cd /usr/src
ip-pbx:/usr/src# wget ftp://ftp.hylafax.org/source/hylafax-6.0.4.tar.gz
ip-pbx:/usr/src# tar xzvf hylafax-6.0.4.tar.gz
ip-pbx:/usr/src# cd hylafax-6.0.4
ip-pbx:/usr/src/hylafax-6.0.4# dpkg-buildpackage
ip-pbx:/usr/src/hylafax-6.0.4# dpkg -i ../hylafax*deb

ip-pbx:/usr/src/hylafax-6.0.4# faxsetup

[...]

Update /var/spool/hylafax/status/any.info.

HylaFAX configuration parameters are:

[1] Init script starts faxq: yes
[2] Init script starts hfaxd yes
[3] Start old protocol: no
[4] Start paging protocol: no

Are these ok [yes]?

[...]

Do you want to run faxaddmodem to configure a modem [yes]?
Serial port that modem is connected to [ttyS0]? ttyIAX0

[...]


Далее, вам будет предложено ввести параметры модема. Тут можете прописать свои значения. Сетап создаст в папке /var/spool/hylafax/etc файл config.ttyIAX0. У меня он выглядит следующим образом:

CountryCode: 7
AreaCode: 495
FAXNumber: +7.495.555.1234
LongDistancePrefix: 1
InternationalPrefix: 011
DialStringRules: etc/dialrules
ServerTracing: 1
SessionTracing: 11
RecvFileMode: 0600
LogFileMode: 0600
DeviceMode: 0600
RingsBeforeAnswer: 1
SpeakerVolume: off
GettyArgs: "-h %l dx_%s"
LocalIdentifier: "NothingSetup"
TagLineFont: etc/lutRS18.pcf
ModemType: Class1
ModemResetCmds: AT+VCID=1
PagerTTYParity: none
Class1Cmd: AT+FCLASS=1.0 # command to enter class 1.0
Class1PPMWaitCmd: AT+FTS=7 # command to stop and wait before PPM
Class1TCFWaitCmd: AT+FTS=7 # command to stop and wait before TCF
Class1EOPWaitCmd: AT+FTS=9 # command to stop and wait before EOP
Class1SwitchingCmd: AT+FRS=7 # command to stop and listen for silence
Class1RecvAbortOK: 200 # wait 200ms for abort response
Class1FrameOverhead: 4 # 4 byte overhead in recvd HDLC frames
Class1RecvIdentTimer: 40000 # 35+5secs waiting for ident frames
Class1TCFMaxNonZero: 10 # max 10% of data may be non-zero
Class1TCFMinRun: 1000 # min run is 2/3rds of TCF duration
CallIDPattern: "NMBR="
CallIDPattern: "NAME="
CallIDPattern: "ANID="
CallIDPattern: "NDID="


Можно подправить данный файл. Далее, прописываем
ip-pbx:/usr/src/hylafax-6.0.4# echo "t0:2345:respawn:/usr/sbin/faxgetty ttyIAX0" >> /etc/inittab
ip-pbx:/usr/src/hylafax-6.0.4# vim /etc/rc.local, сюда добавляем строчки
/usr/local/sbin/hfaxd
/usr/local/sbin/faxq
, после этого ребутаем серв:
ip-pbx:/usr/src/hylafax-6.0.4# reboot

После загрузки сервера проверьте, что у вас нормально работают hylafax и iaxmodem:
команда ps aux | grep iax должна выдать что то похожее на
uucp 5288 0.0 0.0 3084 1312 pts/3 Ss+ Jul07 1:07 /usr/local/bin/iaxmodem ttyIAX0
а команда ps aux | grep fax должна выдать
uucp 5059 0.0 0.0 4372 1108 ? Ss Jul07 0:00 /usr/sbin/faxq
uucp 5063 0.0 0.0 4704 1536 ? S Jul07 0:00 /usr/sbin/hfaxd -d -i 4559
uucp 5295 0.0 0.0 4944 2196 ? Ss Jul07 0:21 /usr/sbin/faxgetty ttyIAX0

Если этого нет, то что то вы сделали не так. Если у вас показывает так же как и у меня, то всё нормально и можно двигаться дальше.

Отправка факсов

По умолчанию, во freepbx пиры сидят в контексте from-internal, меня это вполне устраивает, я ничего не меняю. Если вы хотите выпускать факсы через какой то отдельный транк, то прописшите это в отдельный контекст, который потом поставьте для iax-пира в настройках.
Отправка факсов совершается командой sendfax. У меня она имеет следующий вид:
sendfax -n -T 2 -N -d [номер назначения] [файл факса]
-n - не генерить титульную страницу
-T 2 - совершить 2 попытки отправить факс, прежде чем посчитать отправку неуспешной(по умолчанию будет совершено 12 попыток)
-N - не посылать на электронную почту уведомление об отправке факса
-d [номер назначения] - номер, на который посылать факс
Если у вас всё настроено правильно, то в консоли астериска вы увидите исходящий звонок от iax-пира на номер назначения. Проверить статус отправки можно командами faxstat -s и faxstat -d

Приём факсов
Для того, чтобы принять факс, необходимо направить вызов на iax-пира, под которым в астериске зареган iaxmodem:
exten => _XXXX.,1,Dial(IAX2/iaxmodem/${EXTEN},30,)
После чего факс будет принят hylafax'ом и будет складирован в папку /var/spool/hylafax/recvq. В данном методе существует одна маленькая неприятность - имя файла в /var/spool/hylafax/recvq никак не будет отражать информацию о том, кто послал данный факс. Но, к счастью, эту проблему довольно просто решить. В hylafax есть встроенный инструмент, под названием FaxDispatch. Данный инструмент позволяет получить информацию о вызове. В частности, получить CLID и DID, которые использваолись в вызове. CALLERID звонящего сохраняется в переменной $CIDNUMBER, номер, на который пришёл вызов - в переменной $CALLID4. Необходимо отредактировать файл /var/spool/hylafax/etc/FaxDispatch (если его не существует, то создайте). У меня он выглядит следующим образом:
FILETYPE=tif
FOLDER="/var/spool/hylafax/recvq/"
FULLPATH="${FOLDER}${FILENAME}.tif"
cp $FULLPATH /tmp/$CIDNUMBER\_$CALLID4.tif
chmod 777 /tmp/$CIDNUMBER\_$CALLID4.tif

Думаю, объяснять особо не нужно. Все тифы копируются в папку tmp, им присваивается имя $CIDNUMBER_$CALLID4. В конфиге астериска у меня прописано:

exten => _[124]XX,1,Set(FAXFROM=${CALLERID(num)})
exten => _[124]XX,n,Set(FAXEXTEN=${EXTEN})
exten => _[124]XX,n,Set(FAXMAIL=${FAX_GET(${EXTEN})})
exten => _[124]XX,n,Goto(fax)
exten => _XX.,n(fax),Dial(IAX2/8001/${EXTEN})
exten => h,1,DumpChan()
exten => h,n,System(/usr/bin/perl /space/scripts/fax_handler.pl ${FAXFROM} ${FAXEXTEN} ${FAXMAIL})

т.е. после того, как был принят факс, запускается перловый скрипт следующего содержания:
#!/usr/bin/perl -w

use strict;
use POSIX;

my $tiff2pdf = '/usr/bin/tiff2pdf';
my $rm = '/bin/rm';
my $mv = '/bin/mv';
my $peer = $ARGV[0];
my $user = $ARGV[1];
my $mail = $ARGV[2];
my ($sec,$min,$hour,$mday,$mon,$year,$wday)=localtime(time());
$mon += 1;
$year += 1900;
$mday = '0' . "$mday" if (length($mday) == 1);
$mon = '0' . "$mon" if (length($mon) == 1);
$sec = '0' . "$sec" if (length($sec) == 1);
$min = '0' . "$min" if (length($min) == 1);
$hour = '0' . "$hour" if (length($hour) == 1);
my $_file = "$peer" . '_' . "$user";
my $file = "$peer" . '_' . "$user" . '_' . "$year" . "$mon" . "$mday" . '_' . "$hour:$min:$sec";
my $date = "$year" . "$mon" . "$mday" . '_' . "$hour" . ':' . "$min" . ':' . "$sec";
chdir '/tmp';
exit unless (-e "$_file.tif");
system("$tiff2pdf -o $file.pdf $_file.tif");
system("$rm -f $_file.tif");
system("/usr/local/bin/sendEmail -f [from_mail] -t $mail -u FAX from $peer to $user -m fax received $date -a /tmp/$file.pdf -s [smtp-сервер] -xu [from_mail] -xp [mail_passwd]");
system("$mv $file.pdf /var/spool/asterisk/fax");

Данному скрипту передаются CLID, DID и мейл, на который нужно отсылать письмо с факсом. Скрипт по CLID и DID ищет в папке /tmp нужный файл, конвертит его в pdf пуляет на почту.

Вот и всё, надеюсь, кому то данная статья пригодится.
В статье используются материалы из следующих статей:

http://www.the-asterisk-book.com/unstable/faxserver-mit-iaxmodem-und-hylafax.html
http://lynks.ru/blog/articles/iaxmodem-hylafax-i-ldap

вторник, января 19, 2010

Новый день

К сожалению, из-за смены работы с zabbix'ом пришлось повременить. Сейчас другие заботы, не менее интересные. Всё так же продолжаю заниматься астериском, но уже с сильным уклоном в базы данных, конкретно в postgres.
Руководство на новой работе поставило задачу освоить freebsd, т.к. почти все сервера работают под управлением этой OS, да и сам технический директор с замом работают из под этой оси.
Занялся изучением, поставил себе qemu, хочу туда замостырить фряху. С ужасом и трепетом ожидаю встречи с нею))

среда, августа 19, 2009

Занялся изучением системы zabbix

По работе понадобилась мне данная приблуда для мониторинга серверов, качнул, скомпилил, запустил. Веб-морда красивая, но полезной информации не отображает. Вообщем, с наскоку не получилось, сижу курю мануалы. Жаль, что в универе плохо изучал англицкий язык, знание последнего мне бы сейчас очень пригодилось, т.к. почти вся полезная информация на буржуйском языке. Ну что ж, значит всё как всегда - зарываемся с головой в документацию и пытаемся разобраться что к чему. Как разберусь, накатаю мануал для таких же как я, новичков)