Перенес этот блог в общий свой блог с тэгом gentoo.
Перешел на свой домен.
http://blog.f-y.name
Опыт установки и настройки Gentoo Linux
четверг, 2 сентября 2010 г.
понедельник, 22 октября 2007 г.
Пинг по расписанию для мониторинга состояния сети
В нашей локалке в последнее время появились потери до шлюза, когда в сети много людей. Для мониторинга написал скрипт, который пингует шлюз по расписанию, и выводит на сайт результаты.
Сам скрипт пинга:
#!/bin/bash
fn=`date +%F.%H:%M:%S`
dir=/var/www/localhost/htdocs/ping-stat
ping -i 0.3 -c 500 10.13.0.17 > $dir/$fn.txt
tail -n 3 < $dir/$fn.txt > $dir/$fn.stat.txt
Запись в crontab:
0,30 * * * * root /path/to/script
Скрипт для показа результатов на сайте: /var/www/localhost/cgi-bin:
#!/bin/sh
echo "Content-type: text/html; charset=cp1251"
echo
echo "<html><body>"
echo "<a href='http://$HTTP_HOST/ping-stat-graph/packetlost.php'>График</a><br>"
echo "<a href='http://$HTTP_HOST/ping-stat-graph/packetlost.php?t'>График таймингов</a><br>"
echo "<form action='http://$HTTP_HOST/ping-stat-graph/packetlost.php'>"
echo "Показать последние записи, шт. <input name='s'><input type='submit' value='Показать'><br>"
echo "Показать последние записи, тайминги, шт. <input name='s'><input type='submit' value='Показать'><input type='hidden' name='t'></form>"
echo "<table>"
for i in $( find /var/www/localhost/htdocs/ping-stat/ -type f -not -name "*.stat.txt" -printf "%f\n" | sort )
do
base=`expr match "$i" '\([^.]*\.[^.]*\)'`
echo "<tr><td><a href='/ping-stat/$i'>$base</a><br></td>"
if [ -e "/var/www/localhost/htdocs/ping-stat/$base.stat.txt" ]
then
echo "<td><a href='/ping-stat/$base.stat.txt'>Кратко</a></td><tr>"
else
echo "<td>Кратко</td><tr>"
fi
done
echo "</table>"
echo "</body></html>"
Скрипт для графика /var/www/localhost/htdocs/ping-stat-graph/packetlost.php (автор скрипта мой друг mvf):
<?
$dir = "/var/www/localhost/htdocs/ping-stat/";
$filename_date_format = "%Y-%m-%d.%H:%M:%s.";
$filename_format = "^[[:punct:][:digit:]]{14,30}\.stat\.txt$"; // формат имени файла - для ерега
$file_content_format ="([[:digit:]]+) packets transmitted, ([[:digit:]]+) received, [[:digit:]]+% packet loss, time [[:digit:]]+ms[[:space:]]*rtt min/avg/max/mdev = ([[:digit:]\.]+)/([[:digit:]\.]+)/([[:digit:]\.]+)/([[:digit:]\.]+) ms";
/////////////////////////////////////////////////////////////////
// Returns a date from a string based on a given format
// кривая штуковина скопированная их хелпов и немного переделанная,
// но в принципе худо-бедно может прочитать дату из заданного формата
function parseDate( $date, $format )
{
// Builds up date pattern from the given $format, keeping delimiters in place.
if( !preg_match_all( "/%([YmdHMs])([^%])*/", $format, $formatTokens, PREG_SET_ORDER ) ) {
return false;
}
$datePattern = "";
foreach( $formatTokens as $formatToken )
{
$delimiter = preg_quote( $formatToken[2], "/" );
if($formatToken[1] == 'Y') {
$datePattern .= '(.{1,4})'.$delimiter;
} else {
$datePattern .= '(.{1,2})'.$delimiter;
}
}
if( !preg_match( "/".$datePattern."/", $date, $dateTokens) ) return false;
$dateSegments = array();
for($i = 0; $i < count($formatTokens); $i++) {
$dateSegments[$formatTokens[$i][1]] = $dateTokens[$i+1];
}
return mktime ( $dateSegments["H"], $dateSegments["M"], $dateSegments["s"], $dateSegments["m"], $dateSegments["d"], $dateSegments["Y"]);
}
/////////////////////////////////////////////////////////////////
// для usort
function cmp_by_date($a, $b)
{
if ($a['time'] == $b['time']) { return 0; }
return ($a['time'] < $b['time']) ? -1 : 1;
}
////////////////////////////////////////////////////////////////
//// начало
////////////////////////////////////////////////////////////////
$filenames = scandir($dir);
$data = array();
foreach ($filenames as $filename)
{
$full_name = $dir.$filename;
if (!is_dir($full_name) && $filename != "." && $filename != ".." && ereg($filename_format, $filename))
{
$ftime = parseDate($filename, $filename_date_format);
ereg($file_content_format,file_get_contents($full_name),$res);
$data_piece['time'] = $ftime;
$data_piece['time_text'] = date("H:i",$ftime);
if ($res[1]!=0) $data_piece['lost'] = (1 - $res[2]/$res[1])*100; else $data_piece['lost'] = 100;
$data_piece['transmitted'] = $res[1]+0;
$data_piece['received'] = $res[2]+0;
$data_piece['min'] = $res[3]+0.0;
$data_piece['avg'] = $res[4]+0.0;
$data_piece['max'] = $res[5]+0.0;
$data_piece['mdev'] = $res[6]+0.0;
$data[] = $data_piece;
} // if (!is_dir ...
} // foreach ($filenames
usort($data, "cmp_by_date");
// по идее тут бы лучше разбить скрипт на два...
// один которые собирает и сохраняет данные, второй - показывает сохраенные данные.
if (isset($_GET['s'])) // если указан размер
{
$size = count($data);
$disired_size = 0 + $_GET['s'];
$croppoint = $size - $disired_size;
if ($croppoint < 0) $croppoint = 0;
$data = array_slice($data, $croppoint);
}
require('chart.php');
$time_min = array();
$time_max = array();
$time_avg = array();
$x_axis = array();
$lost = array();
foreach ($data as $data_piece)
{
$time_min[] = $data_piece['min'];
$time_max[] = $data_piece['max'];
$time_avg[] = $data_piece['avg'];
$x_axis[] = $data_piece['time_text'];
$lost[] = $data_piece['lost'];
}
//plot (array $c1, array $c2 = false, color $color = "black",
// string $style = "lines", color $gradient_color = "black",
// int $parameter = 0)
if (isset($_GET['t']))
{
$chart = new chart(2000, 500);
$chart->set_title("Ping timing (10.13.0.17 from 10.13.200.111)");
$chart->set_x_ticks ($x_axis,"text");
$chart->set_labels ("time", "ping, ms");
$chart->set_margins ($left = 50, $right = 10, $top = 20, $bottom = 43);
$chart->plot($time_max, false, "black");
$chart->add_legend("Max");
$chart->plot($time_avg, false, "red", "gradient");
$chart->add_legend("Avg", "red");
$chart->plot($time_min, false, "blue", "gradient");
$chart->add_legend("Min", "blue");
$chart->stroke();
} else
{
$chart = new chart(2000, 500);
$chart->set_title("Packet lost (10.13.0.17 from 10.13.200.111)");
$chart->set_x_ticks ($x_axis,"text");
$chart->set_labels ("time", "lost, %");
$chart->set_margins ($left = 50, $right = 10, $top = 20, $bottom = 43);
$chart->plot($lost, false, "black");
$chart->stroke();
}
?>
Для отрисовки используется библиотека chart
Результат можно посмотреть здесь
Сам скрипт пинга:
#!/bin/bash
fn=`date +%F.%H:%M:%S`
dir=/var/www/localhost/htdocs/ping-stat
ping -i 0.3 -c 500 10.13.0.17 > $dir/$fn.txt
tail -n 3 < $dir/$fn.txt > $dir/$fn.stat.txt
Запись в crontab:
0,30 * * * * root /path/to/script
Скрипт для показа результатов на сайте: /var/www/localhost/cgi-bin:
#!/bin/sh
echo "Content-type: text/html; charset=cp1251"
echo
echo "<html><body>"
echo "<a href='http://$HTTP_HOST/ping-stat-graph/packetlost.php'>График</a><br>"
echo "<a href='http://$HTTP_HOST/ping-stat-graph/packetlost.php?t'>График таймингов</a><br>"
echo "<form action='http://$HTTP_HOST/ping-stat-graph/packetlost.php'>"
echo "Показать последние записи, шт. <input name='s'><input type='submit' value='Показать'><br>"
echo "Показать последние записи, тайминги, шт. <input name='s'><input type='submit' value='Показать'><input type='hidden' name='t'></form>"
echo "<table>"
for i in $( find /var/www/localhost/htdocs/ping-stat/ -type f -not -name "*.stat.txt" -printf "%f\n" | sort )
do
base=`expr match "$i" '\([^.]*\.[^.]*\)'`
echo "<tr><td><a href='/ping-stat/$i'>$base</a><br></td>"
if [ -e "/var/www/localhost/htdocs/ping-stat/$base.stat.txt" ]
then
echo "<td><a href='/ping-stat/$base.stat.txt'>Кратко</a></td><tr>"
else
echo "<td>Кратко</td><tr>"
fi
done
echo "</table>"
echo "</body></html>"
Скрипт для графика /var/www/localhost/htdocs/ping-stat-graph/packetlost.php (автор скрипта мой друг mvf):
<?
$dir = "/var/www/localhost/htdocs/ping-stat/";
$filename_date_format = "%Y-%m-%d.%H:%M:%s.";
$filename_format = "^[[:punct:][:digit:]]{14,30}\.stat\.txt$"; // формат имени файла - для ерега
$file_content_format ="([[:digit:]]+) packets transmitted, ([[:digit:]]+) received, [[:digit:]]+% packet loss, time [[:digit:]]+ms[[:space:]]*rtt min/avg/max/mdev = ([[:digit:]\.]+)/([[:digit:]\.]+)/([[:digit:]\.]+)/([[:digit:]\.]+) ms";
/////////////////////////////////////////////////////////////////
// Returns a date from a string based on a given format
// кривая штуковина скопированная их хелпов и немного переделанная,
// но в принципе худо-бедно может прочитать дату из заданного формата
function parseDate( $date, $format )
{
// Builds up date pattern from the given $format, keeping delimiters in place.
if( !preg_match_all( "/%([YmdHMs])([^%])*/", $format, $formatTokens, PREG_SET_ORDER ) ) {
return false;
}
$datePattern = "";
foreach( $formatTokens as $formatToken )
{
$delimiter = preg_quote( $formatToken[2], "/" );
if($formatToken[1] == 'Y') {
$datePattern .= '(.{1,4})'.$delimiter;
} else {
$datePattern .= '(.{1,2})'.$delimiter;
}
}
if( !preg_match( "/".$datePattern."/", $date, $dateTokens) ) return false;
$dateSegments = array();
for($i = 0; $i < count($formatTokens); $i++) {
$dateSegments[$formatTokens[$i][1]] = $dateTokens[$i+1];
}
return mktime ( $dateSegments["H"], $dateSegments["M"], $dateSegments["s"], $dateSegments["m"], $dateSegments["d"], $dateSegments["Y"]);
}
/////////////////////////////////////////////////////////////////
// для usort
function cmp_by_date($a, $b)
{
if ($a['time'] == $b['time']) { return 0; }
return ($a['time'] < $b['time']) ? -1 : 1;
}
////////////////////////////////////////////////////////////////
//// начало
////////////////////////////////////////////////////////////////
$filenames = scandir($dir);
$data = array();
foreach ($filenames as $filename)
{
$full_name = $dir.$filename;
if (!is_dir($full_name) && $filename != "." && $filename != ".." && ereg($filename_format, $filename))
{
$ftime = parseDate($filename, $filename_date_format);
ereg($file_content_format,file_get_contents($full_name),$res);
$data_piece['time'] = $ftime;
$data_piece['time_text'] = date("H:i",$ftime);
if ($res[1]!=0) $data_piece['lost'] = (1 - $res[2]/$res[1])*100; else $data_piece['lost'] = 100;
$data_piece['transmitted'] = $res[1]+0;
$data_piece['received'] = $res[2]+0;
$data_piece['min'] = $res[3]+0.0;
$data_piece['avg'] = $res[4]+0.0;
$data_piece['max'] = $res[5]+0.0;
$data_piece['mdev'] = $res[6]+0.0;
$data[] = $data_piece;
} // if (!is_dir ...
} // foreach ($filenames
usort($data, "cmp_by_date");
// по идее тут бы лучше разбить скрипт на два...
// один которые собирает и сохраняет данные, второй - показывает сохраенные данные.
if (isset($_GET['s'])) // если указан размер
{
$size = count($data);
$disired_size = 0 + $_GET['s'];
$croppoint = $size - $disired_size;
if ($croppoint < 0) $croppoint = 0;
$data = array_slice($data, $croppoint);
}
require('chart.php');
$time_min = array();
$time_max = array();
$time_avg = array();
$x_axis = array();
$lost = array();
foreach ($data as $data_piece)
{
$time_min[] = $data_piece['min'];
$time_max[] = $data_piece['max'];
$time_avg[] = $data_piece['avg'];
$x_axis[] = $data_piece['time_text'];
$lost[] = $data_piece['lost'];
}
//plot (array $c1, array $c2 = false, color $color = "black",
// string $style = "lines", color $gradient_color = "black",
// int $parameter = 0)
if (isset($_GET['t']))
{
$chart = new chart(2000, 500);
$chart->set_title("Ping timing (10.13.0.17 from 10.13.200.111)");
$chart->set_x_ticks ($x_axis,"text");
$chart->set_labels ("time", "ping, ms");
$chart->set_margins ($left = 50, $right = 10, $top = 20, $bottom = 43);
$chart->plot($time_max, false, "black");
$chart->add_legend("Max");
$chart->plot($time_avg, false, "red", "gradient");
$chart->add_legend("Avg", "red");
$chart->plot($time_min, false, "blue", "gradient");
$chart->add_legend("Min", "blue");
$chart->stroke();
} else
{
$chart = new chart(2000, 500);
$chart->set_title("Packet lost (10.13.0.17 from 10.13.200.111)");
$chart->set_x_ticks ($x_axis,"text");
$chart->set_labels ("time", "lost, %");
$chart->set_margins ($left = 50, $right = 10, $top = 20, $bottom = 43);
$chart->plot($lost, false, "black");
$chart->stroke();
}
?>
Для отрисовки используется библиотека chart
Результат можно посмотреть здесь
пятница, 19 октября 2007 г.
Скрипт для скачки структуры ftp-сервера
Решил написать такой, чтобы рассмотреть, что есть на некоторых наших богатых ftp-серверах, без подключения..
Долго извращался с bash'ем, пока не дошел до переменной IFS :)
В общем вот скрипт:
get_listing:
#!/bin/bash
if [ "$1" == "" ]
then
echo "Usage: `basename $0` server"
exit
fi
server=$1
base=$PWD
IFS=$'\n'
function get_listing()
{
local prev_dir=$PWD
local url=
if [ "$1" != "" ]
then
url=$1
echo "Creating dir: $2"
mkdir "$2"
cd "$2"
fi
echo "Retreiving file list $url"
curl ftp://$server$url/ 2> /dev/null > listing
lst=`cat listing | grep "^d" | grep -v "\.$" | xargs -I'{}' $base/get_fname "{}"`
for f in $lst
do
get_listing "${url}/$f" "$f"
done
cd $prev_dir
}
get_listing
get_fname:
#!/bin/bash
echo `expr match "$1" '\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\(.*\)'`
Как засунуть get_fname в сам скрипт, так и не понял...
Пробовал $() внутри ``, и наоборот, и по всякому..
Долго извращался с bash'ем, пока не дошел до переменной IFS :)
В общем вот скрипт:
get_listing:
#!/bin/bash
if [ "$1" == "" ]
then
echo "Usage: `basename $0` server"
exit
fi
server=$1
base=$PWD
IFS=$'\n'
function get_listing()
{
local prev_dir=$PWD
local url=
if [ "$1" != "" ]
then
url=$1
echo "Creating dir: $2"
mkdir "$2"
cd "$2"
fi
echo "Retreiving file list $url"
curl ftp://$server$url/ 2> /dev/null > listing
lst=`cat listing | grep "^d" | grep -v "\.$" | xargs -I'{}' $base/get_fname "{}"`
for f in $lst
do
get_listing "${url}/$f" "$f"
done
cd $prev_dir
}
get_listing
get_fname:
#!/bin/bash
echo `expr match "$1" '\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\\S*\\s*\(.*\)'`
Как засунуть get_fname в сам скрипт, так и не понял...
Пробовал $() внутри ``, и наоборот, и по всякому..
четверг, 11 октября 2007 г.
Мой сервер
Этот бот и всякие другие фигнюшки у меня крутятся на домашнем сервере.
Необходимость в нем возникла, когда дома появилось больше одного компьютера.
Сначала купил беспроводный маршрутизатор (D-Link DI-624). Но, к сожалению, потом оказалось, что пользоваться им в нашей сети (Корбина-Ярославль) не слишком удобно: невозможно сидеть одновременно и в локальной сети и в Интернете (выход в него происходит через VPN, если выходить через маршрутизатор - невозможно быть в обоих сетях одновременно, так как он не поддерживает статическую маршрутизацию :( ; если подключаться к VPN с компов за ним - провайдер не дает).
Поэтому решил купить старенький комп, установить на него Linux, поставить 3 сетевухи, и сделать из него роутер.
Изначально сервером стал Pentium II, 400 MHz, 80 Gb, 128 Mb. Потом появилась возможность сменить его на P4-2000, 512 Mb (что самое хорошее - линукс перенесся вместе в винчестером без каких либо проблем :) ).
Дак вот - хорошо, что маршрутизатор оказался таким слабым - появился сервер, можно теперь над ним извращаться/издеваться :)
В итоге на нем стоит:
Адрес сервера кстати http://fyfd.homeip.net
Необходимость в нем возникла, когда дома появилось больше одного компьютера.
Сначала купил беспроводный маршрутизатор (D-Link DI-624). Но, к сожалению, потом оказалось, что пользоваться им в нашей сети (Корбина-Ярославль) не слишком удобно: невозможно сидеть одновременно и в локальной сети и в Интернете (выход в него происходит через VPN, если выходить через маршрутизатор - невозможно быть в обоих сетях одновременно, так как он не поддерживает статическую маршрутизацию :( ; если подключаться к VPN с компов за ним - провайдер не дает).
Поэтому решил купить старенький комп, установить на него Linux, поставить 3 сетевухи, и сделать из него роутер.
Изначально сервером стал Pentium II, 400 MHz, 80 Gb, 128 Mb. Потом появилась возможность сменить его на P4-2000, 512 Mb (что самое хорошее - линукс перенесся вместе в винчестером без каких либо проблем :) ).
Дак вот - хорошо, что маршрутизатор оказался таким слабым - появился сервер, можно теперь над ним извращаться/издеваться :)
В итоге на нем стоит:
- Роутер - iptables, NAT
- Web-сервер
- ftp-сервер
- jabber-сервер
- mail-сервер (пока что не работает, так как и не нужен то особо, но хочу всё таки довести до ума)
- IRC-бот (пришлось отключить, так как теперь внутрисетевой сервер не пускает больше 2 (или 1) с IP.. хотя сейчас что то сменилось, надо опять попробовать)
- ммм... чего ж там еще крутится :) .... icq-bot
- World of Warcraft-сервер (стоял еще на P2, жрет сволочь мегов 400 памяти тока в путь :) )
- Ну, MySQL естественно
- ... вспомню, добавлю :)
- Видео-вещание по сети (на сервере стоит TV-тюнер), с помощью VLC, или чего то аналогичного
- Типа адаптера IP-телефонии (не знаю пока как это сделать вообще)
- Управление "умным" домом.. (протокол X10, но это еще надо оборудование купить/спаять..., в общем это еще в будущем.. :) )
- Подключу скоро анлим, настрою web-интерфейс для качалки (mldonkey)
Адрес сервера кстати http://fyfd.homeip.net
icq-бот на основе micq
Решил вот сделать icq бота, в основном для проверки по асе пингов до серверов/коммутаторов в домашней сети.. Нуу, точнее это давно уже было, где то в середине июля :) только вот дошли руки написать что нибудь в дневник..
У micq есть такая мощная штука, как "scripting". В настройке по-умолчанию после запуска создается именованный пайп ~/.micq/scripting. Если в него писать, это расценивается как ввод в консоли micq. Таким образом, одна сторона взаимодействия у нас есть. Для получения команд, будем читать логи пользователей из каталога history.
Скрипт я написал на perl, использовал модуль File::Tail
Формат файла логов примерно такой:
# 20070629123241/ [icq8:xxxxxxxx]!icqbot@foreveryoung.yar <- xxxxxxxxx[icq8:xxxxxxxxx+10000000 online] +1
+1 - означает сколько далее строчек самого сообщения
В общем, не знаю, что еще расписать :), просто приведу пример моего скрипта-пинговалки:
#!/usr/bin/perl
$_ = 1;
exit if( $#ARGV < name = "/home/icqbot/.micq/history/${ARGV[0]}.log" file =" File::Tail-">new( name=>$name, maxinterval=>1, interval=>1, reset_tail=>0 );
open LOG, ">>${ARGV[0]}_bot.log";
$skip = 0;
$cmd = 0;
while( defined( $_ = $file->read ) )
{
if( $skip > 0 )
{
$skip--;
next;
}
# далее отправленное ботом, его просто пропускаем
if( $_ =~ /# \d+\/ \[[^]]+\]\S+ -> [^[]*\[[^]]+\] [+](\d+)/ )
{
$skip = $1;
$cmd = 0;
next;
}
# дальше возможно команда
if( $_ =~ /# \d+\/ \[[^]]+\]\S+ <- [^[]*\[[^]]+\] [+](\d+)/ ) { $cmd = 1; next; } if( !$cmd ) { next; } if( $_ =~ /^ping (.+)$/ ) { print LOG "Pinging $1\n"; $ping = `ping -c 10 $1 2>&1`;
open OUT, ">.micq/scripting";
print OUT "/msg ${ARGV[0]}\n";
print OUT $ping;
print OUT ".\n";
close OUT;
next;
}
}
Скрипт я запускаю так: script &
micq запущен под screen
У micq есть такая мощная штука, как "scripting". В настройке по-умолчанию после запуска создается именованный пайп ~/.micq/scripting. Если в него писать, это расценивается как ввод в консоли micq. Таким образом, одна сторона взаимодействия у нас есть. Для получения команд, будем читать логи пользователей из каталога history.
Скрипт я написал на perl, использовал модуль File::Tail
Формат файла логов примерно такой:
# 20070629123241/ [icq8:xxxxxxxx]!icqbot@foreveryoung.yar <- xxxxxxxxx[icq8:xxxxxxxxx+10000000 online] +1
+1 - означает сколько далее строчек самого сообщения
В общем, не знаю, что еще расписать :), просто приведу пример моего скрипта-пинговалки:
#!/usr/bin/perl
$_ = 1;
exit if( $#ARGV < name = "/home/icqbot/.micq/history/${ARGV[0]}.log" file =" File::Tail-">new( name=>$name, maxinterval=>1, interval=>1, reset_tail=>0 );
open LOG, ">>${ARGV[0]}_bot.log";
$skip = 0;
$cmd = 0;
while( defined( $_ = $file->read ) )
{
if( $skip > 0 )
{
$skip--;
next;
}
# далее отправленное ботом, его просто пропускаем
if( $_ =~ /# \d+\/ \[[^]]+\]\S+ -> [^[]*\[[^]]+\] [+](\d+)/ )
{
$skip = $1;
$cmd = 0;
next;
}
# дальше возможно команда
if( $_ =~ /# \d+\/ \[[^]]+\]\S+ <- [^[]*\[[^]]+\] [+](\d+)/ ) { $cmd = 1; next; } if( !$cmd ) { next; } if( $_ =~ /^ping (.+)$/ ) { print LOG "Pinging $1\n"; $ping = `ping -c 10 $1 2>&1`;
open OUT, ">.micq/scripting";
print OUT "/msg ${ARGV[0]}\n";
print OUT $ping;
print OUT ".\n";
close OUT;
next;
}
}
Скрипт я запускаю так: script
micq запущен под screen
четверг, 26 июля 2007 г.
Подписаться на:
Сообщения (Atom)