Архив за день: 2 января 2011

Syslog-сервер с ICQ-клиентом в одном флаконе

Как известно, множество сетевых устройств могут использовать протокол syslog для оповещения о происходящих в них процессах. Соответственно, существует некоторое количество серверных программ, которые позволяют систематизировать информационные сообщения, передать их каким-либо способом для обработки обслуживающему персоналу, переслать дальше по цепочке серверов. Но зачастую такие системы представляют из себя комбайны по мониторингу, что может не подойти в какой-то конкретной ситуации. Таким образом, мне понадобился сервис, банально рассылающий сообщения syslog по группе пользователей icq. Ниже я предлагаю решение такой задачи.

Используемые инструменты:
1. Сервер на базе Debian Linux.
2. Интерпретатор языка Perl.

Первым делом нужно установить модуль Net::OSCAR, который обеспечит нам функциональность асечного клиента. Вариантов два, использовать CPAN, либо установить в ручном режиме.
В случае с CPAN выполняем:

cpan
install Net::OSCAR
exit

В случае ручного режима (после скачки и распаковки):

apt-get install libdigest-md5-file-perl
apt-get install libscalar-util-numeric-perl
apt-get install libxml-parser-perl
perl Makefile.PL
make test
make install

Будем писать perl-daemon, вечно висящий в памяти. Стоит обратить внимание, что сервис syslog слушает 514 порт, это значит, что он должен запускаться от root-а. Открываем любимый текстовый редактор и набираем следующий код.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/usr/bin/perl -w
 
#Чтобы не ошибаться в переменных
use strict;
#Используемые модули
use Net::OSCAR qw(:standard);
use POSIX qw(setsid);
use IO::Socket;
 
#Отсоединяемся от рабочей консоли
Daemonize();
 
#Настройки для соединения с сервером ICQ
my $icq_uin  = _ICQ_UIN_HERE_;
my $icq_pass = "_ICQ_PASSWORD_HERE_";
#Здесь перечисляются получатели сообщения syslog
my @icq_users = (_ICQ_REC_UIN_HERE_,_ICQ_REC_UIN_HERE_);
 
#Готовимся к приему сообщений syslog на 514 порту
my $sock = IO::Socket::INET->new(LocalPort => '514', Proto => 'udp');
#Таймаут выполнения системной функции recv устанавливаем в 10 секунд
setsockopt($sock,SOL_SOCKET,SO_RCVTIMEO,pack('L!L!',+10,0));
#Сюда будем ловить сообщения от syslog-клиентов
my $buf;
 
#Инициализурем объект и соединяемся с ICQ
my $bot=Net::OSCAR->new();
$bot->signon($icq_uin,$icq_pass);
#Сделаемся видимыми
$bot->set_visibility(VISMODE_PERMITALL);
 
#Пока бот не online, топчемся здесь
while (!$bot->is_on) {
	$bot->do_one_loop();
}
 
#Основной цикл
while (1) {
	#Получаем сообщения от syslog-клиентов, с ожиданием в 10 секунд, раскладываем по переменным результат
	$sock->recv($buf, 1524);
	my ($port, $ipaddr) = sockaddr_in($sock->peername);
	my $hn = gethostbyaddr($ipaddr, AF_INET);
	$buf=~/< (\d+)>(.*?):(.*)/;
	my $head=$2;
	my $msg=$3;
 
	#Если связь с ICQ прервалась, соединимся заново
	if (!$bot->is_on) {
		$bot->signon($icq_uin,$icq_pass);
		$bot->set_visibility(VISMODE_PERMITALL);
		sleep(5);
	}
 
	#Нужно, чтобы сервер нас не потерял
	$bot->do_one_loop();
 
	if ($buf) {
		#Если к нам что-то пришло, отправим всей группе получателей
		foreach my $i (@icq_users) {
			$bot->send_im($i,"$head $msg (from $hn)");
		}
	}
}
 
#До этого места никогда не дойдет :-) В идеале нужен код обработки сигналов системы
$bot->signoff();
 
#Функция отсоединения, полностью copy&paste
sub Daemonize {
	return if ($^O eq 'MSWin32');
	chdir '/' or die "Can't chdir to /: $!";
	umask 0;
	open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
	open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
	open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
	defined(my $pid = fork)   or die "Can't fork: $!";
	exit if $pid;
	setsid or die "Can't start a new session: $!\n";
}

Устанавливаем на файл 700 и запускаем:

chmod 700 ./icqBot.pl
./icqBot.pl

По мотивам:
1. Пишем icq-бот на perl
2. A simple Syslog daemon script written in Perl.
3. Архитектурно-независимый код для recv timeout

p.s. Всё, что нам легко досталось, так же легко и теряется. (c) (В данном случае распространяется обратно в интернет).