lilalo
changeset 25:ba4d6515b8fd v_0_2_3
Выполнен шаг (3) в плане (N05) по построению распределённой системы lilalo.
Агент l3-агент в реальном времени анализирует скрипты в указанном ему
каталоге и по мере обнаружения новых завершённых команд записывает их
в кэш-файл.
Данные о том, докуда разобран каждый скрипт-файл сохраняются во временном
файле, для того чтобы при перезапуске агента он мог продолжить разбор
с того места, где он был остановлен в прошлый раз, а не копировал
данные в кэш-файл повторно.
Агент запускается для каждого пользователя системы.
Если агент обнаружил свою копию работающую от имени того же пользователя,
он автоматически завершается.
Поиск копии агента выполняется так:
просматривается pid-файл агента - если его нет, считается, что и агент не запущен
(Внимание! Не удаляйте pid-файл!! Работа нескольких агентов от имени одного
пользователя может быть некорректной!)
Если он есть, выполняется проверка, действительно ли процесс с таким идентификатором
это l3-агент текущего пользователя. Если нет, pid-файл удаляется, и агент запускается.
Нормальное завершение агента, работающего в режиме демона, выполняется
с помощью сигнала TERM. При завершении агент автоматически стирает свой pid-файл.
Добавлены атрибуты команды, хранящие информацию о участке бинарного файла скрипта,
соответствующей команды:
raw_start - начало блока команды
raw_output_start - начало вывода команды
raw_end - окончание вывода
raw_file - имя бинарного файла
Файлы:
(могут меняться с помощью конфигурационных параметров)
~/.labmaker/.cache.dat
~/.labmaker/cache.xml
~/.labmaker/l3-agent.pid
Конфигурационные параметры:
cache_stat Имя файла с информацией о текущей позиции разбора
в каждом файле
mode Режим, в котором работает агент.
Допустимые значения:
daemon - в режиме непрерывного опроса каталога
Программа не завершается после окончания анализа,
а ждёт появления новых данных
normal - однократный анализ каталога.
Программа завершается после окончания анализа данных
daemon_sleep_interval Интервал через который агент просматривает каталог скриптов
в поисках новых данных
detach Нужно ли выполнять отключение от терминала при работе в режиме демона?
(строго говоря, если процесс не отключился от терминала,
то и в режиме демона он работать не может. Здесь имеется в виду
режим непрерывного опроса каталога)
agent_pidfile Путь к файлу, который будет хранить идентификатор процесса агента.
l3-agent Имя, под которым будет известен процесс l3-agent
Незначительные исправления:
* убрана отладочная информация о new_commands и new_files из frontend'а
Агент l3-агент в реальном времени анализирует скрипты в указанном ему
каталоге и по мере обнаружения новых завершённых команд записывает их
в кэш-файл.
Данные о том, докуда разобран каждый скрипт-файл сохраняются во временном
файле, для того чтобы при перезапуске агента он мог продолжить разбор
с того места, где он был остановлен в прошлый раз, а не копировал
данные в кэш-файл повторно.
Агент запускается для каждого пользователя системы.
Если агент обнаружил свою копию работающую от имени того же пользователя,
он автоматически завершается.
Поиск копии агента выполняется так:
просматривается pid-файл агента - если его нет, считается, что и агент не запущен
(Внимание! Не удаляйте pid-файл!! Работа нескольких агентов от имени одного
пользователя может быть некорректной!)
Если он есть, выполняется проверка, действительно ли процесс с таким идентификатором
это l3-агент текущего пользователя. Если нет, pid-файл удаляется, и агент запускается.
Нормальное завершение агента, работающего в режиме демона, выполняется
с помощью сигнала TERM. При завершении агент автоматически стирает свой pid-файл.
Добавлены атрибуты команды, хранящие информацию о участке бинарного файла скрипта,
соответствующей команды:
raw_start - начало блока команды
raw_output_start - начало вывода команды
raw_end - окончание вывода
raw_file - имя бинарного файла
Файлы:
(могут меняться с помощью конфигурационных параметров)
~/.labmaker/.cache.dat
~/.labmaker/cache.xml
~/.labmaker/l3-agent.pid
Конфигурационные параметры:
cache_stat Имя файла с информацией о текущей позиции разбора
в каждом файле
mode Режим, в котором работает агент.
Допустимые значения:
daemon - в режиме непрерывного опроса каталога
Программа не завершается после окончания анализа,
а ждёт появления новых данных
normal - однократный анализ каталога.
Программа завершается после окончания анализа данных
daemon_sleep_interval Интервал через который агент просматривает каталог скриптов
в поисках новых данных
detach Нужно ли выполнять отключение от терминала при работе в режиме демона?
(строго говоря, если процесс не отключился от терминала,
то и в режиме демона он работать не может. Здесь имеется в виду
режим непрерывного опроса каталога)
agent_pidfile Путь к файлу, который будет хранить идентификатор процесса агента.
l3-agent Имя, под которым будет известен процесс l3-agent
Незначительные исправления:
* убрана отладочная информация о new_commands и new_files из frontend'а
author | devi |
---|---|
date | Thu Nov 03 17:49:56 2005 +0200 (2005-11-03) |
parents | 4b86595adb4b |
children | 916661a89335 |
files | l3-agent l3-frontend l3config.pm |
line diff
1.1 --- a/l3-agent Wed Nov 02 19:25:39 2005 +0200 1.2 +++ b/l3-agent Thu Nov 03 17:49:56 2005 +0200 1.3 @@ -5,6 +5,7 @@ 1.4 # 1.5 1.6 use strict; 1.7 +use POSIX; 1.8 use Term::VT102; 1.9 use Text::Iconv; 1.10 use Data::Dumper; 1.11 @@ -21,6 +22,13 @@ 1.12 our %Commands_Stat; # Statistics about commands usage 1.13 our %Files_Stat; # Statistics about commands usage 1.14 1.15 +our %Script_Files; # Информация о позициях в скрипт-файлах, 1.16 + # до которых уже выполнен разбор 1.17 + # и информация о времени модификации файла 1.18 + # $Script_Files{$file}->{size} 1.19 + # $Script_Files{$file}->{tell} 1.20 + 1.21 +our $Killed =0; # В режиме демона -- процесс получил сигнал о завершении 1.22 1.23 sub init_variables; 1.24 sub main; 1.25 @@ -34,6 +42,10 @@ 1.26 sub print_command_lines; 1.27 sub printq; 1.28 1.29 +sub save_cache_stat; 1.30 +sub load_cache_stat; 1.31 + 1.32 + 1.33 sub load_diff_files 1.34 { 1.35 my @pathes = @_; 1.36 @@ -183,7 +195,8 @@ 1.37 my $lab_scripts_mask = $_[1]; 1.38 1.39 my $cline_re_base = qq' 1.40 - (?:\\^?([0-9]*C?)) # exitcode 1.41 + ( 1.42 + (?:\\^?([0-9]*C?)) # exitcode 1.43 (?:_([0-9]+)_)? # uid 1.44 (?:_([0-9]+)_) # pid 1.45 (...?) # day 1.46 @@ -193,6 +206,7 @@ 1.47 .\\[50D.\\[K # killing symbols 1.48 (.*?([\$\#]\\s?)) # prompt 1.49 (.*) # command line 1.50 + ) 1.51 '; 1.52 #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x; 1.53 #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; 1.54 @@ -221,9 +235,20 @@ 1.55 1.56 for $file (@lab_scripts){ 1.57 #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/; 1.58 + 1.59 + # Пропускаем файл, если он не изменялся со времени нашего предудущего прохода 1.60 + my $size = (stat($file))[7]; 1.61 + next if ($Script_Files{$file} && $Script_Files{$file}->{size} && $Script_Files{$file}->{size} >= $size); 1.62 1.63 open (FILE, "$file"); 1.64 binmode FILE; 1.65 + 1.66 + # Переходим к тому месту, где мы окончили разбор 1.67 + seek (FILE, $Script_Files{$file}->{tell}, 0) if $Script_Files{$file}->{tell}; 1.68 + $Script_Files{$file}->{size} = $size; 1.69 + $Script_Files{$file}->{tell} = 0 unless $Script_Files{$file}->{tell}; 1.70 + 1.71 + 1.72 $file =~ m@.*/(.*?)-.*@; 1.73 1.74 my $tty = $1; 1.75 @@ -231,6 +256,7 @@ 1.76 my %cl; 1.77 my $last_output_length=0; 1.78 while (<FILE>) { 1.79 + 1.80 $commandlines_processed++; 1.81 # time 1.82 1.83 @@ -247,7 +273,7 @@ 1.84 1.85 # Previous command 1.86 my %last_cl = %cl; 1.87 - my $err = $1 || ""; 1.88 + my $err = $2 || ""; 1.89 1.90 1.91 =cut 1.92 @@ -380,11 +406,14 @@ 1.93 raw_start (*) 1.94 Начало блока командной строки в файле бинарного представления 1.95 1.96 + raw_output_start (*) 1.97 + Начало блока вывода 1.98 + 1.99 raw_end (*) 1.100 Конец блока командной строки в файле бинарного представления 1.101 1.102 raw_cline (*) 1.103 - Необработанная командная строка в бинарном виде 1.104 + Необработанная командная строка (без приглашения) в бинарном виде 1.105 1.106 raw_data (*) 1.107 Бинарное представление команды и результатов её выполнения 1.108 @@ -402,17 +431,24 @@ 1.109 =cut 1.110 1.111 # Parse new command 1.112 - $cl{"uid"} = $2; 1.113 + $cl{"uid"} = $3; 1.114 $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 1.115 - $cl{"pid"} = $3; 1.116 - $cl{"day"} = $4; 1.117 - $cl{"lab"} = $5; 1.118 - $cl{"hour"} = $6; 1.119 - $cl{"min"} = $7; 1.120 - $cl{"sec"} = $8; 1.121 - $cl{"fullprompt"} = $9; 1.122 - $cl{"prompt"} = $10; 1.123 - $cl{"raw_cline"} = $11; 1.124 + $cl{"pid"} = $4; 1.125 + $cl{"day"} = $5; 1.126 + $cl{"lab"} = $6; 1.127 + $cl{"hour"} = $7; 1.128 + $cl{"min"} = $8; 1.129 + $cl{"sec"} = $9; 1.130 + $cl{"fullprompt"} = $10; 1.131 + $cl{"prompt"} = $11; 1.132 + $cl{"raw_cline"} = $12; 1.133 + 1.134 + { 1.135 + use bytes; 1.136 + $cl{"raw_start"} = tell (FILE) - length($1); 1.137 + $cl{"raw_output_start"} = tell FILE; 1.138 + } 1.139 + $cl{"raw_file"} = $file; 1.140 1.141 $cl{"err"} = 0; 1.142 $cl{"output"} = ""; 1.143 @@ -454,6 +490,7 @@ 1.144 } 1.145 1.146 # Error code 1.147 + $last_cl{"raw_end"} = $cl{"raw_start"}; 1.148 $last_cl{"err"}=$err; 1.149 $last_cl{"err"}=130 if $err eq "^C"; 1.150 1.151 @@ -484,11 +521,16 @@ 1.152 if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { 1.153 # Changing encoding 1.154 for (keys %last_cl) { 1.155 + next if /raw/; 1.156 $last_cl{$_} = $converter->convert($last_cl{$_}) 1.157 if ($Config{"encoding"} && 1.158 $Config{"encoding"} !~ /^utf-8$/i); 1.159 } 1.160 push @Command_Lines, \%last_cl; 1.161 + 1.162 + # Сохранение позиции в файле, до которой выполнен 1.163 + # успешный разбор 1.164 + $Script_Files{$file}->{tell} = $last_cl{raw_end}; 1.165 } 1.166 next; 1.167 } 1.168 @@ -620,12 +662,14 @@ 1.169 sub print_command_lines 1.170 { 1.171 my $output_filename=$_[0]; 1.172 - open(OUT, ">", $output_filename) 1.173 + my $mode = ">"; 1.174 + $mode =">>" if $Config{mode} eq "daemon"; 1.175 + open(OUT, $mode, $output_filename) 1.176 or die "Can't open $output_filename for writing\n"; 1.177 1.178 1.179 1.180 - print OUT "<livelablog>\n"; 1.181 + #print OUT "<livelablog>\n"; 1.182 1.183 my $cl; 1.184 my $in_range=0; 1.185 @@ -708,6 +752,10 @@ 1.186 # Начинаем вывод команды 1.187 print OUT "<command>\n"; 1.188 print OUT "<time>",$cl->{time},"</time>\n"; 1.189 + print OUT "<raw_start>",$cl->{raw_start},"</raw_start>\n"; 1.190 + print OUT "<raw_output_start>",$cl->{raw_output_start},"</raw_output_start>\n"; 1.191 + print OUT "<raw_end>",$cl->{raw_end},"</raw_end>\n"; 1.192 + print OUT "<raw_file>",$cl->{raw_file},"</raw_file>\n"; 1.193 print OUT "<tty>",$cl->{tty},"</tty>\n"; 1.194 print OUT "<out_class>",$out_class,"</out_class>\n"; 1.195 print OUT "<prompt>"; 1.196 @@ -739,10 +787,31 @@ 1.197 1.198 } 1.199 1.200 - print OUT "</livelablog>\n"; 1.201 + #print OUT "</livelablog>\n"; 1.202 close(OUT); 1.203 + save_cache_stat(); 1.204 } 1.205 1.206 +sub save_cache_stat 1.207 +{ 1.208 + open (CACHE, ">$Config{cache_stat}"); 1.209 + for my $f (keys %Script_Files) { 1.210 + print CACHE "$f\t",$Script_Files{$f}->{size},"\t",$Script_Files{$f}->{tell},"\n"; 1.211 + } 1.212 + close(CACHE); 1.213 +} 1.214 + 1.215 +sub load_cache_stat 1.216 +{ 1.217 + if (open (CACHE, "$Config{cache_stat}")) { 1.218 + while(<CACHE>) { 1.219 + my ($f, $size, $tell) = split /\t/; 1.220 + $Script_Files{$f}->{size} = $size; 1.221 + $Script_Files{$f}->{tell} = $tell; 1.222 + } 1.223 + close(CACHE); 1.224 + }; 1.225 +} 1.226 1.227 =cut 1.228 sub print_command_lines2 1.229 @@ -806,6 +875,11 @@ 1.230 1.231 main(); 1.232 1.233 +sub process_was_killed 1.234 +{ 1.235 + $Killed = 1; 1.236 +} 1.237 + 1.238 sub main 1.239 { 1.240 $| = 1; 1.241 @@ -817,10 +891,64 @@ 1.242 load_diff_files($lab_log); 1.243 } 1.244 1.245 -load_command_lines($Config{"input"}, $Config{"input_mask"}); 1.246 -sort_command_lines; 1.247 -process_command_lines; 1.248 -print_command_lines($Config{"cache"}); 1.249 +if ($Config{"mode"} ne "daemon") { 1.250 + load_command_lines($Config{"input"}, $Config{"input_mask"}); 1.251 + sort_command_lines; 1.252 + process_command_lines; 1.253 + print_command_lines($Config{"cache"}); 1.254 +} 1.255 +else { 1.256 + if (open(PIDFILE, $Config{agent_pidfile})) { 1.257 + my $pid = <PIDFILE>; 1.258 + close(PIDFILE); 1.259 + if ( ! -e "/proc/$pid" || !`grep $Config{"l3-agent"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`) { 1.260 + print "Removing stale pidfile\n"; 1.261 + unlink $Config{agent_pidfile}; 1.262 + or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!"; 1.263 + } 1.264 + else { 1.265 + print "l3-agent is already running\n"; 1.266 + exit(0); 1.267 + } 1.268 + } 1.269 + if ($Config{detach} =~ /^y/i) { 1.270 + #$Config{verbose} = "no"; 1.271 + my $pid = fork; 1.272 + exit if $pid; 1.273 + die "Couldn't fork: $!" unless defined ($pid); 1.274 + 1.275 + open(PIDFILE, ">", $Config{agent_pidfile}) 1.276 + or die "Can't open pidfile ". $Config{agent_pidfile}. " for wrting: $!"; 1.277 + print PIDFILE $$; 1.278 + close(PIDFILE); 1.279 + 1.280 + for my $handle (*STDIN, *STDOUT, *STDERR) { 1.281 + open ($handle, "+<", "/dev/null") 1.282 + or die "can't reopen $handle to /dev/null: $!" 1.283 + } 1.284 + 1.285 + POSIX::setsid() 1.286 + or die "Can't start a new session: $!"; 1.287 + 1.288 + $0 = $Config{"l3-agent"}; 1.289 + 1.290 + $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&process_was_killed; 1.291 + } 1.292 + while (not $Killed) { 1.293 + @Command_Lines = (); 1.294 + @Command_Lines_Index = (); 1.295 + load_cache_stat(); 1.296 + load_command_lines($Config{"input"}, $Config{"input_mask"}); 1.297 + if (@Command_Lines) { 1.298 + sort_command_lines; 1.299 + process_command_lines; 1.300 + print_command_lines($Config{"cache"}); 1.301 + } 1.302 + sleep($Config{"daemon_sleep_interval"} || 1); 1.303 + } 1.304 + 1.305 + unlink $Config{agent_pidfile}; 1.306 +} 1.307 1.308 } 1.309
2.1 --- a/l3-frontend Wed Nov 02 19:25:39 2005 +0200 2.2 +++ b/l3-frontend Thu Nov 03 17:49:56 2005 +0200 2.3 @@ -306,15 +306,6 @@ 2.4 $Result{"body"} .= "</table>\n"; 2.5 $Result{"body"} .= "</td></tr></table>"; 2.6 } 2.7 - else { 2.8 - $Result{"body"} .= "<table width='$Config{comment_width}'>". 2.9 - "<tr><td width='5'/><td>"; 2.10 - $Result{"body"} .= "<table class='note' width='100%'>"; 2.11 - $Result{"body"} .= "commands ".join(" ",@new_commands)."<br/>"; 2.12 - $Result{"body"} .= "files ".join(" ",@new_files)."<br/>"; 2.13 - $Result{"body"} .= "</table>\n"; 2.14 - $Result{"body"} .= "</td></tr></table>"; 2.15 - } 2.16 } 2.17 2.18 # Вывод очередной команды окончен
3.1 --- a/l3config.pm Wed Nov 02 19:25:39 2005 +0200 3.2 +++ b/l3config.pm Thu Nov 03 17:49:56 2005 +0200 3.3 @@ -40,6 +40,7 @@ 3.4 "encoding" => "utf-8", 3.5 3.6 "cache" => "/tmp/report.xml", 3.7 + "cache_stat" => "/tmp/.report.dat", 3.8 3.9 "output" => "/tmp/report.html", 3.10 #"output" => "report.xml", 3.11 @@ -55,7 +56,14 @@ 3.12 3.13 comment_width => "300", 3.14 time_width => "60", 3.15 + 3.16 + "mode" => "daemon", # daemon | normal 3.17 + "daemon_sleep_interval" => "1", 3.18 + "detach" => "yes", 3.19 + "agent_pidfile" => "$ENV{HOME}/.labmaker/l3-agent.pid", 3.20 3.21 + "l3-agent" => "l3-agent", 3.22 + 3.23 "course-name" => "", 3.24 "course-code" => "", 3.25 "course-date" => "",