lilalo
changeset 23:6d93c5f1d0e5 v_0_2_2
Выполнен шаг (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 | 22c617db6172 |
children | 4b86595adb4b |
files | l3-agent l3-frontend l3-report l3config.pm lm lm-report |
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 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/l3-frontend Wed Nov 02 19:16:11 2005 +0200 2.3 @@ -0,0 +1,493 @@ 2.4 +#!/usr/bin/perl -w 2.5 + 2.6 +use lib '.'; 2.7 +use l3config; 2.8 + 2.9 +our @Command_Lines; 2.10 + 2.11 +# vvv Инициализация переменных выполняется процедурой init_variables 2.12 +our @Day_Name; 2.13 +our @Month_Name; 2.14 +our @Of_Month_Name; 2.15 +our %Search_Machines; 2.16 +our %Elements_Visibility; 2.17 +# ^^^ 2.18 + 2.19 +sub search_buy; 2.20 +sub make_comment; 2.21 +sub load_command_lines_from_xml; 2.22 +sub print_command_lines; 2.23 +sub init_variables; 2.24 +sub main; 2.25 + 2.26 +main(); 2.27 + 2.28 +sub main 2.29 +{ 2.30 + $| = 1; 2.31 + 2.32 + init_variables(); 2.33 + init_config(); 2.34 + 2.35 + load_command_lines_from_xml($Config{"cache"}); 2.36 + print_command_lines($Config{"output"}); 2.37 +} 2.38 + 2.39 + 2.40 +sub search_by 2.41 +{ 2.42 + my $sm = shift; 2.43 + my $topic = shift; 2.44 + $topic =~ s/ /+/; 2.45 + 2.46 + return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='". 2.47 + $Search_Machines{$sm}->{"icon"}."' border='0'/></a>"; 2.48 +} 2.49 + 2.50 +sub make_comment 2.51 +{ 2.52 + my $commands = $_[0]; 2.53 + my $files = $_[1]; 2.54 + chomp $commands; 2.55 + chomp $files; 2.56 + return if (!$commands && !$files); 2.57 + 2.58 + my $comment=""; 2.59 + 2.60 + # Commands 2.61 + for my $command (split /\s+/,$commands) { 2.62 + $command =~ s/'//g; 2.63 + my $description=""; 2.64 + eval { $description=`mywi-client '$command'`; } ; 2.65 + $description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description))); 2.66 + $description =~ s/.*?-//; 2.67 + next if $description =~ /^\s*$/; 2.68 + 2.69 + my $query=$command." ".$Config{"keywords"}; 2.70 + $query =~ s/\ /+/g; 2.71 + my $search= search_by("opennet",$query). 2.72 + search_by("local",$command). 2.73 + search_by("google",$query); 2.74 + 2.75 + $comment .= "<tr><td class='note_title'>$command</td>". 2.76 + "<td class='note_search'>$search</td>". 2.77 + "</tr><tr><td width='100%' colspan='2' class='note_text'>". 2.78 + "$description</td></tr><tr/>"; 2.79 + } 2.80 + 2.81 + # Files 2.82 + for my $file (split /\s+/,$files) { 2.83 + $file =~ s@.*/@@; 2.84 + $file =~ s/'//g; 2.85 + next if $file =~ /^\s*$/; 2.86 + next if $file =~ /^-/; 2.87 + 2.88 + my $description=`mywi '$file'`; 2.89 + $description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description))); 2.90 + next if $description =~ /^\s*$/; 2.91 + 2.92 + my $query=$file." ".$Config{"files_keywords"}; 2.93 + $query =~ s/\ /+/g; 2.94 + my $search= search_by("opennet",$query). 2.95 + search_by("local",$file). 2.96 + search_by("google",$query); 2.97 + 2.98 + $comment .= "<tr><td class='note_title'>$file</td>". 2.99 + "<td class='note_search'>$search</td>". 2.100 + "</tr><tr><td width='100%' colspan='2' class='note_text'>". 2.101 + "$description</td></tr><tr/>"; 2.102 + } 2.103 + 2.104 + 2.105 + return $comment; 2.106 +} 2.107 + 2.108 +=cut 2.109 +Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта 2.110 +из XML-документа в переменную @Command_Lines 2.111 + 2.112 +Предупреждение! 2.113 +Процедура не в состоянии обрабатывать XML-документ любой структуры. 2.114 +В действительности файл cache из которого загружаются данные 2.115 +просто напоминает XML с виду. 2.116 +=cut 2.117 +sub load_command_lines_from_xml 2.118 +{ 2.119 + my $datafile = $_[0]; 2.120 + 2.121 + open (CLASS, $datafile) 2.122 + or die "Can't open file of the class ",$datafile,"\n"; 2.123 + local $/; 2.124 + $data = <CLASS>; 2.125 + close(CLASS); 2.126 + 2.127 + for $command ($data =~ m@<command>(.*?)</command>@sg) { 2.128 + my %cl; 2.129 + while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) { 2.130 + $cl{$1} = $2; 2.131 + } 2.132 + push @Command_Lines, \%cl; 2.133 + } 2.134 +} 2.135 + 2.136 +=cut 2.137 +Процедура print_command_lines выводит HTML-представление 2.138 +разобранного lab-скрипта. 2.139 + 2.140 +Разобранный lab-скрипт должен находиться в массиве @Command_Lines 2.141 +=cut 2.142 + 2.143 +sub print_command_lines 2.144 +{ 2.145 + my $output_filename=$_[0]; 2.146 + 2.147 + my $course_name = $Config{"course-name"}; 2.148 + my $course_code = $Config{"course-code"}; 2.149 + my $course_date = $Config{"course-date"}; 2.150 + my $course_center = $Config{"course-center"}; 2.151 + my $course_trainer = $Config{"course-trainer"}; 2.152 + my $course_student = $Config{"course-student"}; 2.153 + 2.154 + 2.155 + # Результат выполнения процедуры равен 2.156 + # join("", @Result{header,body,stat,help,about,footer}) 2.157 + my %Result; 2.158 + my $toc =""; # Хранит оглавление по дням 2.159 + 2.160 + $Result{"body"} = "<table width='100%'>\n"; 2.161 + 2.162 + my $cl; 2.163 + my $last_tty=""; 2.164 + my $last_day=""; 2.165 + my $in_range=0; 2.166 + 2.167 + for my $cl (@Command_Lines) { 2.168 + 2.169 + if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { 2.170 + $in_range=1; 2.171 + next; 2.172 + } 2.173 + if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { 2.174 + $in_range=0; 2.175 + next; 2.176 + } 2.177 + next if ($Config{"from"} && $Config{"to"} && !$in_range) 2.178 + || 2.179 + ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) 2.180 + || 2.181 + ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) 2.182 + || 2.183 + ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); 2.184 + 2.185 + #my @new_commands=@{$cl->{"new_commands"}}; 2.186 + #my @new_files=@{$cl->{"new_files"}}; 2.187 + 2.188 + my $cl_class="cline"; 2.189 + my $out_class="output"; 2.190 + if ($cl->{"class"}) { 2.191 + $cl_class = $cl->{"class"}."_".$cl_class; 2.192 + $out_class = $cl->{"class"}."_".$out_class; 2.193 + } 2.194 + 2.195 + my @new_commands; 2.196 + my @new_files; 2.197 + @new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"}; 2.198 + @new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"}; 2.199 + 2.200 + my $output=""; 2.201 + if ($Config{"head_lines"} || $Config{"tail_lines"}) { 2.202 + # Partialy output 2.203 + my @lines = split '\n', $cl->{"output"}; 2.204 + # head 2.205 + my $mark=1; 2.206 + for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) { 2.207 + $output .= $lines[$i]."\n"; 2.208 + } 2.209 + # tail 2.210 + my $start=$#lines-$Config{"tail_lines"}+1; 2.211 + if ($start < 0) { 2.212 + $start=0; 2.213 + $mark=0; 2.214 + } 2.215 + if ($start < $Config{"head_lines"}) { 2.216 + $start=$Config{"head_lines"}; 2.217 + $mark=0; 2.218 + } 2.219 + $output .= $Config{"skip_text"}."\n" if $mark; 2.220 + for (my $i=$start; $i<= $#lines; $i++) { 2.221 + $output .= $lines[$i]."\n"; 2.222 + } 2.223 + } 2.224 + else { 2.225 + # Full output 2.226 + $output .= $cl->{"output"}; 2.227 + } 2.228 + #$output .= "^C\n" if ($cl->{"err"} eq "130"); 2.229 + 2.230 + # 2.231 + ## 2.232 + ## Начинается собственно вывод 2.233 + ## 2.234 + # 2.235 + 2.236 + # <command> 2.237 + 2.238 + my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time}); 2.239 + # Добавляем спереди 0 для удобочитаемости 2.240 + $min = "0".$min if $min =~ /^.$/; 2.241 + $hour = "0".$hour if $hour =~ /^.$/; 2.242 + $sec = "0".$sec if $sec =~ /^.$/; 2.243 + 2.244 + $class=$cl->{"out_class"}; 2.245 + $class =~ s/output$//; 2.246 + 2.247 + 2.248 + $Result{"body"} .= "<tr class='command'>\n"; 2.249 + 2.250 + 2.251 + # DAY CHANGE 2.252 + if ( $last_day ne $day) { 2.253 + #$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>"; 2.254 + $Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>"; 2.255 + $toc .= "<li><a href='#day$day'>".$Day_Name[$wday]."</a></li>\n"; 2.256 + $last_day=$day; 2.257 + } 2.258 + 2.259 + # CONSOLE CHANGE 2.260 + if ( $last_tty ne $cl->{"tty"}) { 2.261 + $Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><td/></tr></table></td></tr><tr>"; 2.262 + $last_tty=$cl->{"tty"}; 2.263 + } 2.264 + 2.265 + # TIME 2.266 + if ($Config{"show_time"} =~ /^y/i) { 2.267 + $Result{"body"} .= "<td valign='top' class='time' width='$Config{time_width}'><pre>". 2.268 + $hour. ":". $min. ":". $sec. 2.269 + "</td>"; 2.270 + } else { 2.271 + $Result{"body"} .= "<td width='0'/>" 2.272 + } 2.273 + 2.274 + # COMMAND 2.275 + $Result{"body"} .= "<td class='script'>\n"; 2.276 + $Result{"body"} .= "<pre class='${class}cline'>\n"; 2.277 + my $cline = $cl->{"cline"}; 2.278 + $cline =~ s/\n//; 2.279 + $Result{"body"} .= $cl->{"prompt"}.$cl->{"cline"}; 2.280 + $Result{"body"} .= "</pre>\n"; 2.281 + 2.282 + my $last_command = $cl->{"last_command"}; 2.283 + if (!( 2.284 + $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) || 2.285 + $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) || 2.286 + $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}}) 2.287 + )) { 2.288 + 2.289 + $Result{"body"} .= "<pre class='".$cl->{out_class}."'>"; 2.290 + $Result{"body"} .= $output; 2.291 + $Result{"body"} .= "</pre>\n"; 2.292 + } 2.293 + 2.294 + # DIFF 2.295 + if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) { 2.296 + $Result{"body"} .= "<table><tr><td width='5'/><td class='diff'><pre>"; 2.297 + $Result{"body"} .= $cl->{"diff"}; 2.298 + $Result{"body"} .= "</pre></td></tr></table>"; 2.299 + } 2.300 + 2.301 + # COMMENT 2.302 + if ( $Config{"show_comments"} =~ /^y/i) { 2.303 + my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files)); 2.304 + if ($comment) { 2.305 + $Result{"body"} .= "<table width='$Config{comment_width}'>". 2.306 + "<tr><td width='5'/><td>"; 2.307 + $Result{"body"} .= "<table class='note' width='100%'>"; 2.308 + $Result{"body"} .= $comment; 2.309 + $Result{"body"} .= "</table>\n"; 2.310 + $Result{"body"} .= "</td></tr></table>"; 2.311 + } 2.312 + else { 2.313 + $Result{"body"} .= "<table width='$Config{comment_width}'>". 2.314 + "<tr><td width='5'/><td>"; 2.315 + $Result{"body"} .= "<table class='note' width='100%'>"; 2.316 + $Result{"body"} .= "commands ".join(" ",@new_commands)."<br/>"; 2.317 + $Result{"body"} .= "files ".join(" ",@new_files)."<br/>"; 2.318 + $Result{"body"} .= "</table>\n"; 2.319 + $Result{"body"} .= "</td></tr></table>"; 2.320 + } 2.321 + } 2.322 + 2.323 + # Вывод очередной команды окончен 2.324 + $Result{"body"} .= "</td>\n"; 2.325 + $Result{"body"} .= "</tr>\n"; 2.326 + } 2.327 + 2.328 + $Result{"body"} .= "</table>\n"; 2.329 + 2.330 + $Result{"stat"} = "<hr/>"; 2.331 + $Result{"stat"} .= "<h2 id='stat'>Статистика</h2>"; 2.332 + $Result{"stat"} .= "Статистическая информация о журнале<br/>"; 2.333 + $Result{"help"} .= "<hr/>"; 2.334 + $Result{"help"} .= "<h2 id='help'>Справка</h2>"; 2.335 + $Result{"help"} .= "$Html_Help<br/>"; 2.336 + $Result{"about"} .= "<hr/>"; 2.337 + $Result{"about"} .= "<h2 id='about'>О программе</h2>"; 2.338 + $Result{"about"} .= "$Html_About"; 2.339 + $Result{"footer"} .= "</body>\n"; 2.340 + $Result{"footer"} .= "</html>\n"; 2.341 + 2.342 + # Заголовок генерируется позже всего 2.343 + # Тогда, когда известно уже, что должно быть написано в 2.344 + # оглавлении 2.345 + $Result{"header"} = <<HEADER; 2.346 + <html> 2.347 + <head> 2.348 + <meta content='text/html; charset=utf-8' http-equiv='Content-Type' /> 2.349 + <link rel='stylesheet' href='labmaker.css' type='text/css'/> 2.350 + </head> 2.351 + <body> 2.352 + <script> 2.353 + $Html_JavaScript 2.354 + </script> 2.355 + <h2>Журнал лабораторных работ</h2> 2.356 + 2.357 + <p> 2.358 + Выполнил $course_student<br/> 2.359 + Проверил $course_trainer <br/> 2.360 + Курс $course_name ($course_code), 2.361 + $course_date<br/> 2.362 + Учебный центр $course_center <br/> 2.363 + </p> 2.364 + 2.365 + <ul> 2.366 + <li><a href='#log'>Журнал</a></li> 2.367 + <ul>$toc</ul> 2.368 + <li><a href='#stat'>Статистика</a></li> 2.369 + <li><a href='#help'>Справка</a></li> 2.370 + <li><a href='#about'>О программе</a></li> 2.371 + </ul> 2.372 + 2.373 + <h2 id="log">Журнал</h2> 2.374 +HEADER 2.375 + $Result{"header"} .= "<table class='visibility_form'><tr><td><form>\n"; 2.376 + for my $element (keys %Elements_Visibility) 2.377 + { 2.378 + my @e = split /\s+/, $element; 2.379 + my $showhide = join "", map { "ShowHide('$_');" } @e ; 2.380 + $Result{"header"} .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>". 2.381 + $Elements_Visibility{$element}. 2.382 + "</input><br>\n"; 2.383 + } 2.384 + 2.385 + $Result{"header"} .= "</form></td></tr></table>\n"; 2.386 + 2.387 + open(OUT, ">", $output_filename) 2.388 + or die "Can't open $output_filename for writing\n"; 2.389 + print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"}; 2.390 + close(OUT); 2.391 +} 2.392 + 2.393 + 2.394 + 2.395 + 2.396 + 2.397 + 2.398 +sub init_variables 2.399 +{ 2.400 +$Html_Help = <<HELP; 2.401 + Справка по использованию журнала 2.402 +HELP 2.403 + 2.404 +$Html_About = <<ABOUT; 2.405 + <p> 2.406 + LiLaLo (L3) расшифровывается как Live Lab Log.<br/> 2.407 + Программа разработана для повышения эффективности обучения<br/> 2.408 + Unix/Linux-системам.<br/> 2.409 + (c) Игорь Чубин, 2004-2005<br/> 2.410 + </p> 2.411 +ABOUT 2.412 +$Html_About.='$Id$ </p>'; 2.413 + 2.414 +$Html_JavaScript = <<JS; 2.415 + function getElementsByClassName(Class_Name) 2.416 + { 2.417 + var Result=new Array(); 2.418 + var All_Elements=document.all || document.getElementsByTagName('*'); 2.419 + for (i=0; i<All_Elements.length; i++) 2.420 + if (All_Elements[i].className==Class_Name) 2.421 + Result.push(All_Elements[i]); 2.422 + return Result; 2.423 + } 2.424 + function ShowHide (name) 2.425 + { 2.426 + elements=getElementsByClassName(name); 2.427 + for(i=0; i<elements.length; i++) 2.428 + if (elements[i].style.display == "none") 2.429 + elements[i].style.display = ""; 2.430 + else 2.431 + elements[i].style.display = "none"; 2.432 + //if (elements[i].style.visibility == "hidden") 2.433 + // elements[i].style.visibility = "visible"; 2.434 + //else 2.435 + // elements[i].style.visibility = "hidden"; 2.436 + } 2.437 + function filter_by_output(text) 2.438 + { 2.439 + 2.440 + var jjj=0; 2.441 + 2.442 + elements=getElementsByClassName('command'); 2.443 + for(i=0; i<elements.length; i++) { 2.444 + subelems = elements[i].getElementsByTagName('pre'); 2.445 + for(j=0; j<subelems.length; j++) { 2.446 + if (subelems[j].className = 'output') { 2.447 + var str = new String(subelems[j].nodeValue); 2.448 + if (jjj != 1) { 2.449 + alert(str); 2.450 + jjj=1; 2.451 + } 2.452 + if (str.indexOf(text) >0) 2.453 + subelems[j].style.display = "none"; 2.454 + else 2.455 + subelems[j].style.display = ""; 2.456 + 2.457 + } 2.458 + 2.459 + } 2.460 + } 2.461 + 2.462 + } 2.463 +JS 2.464 + 2.465 +%Search_Machines = ( 2.466 + "google" => { "query" => "http://www.google.com/search?q=" , 2.467 + "icon" => "google.ico" }, 2.468 + "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", 2.469 + "icon" => "freebsd.ico" }, 2.470 + "linux" => { "query" => "http://man.he.net/?topic=", 2.471 + "icon" => "linux.ico"}, 2.472 + "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=", 2.473 + "icon" => "opennet.ico"}, 2.474 + "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", 2.475 + "icon" => "freebsd.ico" }, 2.476 + 2.477 + ); 2.478 + 2.479 +%Elements_Visibility = ( 2.480 + "note" => "замечания", 2.481 + "diff" => "редактор", 2.482 + "time" => "время", 2.483 + "ttychange" => "терминал", 2.484 + "wrong_output wrong_cline wrong_root_output wrong_root_cline" 2.485 + => "команды с ошибками", 2.486 + "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" 2.487 + => "прерванные команды", 2.488 + "tab_completion_output tab_completion_cline" 2.489 + => "продолжение с помощью tab" 2.490 +); 2.491 + 2.492 +@Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /; 2.493 +@Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /; 2.494 +@Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /; 2.495 +} 2.496 +
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/l3-report Wed Nov 02 19:16:11 2005 +0200 3.3 @@ -0,0 +1,5 @@ 3.4 +#!/bin/sh 3.5 + 3.6 +./l3-agent "$@" 3.7 +./l3-frontend "$@" 3.8 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/l3config.pm Wed Nov 02 19:16:11 2005 +0200 4.3 @@ -0,0 +1,92 @@ 4.4 +package l3config; 4.5 + 4.6 +use Exporter; 4.7 +use Getopt::Long; 4.8 + 4.9 +@ISA = ('Exporter'); 4.10 +@EXPORT = qw(%Config &init_config); 4.11 + 4.12 +our $Config_File = "labmaker.conf"; 4.13 +our %Config = ( 4.14 + "skip_empty" => "yes", 4.15 + "skip_interrupted" => "no", 4.16 + "skip_wrong" => "no", 4.17 + "editors" => ["vi", "pico", "ee", "vim"], 4.18 + "pagers" => ["more", "less", "zmore", "zless", "info", 4.19 + "man", "mc", "trafshow", "screen", "cfdisk", 4.20 + "trafshow-bsd", "yes", "lynx", "links", "centericq" 4.21 + ], 4.22 + "terminal" => ["mc"], 4.23 + "suppress_editors" => "yes", 4.24 + "suppress_pagers" => "yes", 4.25 + "suppress_terminal" => "yes", 4.26 + 4.27 + "terminal_width" => 100, 4.28 + "terminal_height" => 100, 4.29 + "verbose" => "yes", 4.30 + 4.31 + "head_lines" => 5, 4.32 + "tail_lines" => 5, 4.33 + "cache_head_lines" => 5, 4.34 + "cache_tail_lines" => 5, 4.35 + "skip_text" => "...", 4.36 + "show_time" => "yes", 4.37 + "show_diffs" => "yes", 4.38 + "show_comments" => "yes", 4.39 + 4.40 + "input" => "/root/.labmaker", 4.41 + "diffs" => "", 4.42 + "input_mask" => "*.script", 4.43 + "encoding" => "utf-8", 4.44 + 4.45 + "cache" => "/tmp/report.xml", 4.46 + 4.47 + "output" => "/tmp/report.html", 4.48 + #"output" => "report.xml", 4.49 + "output_mask" => "INDEX", 4.50 + "output_format" => "html", 4.51 + 4.52 + "signature" => "#lm:", 4.53 + "from" => "", 4.54 + "to" => "", 4.55 + "lab" => "", 4.56 + "keywords" => "linux command", 4.57 + "files_keywords" => "linux file", 4.58 + 4.59 + comment_width => "300", 4.60 + time_width => "60", 4.61 + 4.62 + "course-name" => "", 4.63 + "course-code" => "", 4.64 + "course-date" => "", 4.65 + "course-center" => "", 4.66 + "course-trainer" => "", 4.67 + "course-student" => "", 4.68 + ); 4.69 + 4.70 +sub read_config_file 4.71 +{ 4.72 + my $config = $_[0]; 4.73 + my $filename = $_[1]; 4.74 + open(CONFIG, "$filename") 4.75 + or return; 4.76 + while (<CONFIG>) { 4.77 + s/#.*//; 4.78 + next if /^\s*$/; 4.79 + my ($var, $val) = split /\s*=\s*/, $_, 2; 4.80 + $var =~ s/\s*//; 4.81 + $config->{$var} = $val; 4.82 + } 4.83 + close(CONFIG); 4.84 +} 4.85 + 4.86 + 4.87 +sub init_config 4.88 +{ 4.89 + my %argv_config; 4.90 + my %file_config; 4.91 + read_config_file(\%file_config, $Config_File); 4.92 + GetOptions(\%argv_config, map "$_=s", keys %Config); 4.93 + %Config = (%Config, %file_config, %argv_config); 4.94 +} 4.95 +
5.1 --- a/lm Tue Nov 01 14:32:30 2005 +0200 5.2 +++ b/lm Wed Nov 02 19:16:11 2005 +0200 5.3 @@ -21,7 +21,8 @@ 5.4 "show_host" => "no", 5.5 5.6 # Вспомогательные программы 5.7 - "l3-report" => "./lm-report", 5.8 + #"l3-report" => "./lm-report", 5.9 + "l3-report" => "./l3-report", 5.10 5.11 # Каталоги 5.12 "path_lilalo" => "/var/lilalo/",
6.1 --- a/lm-report Tue Nov 01 14:32:30 2005 +0200 6.2 +++ b/lm-report Wed Nov 02 19:16:11 2005 +0200 6.3 @@ -1184,19 +1184,3 @@ 6.4 } 6.5 6.6 6.7 -sub init_variables 6.8 -{ 6.9 -$Html_Help = <<HELP; 6.10 - Справка по использованию журнала 6.11 -HELP 6.12 - 6.13 -$Html_About = <<ABOUT; 6.14 - <p> 6.15 - LiLaLo (L3) расшифровывается как Live Lab Log.<br/> 6.16 - Программа разработана для повышения эффективности обучения<br/> 6.17 - Unix/Linux-системам.<br/> 6.18 - (c) Игорь Чубин, 2004-2005<br/> 6.19 - </p> 6.20 -ABOUT 6.21 -$Html_About.='$Id$ </p>'; 6.22 -}