lilalo
diff l3-agent @ 23:6d93c5f1d0e5
Выполнен шаг (2) в плане (N05) по построению распределённой системы lilalo.
Программа lm-report разрезана на две: l3-agent и l3-frontend.
Агент выполняет анализ script-файлов и записывает
результаты анализа в файл обмена (cache).
Фронтенд читает данные из файла обмена и представляет
их в требуемом формате (в настоящий момент только html).
Сейчас взаимодействие agent'а и frontend'а выполняется так:
. ^ . +-------+ . ^^ .
/ \ | | / \
( agent )-->| cache |--->( frontend )
\ / | | \ /
' . ' +-------+ ' .. '
Добавлены файлы:
l3-agent - агент
l3-frontend - фронтенд
l3-report - замена lm-report, использующая l3-agent и l3-frontend
l3-config.pm - модуль конфигурации системы
Новые конфигурационные параметры:
cache - Путь к временному XML-файлу, предназначенному
для обмена информацией между агентом и фронтендом
cache_head_lines - Количество строк вывода команды сверху, которые
должны быть сохранены в промежуточном XML-файле
cache_tail_lines - Количество строк вывода команды снизу, которые
должны быть сохранены в промежуточном XML-файле
Устаревшие параметры:
output_mask - Использование output_mask осуждается.
Параметр будет удалён из будущих версий
Использование lm-report осуждается.
В будущих версиях программа lm-report будет удалена из дистрибутива.
Вместо неё нужно использовать l3-report.
Программа lm-report разрезана на две: l3-agent и l3-frontend.
Агент выполняет анализ script-файлов и записывает
результаты анализа в файл обмена (cache).
Фронтенд читает данные из файла обмена и представляет
их в требуемом формате (в настоящий момент только html).
Сейчас взаимодействие agent'а и frontend'а выполняется так:
. ^ . +-------+ . ^^ .
/ \ | | / \
( agent )-->| cache |--->( frontend )
\ / | | \ /
' . ' +-------+ ' .. '
Добавлены файлы:
l3-agent - агент
l3-frontend - фронтенд
l3-report - замена lm-report, использующая l3-agent и l3-frontend
l3-config.pm - модуль конфигурации системы
Новые конфигурационные параметры:
cache - Путь к временному XML-файлу, предназначенному
для обмена информацией между агентом и фронтендом
cache_head_lines - Количество строк вывода команды сверху, которые
должны быть сохранены в промежуточном XML-файле
cache_tail_lines - Количество строк вывода команды снизу, которые
должны быть сохранены в промежуточном XML-файле
Устаревшие параметры:
output_mask - Использование output_mask осуждается.
Параметр будет удалён из будущих версий
Использование lm-report осуждается.
В будущих версиях программа lm-report будет удалена из дистрибутива.
Вместо неё нужно использовать l3-report.
author | devi |
---|---|
date | Wed Nov 02 19:16:11 2005 +0200 (2005-11-02) |
parents | |
children | ba4d6515b8fd |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/l3-agent Wed Nov 02 19:16:11 2005 +0200 1.3 @@ -0,0 +1,829 @@ 1.4 +#!/usr/bin/perl -w 1.5 + 1.6 +# 1.7 +# (c) Igor Chubin, imchubin@mail.ru, 2004-2005 1.8 +# 1.9 + 1.10 +use strict; 1.11 +use Term::VT102; 1.12 +use Text::Iconv; 1.13 +use Data::Dumper; 1.14 +use Time::Local 'timelocal_nocheck'; 1.15 + 1.16 +use lib "."; 1.17 +use l3config; 1.18 + 1.19 + 1.20 +our @Command_Lines; 1.21 +our @Command_Lines_Index; 1.22 +our @Diffs; 1.23 + 1.24 +our %Commands_Stat; # Statistics about commands usage 1.25 +our %Files_Stat; # Statistics about commands usage 1.26 + 1.27 + 1.28 +sub init_variables; 1.29 +sub main; 1.30 + 1.31 +sub load_diff_files; 1.32 +sub bind_diff; 1.33 +sub extract_from_cline; 1.34 +sub load_command_lines; 1.35 +sub sort_command_lines; 1.36 +sub process_command_lines; 1.37 +sub print_command_lines; 1.38 +sub printq; 1.39 + 1.40 +sub load_diff_files 1.41 +{ 1.42 + my @pathes = @_; 1.43 + 1.44 + for my $path (@pathes) { 1.45 + my $template = "*.diff"; 1.46 + my @files = <$path/$template>; 1.47 + my $i=0; 1.48 + for my $file (@files) { 1.49 + my %diff; 1.50 + 1.51 + $diff{"path"}=$path; 1.52 + $diff{"uid"}="SET THIS"; 1.53 + 1.54 +# Сейчас UID определяется из названия каталога 1.55 +# откуда берутся diff-файлы 1.56 +# Это неправильно 1.57 +# 1.58 +# ВАРИАНТ: 1.59 +# К файлам жураналам должны прилагаться ситемны файлы, 1.60 +# мз которых и будет определяться соответствие 1.61 +# имён пользователей их uid'ам 1.62 +# 1.63 + $diff{"uid"} = 0 if $path =~ m@/root/@; 1.64 + 1.65 + $diff{"bind_to"}=""; 1.66 + $diff{"time_range"}=-1; 1.67 + 1.68 + next if not $file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@; 1.69 + $diff{"day"}=$1 || ""; 1.70 + $diff{"hour"}=$2; 1.71 + $diff{"min"}=$3; 1.72 + $diff{"sec"}=$4 || 0; 1.73 + 1.74 + $diff{"index"}=$i; 1.75 + 1.76 + print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n"; 1.77 + 1.78 + local $/; 1.79 + open (F, "$file") 1.80 + or return "Can't open file $file ($_[0]) for reading"; 1.81 + my $text = <F>; 1.82 + if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) { 1.83 + my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8"); 1.84 + $text = $converter->convert($text); 1.85 + } 1.86 + close(F); 1.87 + $diff{"text"}=$text; 1.88 + #print "$file loaded ($diff{day})\n"; 1.89 + 1.90 + push @Diffs, \%diff; 1.91 + $i++; 1.92 + } 1.93 + } 1.94 +} 1.95 + 1.96 + 1.97 +sub bind_diff 1.98 +{ 1.99 +# my $path = shift; 1.100 +# my $pid = shift; 1.101 +# my $day = shift; 1.102 +# my $lab = shift; 1.103 + 1.104 + print "Trying to bind diff...\n"; 1.105 + 1.106 + my $cl = shift; 1.107 + my $hour = $cl->{"hour"}; 1.108 + my $min = $cl->{"min"}; 1.109 + my $sec = $cl->{"sec"}; 1.110 + 1.111 + my $min_dt = 10000; 1.112 + 1.113 + for my $diff (@Diffs) { 1.114 + # Check here date, time and user 1.115 + next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"})); 1.116 + #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"}); 1.117 + 1.118 + my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec); 1.119 + if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) { 1.120 + print "Approppriate diff found: dt=$dt\n"; 1.121 + if ($diff->{"bind_to"}) { 1.122 + undef $diff->{"bind_to"}->{"diff"}; 1.123 + }; 1.124 + $diff->{"time_range"}=$dt; 1.125 + $diff->{"bind_to"}=$cl; 1.126 + 1.127 + $cl->{"diff"} = $diff->{"index"}; 1.128 + $min_dt = $dt; 1.129 + } 1.130 + 1.131 + } 1.132 +} 1.133 + 1.134 + 1.135 +sub extract_from_cline 1.136 +# Разобрать командную строку $_[1] и возвратить хэш, содержащий 1.137 +# номер первого появление команды в строке: 1.138 +# команда => первая позиция 1.139 +{ 1.140 + my $what = $_[0]; 1.141 + my $cline = $_[1]; 1.142 + my @lists = split /\;/, $cline; 1.143 + 1.144 + 1.145 + my @commands = (); 1.146 + for my $list (@lists) { 1.147 + push @commands, split /\|/, $list; 1.148 + } 1.149 + 1.150 + my %commands; 1.151 + my %files; 1.152 + my $i=0; 1.153 + for my $command (@commands) { 1.154 + $command =~ /\s*(\S+)\s*(.*)/; 1.155 + if ($1 && $1 eq "sudo" ) { 1.156 + $commands{"$1"}=$i++; 1.157 + $command =~ s/\s*sudo\s+//; 1.158 + } 1.159 + $command =~ /\s*(\S+)\s*(.*)/; 1.160 + if ($1 && !defined $commands{"$1"}) { 1.161 + $commands{"$1"}=$i++; 1.162 + }; 1.163 + if ($2) { 1.164 + my $args = $2; 1.165 + my @args = split (/\s+/, $args); 1.166 + for my $a (@args) { 1.167 + $files{"$a"}=$i++ 1.168 + if !defined $files{"$a"}; 1.169 + }; 1.170 + 1.171 + 1.172 + } 1.173 + } 1.174 + 1.175 + if ($what eq "commands") { 1.176 + return %commands; 1.177 + } else { 1.178 + return %files; 1.179 + } 1.180 + 1.181 +} 1.182 + 1.183 +sub load_command_lines 1.184 +{ 1.185 + my $lab_scripts_path = $_[0]; 1.186 + my $lab_scripts_mask = $_[1]; 1.187 + 1.188 + my $cline_re_base = qq' 1.189 + (?:\\^?([0-9]*C?)) # exitcode 1.190 + (?:_([0-9]+)_)? # uid 1.191 + (?:_([0-9]+)_) # pid 1.192 + (...?) # day 1.193 + (.?.?) # lab 1.194 + \\s # space separator 1.195 + ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time 1.196 + .\\[50D.\\[K # killing symbols 1.197 + (.*?([\$\#]\\s?)) # prompt 1.198 + (.*) # command line 1.199 + '; 1.200 + #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x; 1.201 + #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; 1.202 + my $cline_re = qr/$cline_re_base/sx; 1.203 + my $cline_re1 = qr/$cline_re_base\x0D/sx; 1.204 + my $cline_re2 = qr/$cline_re_base$/sx; 1.205 + 1.206 + my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"}, 1.207 + 'rows' => $Config{"terminal_height"}); 1.208 + my $cline_vt = Term::VT102->new ('cols' => $Config{"terminal_width"}, 1.209 + 'rows' => $Config{"terminal_height"}); 1.210 + 1.211 + my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8") 1.212 + if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i); 1.213 + 1.214 + print "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/; 1.215 + 1.216 + my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>; 1.217 + my $file; 1.218 + my $files_number = $#lab_scripts; 1.219 + my $ii = 0; 1.220 + my $skip_info; 1.221 + 1.222 + my $commandlines_loaded =0; 1.223 + my $commandlines_processed =0; 1.224 + 1.225 + for $file (@lab_scripts){ 1.226 + #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/; 1.227 + 1.228 + open (FILE, "$file"); 1.229 + binmode FILE; 1.230 + $file =~ m@.*/(.*?)-.*@; 1.231 + 1.232 + my $tty = $1; 1.233 + my $first_pass = 1; 1.234 + my %cl; 1.235 + my $last_output_length=0; 1.236 + while (<FILE>) { 1.237 + $commandlines_processed++; 1.238 + # time 1.239 + 1.240 + if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) { 1.241 + s/.*\x0d(?!\x0a)//; 1.242 + # print "!!!",$_,"!!!\n"; 1.243 + # next; 1.244 + # while (m/$cline_re1/gs) { 1.245 + # } 1.246 + m/$cline_re2/gs; 1.247 + 1.248 + $commandlines_loaded++; 1.249 + $last_output_length=0; 1.250 + 1.251 + # Previous command 1.252 + my %last_cl = %cl; 1.253 + my $err = $1 || ""; 1.254 + 1.255 + 1.256 +=cut 1.257 + 1.258 +ТАБЛИЦА КОМАНД 1.259 + 1.260 + uid 1.261 + Идентификатор пользователя 1.262 + 1.263 + tty 1.264 + Идентификатор терминала, на котором была вызвана команда 1.265 + 1.266 + pid 1.267 + PID-процесса командного интерпретатора, 1.268 + в котором была вызвана команда 1.269 + 1.270 + lab 1.271 + лабораторная работа, к которой относится команда. 1.272 + Идентификатор текущей лабораторной работы 1.273 + хранится в файле ~/.labmaker/lab 1.274 + 1.275 + pwd (!) 1.276 + текущий каталог, из которого была вызвана команда 1.277 + 1.278 + day 1.279 + время вызова, день 1.280 + В действительности здесь хранится не время вызова команды, 1.281 + а с момента появления приглашения командного интерпретатора 1.282 + для ввода команды 1.283 + 1.284 + 1.285 + hour 1.286 + время вызова, час 1.287 + 1.288 + min 1.289 + время вызова, минута 1.290 + 1.291 + sec 1.292 + время вызова, секунда 1.293 + 1.294 + time (!) 1.295 + время вызова команды в Unix-формате. 1.296 + Предпочтительнее использовать этот формат чем hour:min:sec, 1.297 + использовавшийся в Labmaker 1.298 + 1.299 + fullprompt 1.300 + Приглашение командной строки 1.301 + 1.302 + prompt 1.303 + Сокращённое приглашение командной строки 1.304 + 1.305 + cline 1.306 + Командная строка 1.307 + 1.308 + output 1.309 + Результат выполнения команды 1.310 + 1.311 + diff 1.312 + Указатель на ассоциированный с командой diff 1.313 + 1.314 + note (!) 1.315 + Текстовый комментарий к команде. 1.316 + Может генерироваться из самого лога с помощью команд 1.317 + #^ Комментарий 1.318 + #v Комментарий 1.319 + в том случае, если для комментирования достаточно одной строки, 1.320 + или с помощью команд 1.321 + cat > /dev/null #^ Заголовок 1.322 + Текст 1.323 + ^D 1.324 + в том случае, если комментарий развёрнутый. 1.325 + В последнем случае комментарий может содержать 1.326 + заголовок, абзацы и несложное форматирование. 1.327 + 1.328 + Символ ^ или v после знака комментария # обозначает, 1.329 + к какой команде относится комментарий: 1.330 + к предыдущей (^) или последующей (v) 1.331 + 1.332 + err 1.333 + Код завершения командной строки 1.334 + 1.335 + histnum (!) 1.336 + Номер команды в истории командного интерпретатора 1.337 + 1.338 + status (!) 1.339 + Является ли данная команда вызванной (r), запомненной (s) 1.340 + или это подсказка completion (c). 1.341 + 1.342 + Команды, которые были вызваны и обработаны интерпретатором 1.343 + имеют состояние "r". К таким командам относится большинство 1.344 + команд вводимых в интерпретатор. 1.345 + 1.346 + Если команда набрана, но вызывать её по какой-либо причине 1.347 + не хочется (например, команда может быть не полной, вредоносной 1.348 + или просто бессмысленной в текущих условиях), 1.349 + её можно сбросить с помощью комбинации клавиш Ctrl-C 1.350 + (не путайте с прерыванием работающей команды! здесь она даже 1.351 + не запускается!). 1.352 + В таком случае она не выполняется, но попадает в журнал 1.353 + со статусом "s". 1.354 + 1.355 + Если команда появилась в журнале благодаря автопроолжению 1.356 + -- когда было показано несколько вариантов -- 1.357 + она имеет статус "c". 1.358 + 1.359 + euid 1.360 + Идентификатор пользователя от имени которого будет 1.361 + выполняться команда. 1.362 + Может отличаться от реального uid в том случае, 1.363 + если вызывается с помощью sudo 1.364 + 1.365 + 1.366 + version (!) 1.367 + Версия lilalo-prompt использовавшаяся при записи 1.368 + команды. 1.369 + 1.370 + 0 - версия использовавшая в labmaker. 1.371 + Отсутствует информация о текущем каталоге и номере в истории. 1.372 + Информация о версии также не указана в приглашении. 1.373 + 1.374 + 1.375 + 1 - версия использующаяся в lilalo 1.376 + 1.377 + raw_file (*) 1.378 + Имя файла, в котором находится бинарное представление журнала. 1.379 + Может содержать ключевое слово HERE, 1.380 + обозначающее что бинарное представление хранится 1.381 + непосредственно в базе данных в атрибуте raw_data 1.382 + 1.383 + raw_start (*) 1.384 + Начало блока командной строки в файле бинарного представления 1.385 + 1.386 + raw_end (*) 1.387 + Конец блока командной строки в файле бинарного представления 1.388 + 1.389 + raw_cline (*) 1.390 + Необработанная командная строка в бинарном виде 1.391 + 1.392 + raw_data (*) 1.393 + Бинарное представление команды и результатов её выполнения 1.394 + 1.395 + 1.396 + 1.397 + 1.398 +ТАБЛИЦА SESSION 1.399 + 1.400 + Информация о сеансах 1.401 + 1.402 + 1.403 + 1.404 + 1.405 +=cut 1.406 + 1.407 + # Parse new command 1.408 + $cl{"uid"} = $2; 1.409 + $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 1.410 + $cl{"pid"} = $3; 1.411 + $cl{"day"} = $4; 1.412 + $cl{"lab"} = $5; 1.413 + $cl{"hour"} = $6; 1.414 + $cl{"min"} = $7; 1.415 + $cl{"sec"} = $8; 1.416 + $cl{"fullprompt"} = $9; 1.417 + $cl{"prompt"} = $10; 1.418 + $cl{"raw_cline"} = $11; 1.419 + 1.420 + $cl{"err"} = 0; 1.421 + $cl{"output"} = ""; 1.422 + $cl{"tty"} = $tty; 1.423 + 1.424 + $cline_vt->process($cl{"raw_cline"}."\n"); 1.425 + $cl{"cline"} = $cline_vt->row_plaintext (1); 1.426 + $cl{"cline"} =~ s/\s*$//; 1.427 + $cline_vt->reset(); 1.428 + 1.429 + my %commands = extract_from_cline("commands", $cl{"cline"}); 1.430 + $cl{"euid"}=0 if defined $commands{"sudo"}; 1.431 + my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands; 1.432 + $cl{"last_command"} = $comms[$#comms] || ""; 1.433 + 1.434 + if ( 1.435 + $Config{"suppress_editors"} =~ /^y/i 1.436 + && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}}) || 1.437 + $Config{"suppress_pagers"} =~ /^y/i 1.438 + && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}}) || 1.439 + $Config{"suppress_terminal"}=~ /^y/i 1.440 + && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}}) 1.441 + ) { 1.442 + $cl{"suppress_output"} = "1"; 1.443 + } 1.444 + else { 1.445 + $cl{"suppress_output"} = "0"; 1.446 + 1.447 + } 1.448 + $skip_info = 0; 1.449 + 1.450 + 1.451 + print " ",$cl{"last_command"}; 1.452 + 1.453 + # Processing previous command line 1.454 + if ($first_pass) { 1.455 + $first_pass = 0; 1.456 + next; 1.457 + } 1.458 + 1.459 + # Error code 1.460 + $last_cl{"err"}=$err; 1.461 + $last_cl{"err"}=130 if $err eq "^C"; 1.462 + 1.463 + if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) { 1.464 + bind_diff(\%last_cl); 1.465 + } 1.466 + 1.467 + # Output 1.468 + if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { 1.469 + for (my $i=0; $i<$Config{"terminal_height"}; $i++) { 1.470 + my $line= $vt->row_plaintext($i); 1.471 + next if !defined ($line) || $line =~ /^\s*$/; 1.472 + $line =~ s/\s*$//; 1.473 + $last_cl{"output"} .= $line."\n"; 1.474 + } 1.475 + } 1.476 + else { 1.477 + $last_cl{"output"}= ""; 1.478 + } 1.479 + 1.480 + $vt->reset(); 1.481 + 1.482 + 1.483 + # Classifying the command line 1.484 + 1.485 + 1.486 + # Save 1.487 + if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { 1.488 + # Changing encoding 1.489 + for (keys %last_cl) { 1.490 + $last_cl{$_} = $converter->convert($last_cl{$_}) 1.491 + if ($Config{"encoding"} && 1.492 + $Config{"encoding"} !~ /^utf-8$/i); 1.493 + } 1.494 + push @Command_Lines, \%last_cl; 1.495 + } 1.496 + next; 1.497 + } 1.498 + $last_output_length+=length($_); 1.499 + #if (!$cl{"suppress_output"} || $last_output_length < 5000) { 1.500 + if ($last_output_length < 50000) { 1.501 + #print "(",length($_),")" if (length($_) > 2000) ; 1.502 + $vt->process("$_"."\n") 1.503 + } 1.504 + else 1.505 + { 1.506 + if (!$skip_info) { 1.507 + print "($cl{last_command})"; 1.508 + $skip_info = 1; 1.509 + } 1.510 + } 1.511 + } 1.512 + close(FILE); 1.513 + 1.514 + } 1.515 + if ($Config{"verbose"} =~ /y/) { 1.516 + print "...finished." ; 1.517 + print "Lines loaded: $commandlines_processed\n"; 1.518 + print "Command lines: $commandlines_loaded\n"; 1.519 + } 1.520 +} 1.521 + 1.522 + 1.523 + 1.524 +sub printq 1.525 +{ 1.526 + my $TO = shift; 1.527 + my $text = join "", @_; 1.528 + $text =~ s/&/&/g; 1.529 + $text =~ s/</</g; 1.530 + $text =~ s/>/>/g; 1.531 + print $TO $text; 1.532 +} 1.533 + 1.534 + 1.535 +sub sort_command_lines 1.536 +{ 1.537 + print "Sorting command lines...\n" if $Config{"verbose"} =~ /y/; 1.538 + 1.539 + # Sort Command_Lines 1.540 + # Write Command_Lines to Command_Lines_Index 1.541 + 1.542 + my @index; 1.543 + for (my $i=0;$i<=$#Command_Lines;$i++) { 1.544 + $index[$i]=$i; 1.545 + } 1.546 + 1.547 + @Command_Lines_Index = sort { 1.548 + $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"} || 1.549 + $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"} || 1.550 + $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"} || 1.551 + $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"} 1.552 + } @index; 1.553 + 1.554 + print "...finished\n" if $Config{"verbose"} =~ /y/; 1.555 + 1.556 +} 1.557 + 1.558 +sub process_command_lines 1.559 +{ 1.560 + for my $i (@Command_Lines_Index) { 1.561 + 1.562 + my $cl = \$Command_Lines[$i]; 1.563 + @{${$cl}->{"new_commands"}} =(); 1.564 + @{${$cl}->{"new_files"}} =(); 1.565 + $$cl->{"class"} = ""; 1.566 + 1.567 + if ($$cl->{"err"}) { 1.568 + $$cl->{"class"}="wrong"; 1.569 + $$cl->{"class"}="interrupted" 1.570 + if ($$cl->{"err"} eq 130); 1.571 + } 1.572 + if (!$$cl->{"euid"}) { 1.573 + $$cl->{"class"}.="_root"; 1.574 + } 1.575 + 1.576 +#tab# my @tab_words=split /\s+/, $$cl->{"output"}; 1.577 +#tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; 1.578 +#tab# $last_word =~ s@.*/@@; 1.579 +#tab# my $this_is_tab=1; 1.580 +#tab# 1.581 +#tab# if ($last_word && @tab_words >2) { 1.582 +#tab# for my $tab_words (@tab_words) { 1.583 +#tab# if ($tab_words !~ /^$last_word/) { 1.584 +#tab# $this_is_tab=0; 1.585 +#tab# last; 1.586 +#tab# } 1.587 +#tab# } 1.588 +#tab# } 1.589 +#tab# $$cl->{"class"}="tab" if $this_is_tab; 1.590 + 1.591 + 1.592 + if ( !$$cl->{"err"}) { 1.593 + # Command does not contain mistakes 1.594 + 1.595 + my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); 1.596 + my %files = extract_from_cline("files", ${$cl}->{"cline"}); 1.597 + 1.598 + # Searching for new commands only 1.599 + for my $command (keys %commands) { 1.600 + if (!defined $Commands_Stat{$command}) { 1.601 + push @{$$cl->{new_commands}}, $command; 1.602 + } 1.603 + $Commands_Stat{$command}++; 1.604 + } 1.605 + 1.606 + for my $file (keys %files) { 1.607 + if (!defined $Files_Stat{$file}) { 1.608 + push @{$$cl->{new_files}}, $file; 1.609 + } 1.610 + $Files_Stat{$file}++; 1.611 + } 1.612 + } 1.613 + } 1.614 + 1.615 +} 1.616 + 1.617 + 1.618 +=cut 1.619 +Вывести результат обработки журнала. 1.620 +=cut 1.621 + 1.622 + 1.623 +sub print_command_lines 1.624 +{ 1.625 + my $output_filename=$_[0]; 1.626 + open(OUT, ">", $output_filename) 1.627 + or die "Can't open $output_filename for writing\n"; 1.628 + 1.629 + 1.630 + 1.631 + print OUT "<livelablog>\n"; 1.632 + 1.633 + my $cl; 1.634 + my $in_range=0; 1.635 + for my $i (@Command_Lines_Index) { 1.636 + $cl = $Command_Lines[$i]; 1.637 + 1.638 + if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { 1.639 + $in_range=1; 1.640 + next; 1.641 + } 1.642 + if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { 1.643 + $in_range=0; 1.644 + next; 1.645 + } 1.646 + next if ($Config{"from"} && $Config{"to"} && !$in_range) 1.647 + || 1.648 + ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) 1.649 + || 1.650 + ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) 1.651 + || 1.652 + ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); 1.653 + 1.654 + my @new_commands=@{$cl->{"new_commands"}}; 1.655 + my @new_files=@{$cl->{"new_files"}}; 1.656 + 1.657 + my $cl_class="cline"; 1.658 + my $out_class="output"; 1.659 + if ($cl->{"class"}) { 1.660 + $cl_class = $cl->{"class"}."_".$cl_class; 1.661 + $out_class = $cl->{"class"}."_".$out_class; 1.662 + } 1.663 + 1.664 + # Вырезаем из вывода только нужное количество строк 1.665 + 1.666 + my $output=""; 1.667 + if ($Config{"head_lines"} || $Config{"tail_lines"}) { 1.668 + # Partialy output 1.669 + my @lines = split '\n', $cl->{"output"}; 1.670 + # head 1.671 + my $mark=1; 1.672 + for (my $i=0; $i<= $#lines && $i < $Config{"cache_head_lines"}; $i++) { 1.673 + $output .= $lines[$i]."\n"; 1.674 + } 1.675 + # tail 1.676 + my $start=$#lines-$Config{"cache_tail_lines"}+1; 1.677 + if ($start < 0) { 1.678 + $start=0; 1.679 + $mark=0; 1.680 + } 1.681 + if ($start < $Config{"cache_head_lines"}) { 1.682 + $start=$Config{"cache_head_lines"}; 1.683 + $mark=0; 1.684 + } 1.685 + $output .= $Config{"skip_text"}."\n" if $mark; 1.686 + for (my $i=$start; $i<= $#lines; $i++) { 1.687 + $output .= $lines[$i]."\n"; 1.688 + } 1.689 + } 1.690 + else { 1.691 + # Full output 1.692 + $output .= $cl->{"output"}; 1.693 + } 1.694 + $output .= "^C\n" if ($cl->{"err"} eq "130"); 1.695 + 1.696 + 1.697 + # Совместимость с labmaker 1.698 + 1.699 + # Переводим в секунды Эпохи 1.700 + # В labmaker'е данные хранились в неудобной форме: hour, min, sec, day of year 1.701 + # Информация о годе отсутствовала 1.702 + # Её можно внести: 1.703 + # Декабрь 2004 год; остальные -- 2005 год. 1.704 + 1.705 + my $year = 2005; 1.706 + $year = 2004 if ( $cl->{day} > 330 ); 1.707 + # timelocal( $sec, $min, $hour, $mday,$mon,$year); 1.708 + $cl->{time} = timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year); 1.709 + 1.710 + 1.711 + # Начинаем вывод команды 1.712 + print OUT "<command>\n"; 1.713 + print OUT "<time>",$cl->{time},"</time>\n"; 1.714 + print OUT "<tty>",$cl->{tty},"</tty>\n"; 1.715 + print OUT "<out_class>",$out_class,"</out_class>\n"; 1.716 + print OUT "<prompt>"; 1.717 + printq(\*OUT,,$cl->{"prompt"}); 1.718 + print OUT "</prompt>"; 1.719 + print OUT "<cline>"; 1.720 + printq(\*OUT,$cl->{"cline"}); 1.721 + print OUT "</cline>\n"; 1.722 + print OUT "<last_command>",$cl->{"last_command"},"</last_command>\n"; 1.723 + if (@new_commands) { 1.724 + print OUT "<new_commands>"; 1.725 + printq(\*OUT, join (" ", @new_commands)); 1.726 + print OUT "</new_commands>"; 1.727 + } 1.728 + if (@new_files) { 1.729 + print OUT "<new_files>"; 1.730 + printq(\*OUT, join (" ", @new_files)); 1.731 + print OUT "</new_files>"; 1.732 + } 1.733 + print OUT "<output>"; 1.734 + printq(\*OUT,$output); 1.735 + print OUT "</output>\n"; 1.736 + if ($cl->{"diff"}) { 1.737 + print OUT "<diff>"; 1.738 + printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); 1.739 + print OUT "</diff>\n"; 1.740 + } 1.741 + print OUT "</command>\n"; 1.742 + 1.743 + } 1.744 + 1.745 + print OUT "</livelablog>\n"; 1.746 + close(OUT); 1.747 +} 1.748 + 1.749 + 1.750 +=cut 1.751 +sub print_command_lines2 1.752 +{ 1.753 + my $output_filename=$_[0]; 1.754 + open(OUT, ">", $output_filename) 1.755 + or die "Can't open $output_filename for writing\n"; 1.756 + 1.757 + 1.758 + print OUT <<OUT; 1.759 +<log> 1.760 +OUT 1.761 + 1.762 + my $cl; 1.763 + for my $i (@Command_Lines_Index) { 1.764 + 1.765 + 1.766 + $cl = $Command_Lines[$i]; 1.767 + 1.768 + 1.769 +# Printing out 1.770 + print OUT <<OUT; 1.771 + <command> 1.772 + <day>$cl->{day}</day> 1.773 + <hour>$cl->{hour}</hour> 1.774 + <min>$cl->{min}</min> 1.775 + <sec>$cl->{sec}</sec> 1.776 + <tty>$cl->{tty}</tty> 1.777 + <uid>$cl->{uid}</uid> 1.778 + <euid>$cl->{euid}</euid> 1.779 + <prompt>$cl->{prompt}</prompt> 1.780 + <cline>$cl->{cline}</cline> 1.781 + <status>$cl->{err}</cline> 1.782 + <output> 1.783 +$cl->{output}</output> 1.784 + </command> 1.785 +OUT 1.786 + } 1.787 + 1.788 + for my $diff (@Diffs) { 1.789 + 1.790 + print OUT <<OUT; 1.791 + <diff> 1.792 + <path>$diff->{path}</path> 1.793 + <uid>$diff->{uid}</uid> 1.794 + <day>$diff->{day}</day> 1.795 + <hour>$diff->{hour}</hour> 1.796 + <min>$diff->{min}</min> 1.797 + <sec>$diff->{sec}</sec> 1.798 + <text> 1.799 +$diff->{text}</text> 1.800 + </diff> 1.801 +OUT 1.802 + } 1.803 + 1.804 + print OUT <<OUT; 1.805 +</log> 1.806 +OUT 1.807 +} 1.808 +=cut 1.809 + 1.810 +main(); 1.811 + 1.812 +sub main 1.813 +{ 1.814 +$| = 1; 1.815 + 1.816 +init_variables(); 1.817 +init_config(); 1.818 + 1.819 +for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) { 1.820 + load_diff_files($lab_log); 1.821 +} 1.822 + 1.823 +load_command_lines($Config{"input"}, $Config{"input_mask"}); 1.824 +sort_command_lines; 1.825 +process_command_lines; 1.826 +print_command_lines($Config{"cache"}); 1.827 + 1.828 +} 1.829 + 1.830 +sub init_variables 1.831 +{ 1.832 +}