# HG changeset patch # User devi # Date 1130951771 -7200 # Node ID 6d93c5f1d0e5b236cc6a3ac601c1d579ed8158a0 # Parent 22c617db6172946dfd112d1c2bd2f51ae1a8546d Выполнен шаг (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. diff -r 22c617db6172 -r 6d93c5f1d0e5 l3-agent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/l3-agent Wed Nov 02 19:16:11 2005 +0200 @@ -0,0 +1,829 @@ +#!/usr/bin/perl -w + +# +# (c) Igor Chubin, imchubin@mail.ru, 2004-2005 +# + +use strict; +use Term::VT102; +use Text::Iconv; +use Data::Dumper; +use Time::Local 'timelocal_nocheck'; + +use lib "."; +use l3config; + + +our @Command_Lines; +our @Command_Lines_Index; +our @Diffs; + +our %Commands_Stat; # Statistics about commands usage +our %Files_Stat; # Statistics about commands usage + + +sub init_variables; +sub main; + +sub load_diff_files; +sub bind_diff; +sub extract_from_cline; +sub load_command_lines; +sub sort_command_lines; +sub process_command_lines; +sub print_command_lines; +sub printq; + +sub load_diff_files +{ + my @pathes = @_; + + for my $path (@pathes) { + my $template = "*.diff"; + my @files = <$path/$template>; + my $i=0; + for my $file (@files) { + my %diff; + + $diff{"path"}=$path; + $diff{"uid"}="SET THIS"; + +# Сейчас UID определяется из названия каталога +# откуда берутся diff-файлы +# Это неправильно +# +# ВАРИАНТ: +# К файлам жураналам должны прилагаться ситемны файлы, +# мз которых и будет определяться соответствие +# имён пользователей их uid'ам +# + $diff{"uid"} = 0 if $path =~ m@/root/@; + + $diff{"bind_to"}=""; + $diff{"time_range"}=-1; + + next if not $file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@; + $diff{"day"}=$1 || ""; + $diff{"hour"}=$2; + $diff{"min"}=$3; + $diff{"sec"}=$4 || 0; + + $diff{"index"}=$i; + + print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n"; + + local $/; + open (F, "$file") + or return "Can't open file $file ($_[0]) for reading"; + my $text = ; + if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) { + my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8"); + $text = $converter->convert($text); + } + close(F); + $diff{"text"}=$text; + #print "$file loaded ($diff{day})\n"; + + push @Diffs, \%diff; + $i++; + } + } +} + + +sub bind_diff +{ +# my $path = shift; +# my $pid = shift; +# my $day = shift; +# my $lab = shift; + + print "Trying to bind diff...\n"; + + my $cl = shift; + my $hour = $cl->{"hour"}; + my $min = $cl->{"min"}; + my $sec = $cl->{"sec"}; + + my $min_dt = 10000; + + for my $diff (@Diffs) { + # Check here date, time and user + next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"})); + #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"}); + + my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec); + if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) { + print "Approppriate diff found: dt=$dt\n"; + if ($diff->{"bind_to"}) { + undef $diff->{"bind_to"}->{"diff"}; + }; + $diff->{"time_range"}=$dt; + $diff->{"bind_to"}=$cl; + + $cl->{"diff"} = $diff->{"index"}; + $min_dt = $dt; + } + + } +} + + +sub extract_from_cline +# Разобрать командную строку $_[1] и возвратить хэш, содержащий +# номер первого появление команды в строке: +# команда => первая позиция +{ + my $what = $_[0]; + my $cline = $_[1]; + my @lists = split /\;/, $cline; + + + my @commands = (); + for my $list (@lists) { + push @commands, split /\|/, $list; + } + + my %commands; + my %files; + my $i=0; + for my $command (@commands) { + $command =~ /\s*(\S+)\s*(.*)/; + if ($1 && $1 eq "sudo" ) { + $commands{"$1"}=$i++; + $command =~ s/\s*sudo\s+//; + } + $command =~ /\s*(\S+)\s*(.*)/; + if ($1 && !defined $commands{"$1"}) { + $commands{"$1"}=$i++; + }; + if ($2) { + my $args = $2; + my @args = split (/\s+/, $args); + for my $a (@args) { + $files{"$a"}=$i++ + if !defined $files{"$a"}; + }; + + + } + } + + if ($what eq "commands") { + return %commands; + } else { + return %files; + } + +} + +sub load_command_lines +{ + my $lab_scripts_path = $_[0]; + my $lab_scripts_mask = $_[1]; + + my $cline_re_base = qq' + (?:\\^?([0-9]*C?)) # exitcode + (?:_([0-9]+)_)? # uid + (?:_([0-9]+)_) # pid + (...?) # day + (.?.?) # lab + \\s # space separator + ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time + .\\[50D.\\[K # killing symbols + (.*?([\$\#]\\s?)) # prompt + (.*) # command line + '; + #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x; + #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; + my $cline_re = qr/$cline_re_base/sx; + my $cline_re1 = qr/$cline_re_base\x0D/sx; + my $cline_re2 = qr/$cline_re_base$/sx; + + my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"}, + 'rows' => $Config{"terminal_height"}); + my $cline_vt = Term::VT102->new ('cols' => $Config{"terminal_width"}, + 'rows' => $Config{"terminal_height"}); + + my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8") + if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i); + + print "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/; + + my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>; + my $file; + my $files_number = $#lab_scripts; + my $ii = 0; + my $skip_info; + + my $commandlines_loaded =0; + my $commandlines_processed =0; + + for $file (@lab_scripts){ + #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/; + + open (FILE, "$file"); + binmode FILE; + $file =~ m@.*/(.*?)-.*@; + + my $tty = $1; + my $first_pass = 1; + my %cl; + my $last_output_length=0; + while () { + $commandlines_processed++; + # time + + if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) { + s/.*\x0d(?!\x0a)//; + # print "!!!",$_,"!!!\n"; + # next; + # while (m/$cline_re1/gs) { + # } + m/$cline_re2/gs; + + $commandlines_loaded++; + $last_output_length=0; + + # Previous command + my %last_cl = %cl; + my $err = $1 || ""; + + +=cut + +ТАБЛИЦА КОМАНД + + uid + Идентификатор пользователя + + tty + Идентификатор терминала, на котором была вызвана команда + + pid + PID-процесса командного интерпретатора, + в котором была вызвана команда + + lab + лабораторная работа, к которой относится команда. + Идентификатор текущей лабораторной работы + хранится в файле ~/.labmaker/lab + + pwd (!) + текущий каталог, из которого была вызвана команда + + day + время вызова, день + В действительности здесь хранится не время вызова команды, + а с момента появления приглашения командного интерпретатора + для ввода команды + + + hour + время вызова, час + + min + время вызова, минута + + sec + время вызова, секунда + + time (!) + время вызова команды в Unix-формате. + Предпочтительнее использовать этот формат чем hour:min:sec, + использовавшийся в Labmaker + + fullprompt + Приглашение командной строки + + prompt + Сокращённое приглашение командной строки + + cline + Командная строка + + output + Результат выполнения команды + + diff + Указатель на ассоциированный с командой diff + + note (!) + Текстовый комментарий к команде. + Может генерироваться из самого лога с помощью команд + #^ Комментарий + #v Комментарий + в том случае, если для комментирования достаточно одной строки, + или с помощью команд + cat > /dev/null #^ Заголовок + Текст + ^D + в том случае, если комментарий развёрнутый. + В последнем случае комментарий может содержать + заголовок, абзацы и несложное форматирование. + + Символ ^ или v после знака комментария # обозначает, + к какой команде относится комментарий: + к предыдущей (^) или последующей (v) + + err + Код завершения командной строки + + histnum (!) + Номер команды в истории командного интерпретатора + + status (!) + Является ли данная команда вызванной (r), запомненной (s) + или это подсказка completion (c). + + Команды, которые были вызваны и обработаны интерпретатором + имеют состояние "r". К таким командам относится большинство + команд вводимых в интерпретатор. + + Если команда набрана, но вызывать её по какой-либо причине + не хочется (например, команда может быть не полной, вредоносной + или просто бессмысленной в текущих условиях), + её можно сбросить с помощью комбинации клавиш Ctrl-C + (не путайте с прерыванием работающей команды! здесь она даже + не запускается!). + В таком случае она не выполняется, но попадает в журнал + со статусом "s". + + Если команда появилась в журнале благодаря автопроолжению + -- когда было показано несколько вариантов -- + она имеет статус "c". + + euid + Идентификатор пользователя от имени которого будет + выполняться команда. + Может отличаться от реального uid в том случае, + если вызывается с помощью sudo + + + version (!) + Версия lilalo-prompt использовавшаяся при записи + команды. + + 0 - версия использовавшая в labmaker. + Отсутствует информация о текущем каталоге и номере в истории. + Информация о версии также не указана в приглашении. + + + 1 - версия использующаяся в lilalo + + raw_file (*) + Имя файла, в котором находится бинарное представление журнала. + Может содержать ключевое слово HERE, + обозначающее что бинарное представление хранится + непосредственно в базе данных в атрибуте raw_data + + raw_start (*) + Начало блока командной строки в файле бинарного представления + + raw_end (*) + Конец блока командной строки в файле бинарного представления + + raw_cline (*) + Необработанная командная строка в бинарном виде + + raw_data (*) + Бинарное представление команды и результатов её выполнения + + + + +ТАБЛИЦА SESSION + + Информация о сеансах + + + + +=cut + + # Parse new command + $cl{"uid"} = $2; + $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 + $cl{"pid"} = $3; + $cl{"day"} = $4; + $cl{"lab"} = $5; + $cl{"hour"} = $6; + $cl{"min"} = $7; + $cl{"sec"} = $8; + $cl{"fullprompt"} = $9; + $cl{"prompt"} = $10; + $cl{"raw_cline"} = $11; + + $cl{"err"} = 0; + $cl{"output"} = ""; + $cl{"tty"} = $tty; + + $cline_vt->process($cl{"raw_cline"}."\n"); + $cl{"cline"} = $cline_vt->row_plaintext (1); + $cl{"cline"} =~ s/\s*$//; + $cline_vt->reset(); + + my %commands = extract_from_cline("commands", $cl{"cline"}); + $cl{"euid"}=0 if defined $commands{"sudo"}; + my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands; + $cl{"last_command"} = $comms[$#comms] || ""; + + if ( + $Config{"suppress_editors"} =~ /^y/i + && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}}) || + $Config{"suppress_pagers"} =~ /^y/i + && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}}) || + $Config{"suppress_terminal"}=~ /^y/i + && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}}) + ) { + $cl{"suppress_output"} = "1"; + } + else { + $cl{"suppress_output"} = "0"; + + } + $skip_info = 0; + + + print " ",$cl{"last_command"}; + + # Processing previous command line + if ($first_pass) { + $first_pass = 0; + next; + } + + # Error code + $last_cl{"err"}=$err; + $last_cl{"err"}=130 if $err eq "^C"; + + if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) { + bind_diff(\%last_cl); + } + + # Output + if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { + for (my $i=0; $i<$Config{"terminal_height"}; $i++) { + my $line= $vt->row_plaintext($i); + next if !defined ($line) || $line =~ /^\s*$/; + $line =~ s/\s*$//; + $last_cl{"output"} .= $line."\n"; + } + } + else { + $last_cl{"output"}= ""; + } + + $vt->reset(); + + + # Classifying the command line + + + # Save + if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { + # Changing encoding + for (keys %last_cl) { + $last_cl{$_} = $converter->convert($last_cl{$_}) + if ($Config{"encoding"} && + $Config{"encoding"} !~ /^utf-8$/i); + } + push @Command_Lines, \%last_cl; + } + next; + } + $last_output_length+=length($_); + #if (!$cl{"suppress_output"} || $last_output_length < 5000) { + if ($last_output_length < 50000) { + #print "(",length($_),")" if (length($_) > 2000) ; + $vt->process("$_"."\n") + } + else + { + if (!$skip_info) { + print "($cl{last_command})"; + $skip_info = 1; + } + } + } + close(FILE); + + } + if ($Config{"verbose"} =~ /y/) { + print "...finished." ; + print "Lines loaded: $commandlines_processed\n"; + print "Command lines: $commandlines_loaded\n"; + } +} + + + +sub printq +{ + my $TO = shift; + my $text = join "", @_; + $text =~ s/&/&/g; + $text =~ s//>/g; + print $TO $text; +} + + +sub sort_command_lines +{ + print "Sorting command lines...\n" if $Config{"verbose"} =~ /y/; + + # Sort Command_Lines + # Write Command_Lines to Command_Lines_Index + + my @index; + for (my $i=0;$i<=$#Command_Lines;$i++) { + $index[$i]=$i; + } + + @Command_Lines_Index = sort { + $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"} || + $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"} || + $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"} || + $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"} + } @index; + + print "...finished\n" if $Config{"verbose"} =~ /y/; + +} + +sub process_command_lines +{ + for my $i (@Command_Lines_Index) { + + my $cl = \$Command_Lines[$i]; + @{${$cl}->{"new_commands"}} =(); + @{${$cl}->{"new_files"}} =(); + $$cl->{"class"} = ""; + + if ($$cl->{"err"}) { + $$cl->{"class"}="wrong"; + $$cl->{"class"}="interrupted" + if ($$cl->{"err"} eq 130); + } + if (!$$cl->{"euid"}) { + $$cl->{"class"}.="_root"; + } + +#tab# my @tab_words=split /\s+/, $$cl->{"output"}; +#tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; +#tab# $last_word =~ s@.*/@@; +#tab# my $this_is_tab=1; +#tab# +#tab# if ($last_word && @tab_words >2) { +#tab# for my $tab_words (@tab_words) { +#tab# if ($tab_words !~ /^$last_word/) { +#tab# $this_is_tab=0; +#tab# last; +#tab# } +#tab# } +#tab# } +#tab# $$cl->{"class"}="tab" if $this_is_tab; + + + if ( !$$cl->{"err"}) { + # Command does not contain mistakes + + my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); + my %files = extract_from_cline("files", ${$cl}->{"cline"}); + + # Searching for new commands only + for my $command (keys %commands) { + if (!defined $Commands_Stat{$command}) { + push @{$$cl->{new_commands}}, $command; + } + $Commands_Stat{$command}++; + } + + for my $file (keys %files) { + if (!defined $Files_Stat{$file}) { + push @{$$cl->{new_files}}, $file; + } + $Files_Stat{$file}++; + } + } + } + +} + + +=cut +Вывести результат обработки журнала. +=cut + + +sub print_command_lines +{ + my $output_filename=$_[0]; + open(OUT, ">", $output_filename) + or die "Can't open $output_filename for writing\n"; + + + + print OUT "\n"; + + my $cl; + my $in_range=0; + for my $i (@Command_Lines_Index) { + $cl = $Command_Lines[$i]; + + if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { + $in_range=1; + next; + } + if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { + $in_range=0; + next; + } + next if ($Config{"from"} && $Config{"to"} && !$in_range) + || + ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) + || + ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) + || + ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); + + my @new_commands=@{$cl->{"new_commands"}}; + my @new_files=@{$cl->{"new_files"}}; + + my $cl_class="cline"; + my $out_class="output"; + if ($cl->{"class"}) { + $cl_class = $cl->{"class"}."_".$cl_class; + $out_class = $cl->{"class"}."_".$out_class; + } + + # Вырезаем из вывода только нужное количество строк + + my $output=""; + if ($Config{"head_lines"} || $Config{"tail_lines"}) { + # Partialy output + my @lines = split '\n', $cl->{"output"}; + # head + my $mark=1; + for (my $i=0; $i<= $#lines && $i < $Config{"cache_head_lines"}; $i++) { + $output .= $lines[$i]."\n"; + } + # tail + my $start=$#lines-$Config{"cache_tail_lines"}+1; + if ($start < 0) { + $start=0; + $mark=0; + } + if ($start < $Config{"cache_head_lines"}) { + $start=$Config{"cache_head_lines"}; + $mark=0; + } + $output .= $Config{"skip_text"}."\n" if $mark; + for (my $i=$start; $i<= $#lines; $i++) { + $output .= $lines[$i]."\n"; + } + } + else { + # Full output + $output .= $cl->{"output"}; + } + $output .= "^C\n" if ($cl->{"err"} eq "130"); + + + # Совместимость с labmaker + + # Переводим в секунды Эпохи + # В labmaker'е данные хранились в неудобной форме: hour, min, sec, day of year + # Информация о годе отсутствовала + # Её можно внести: + # Декабрь 2004 год; остальные -- 2005 год. + + my $year = 2005; + $year = 2004 if ( $cl->{day} > 330 ); + # timelocal( $sec, $min, $hour, $mday,$mon,$year); + $cl->{time} = timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year); + + + # Начинаем вывод команды + print OUT "\n"; + print OUT "\n"; + print OUT "",$cl->{tty},"\n"; + print OUT "",$out_class,"\n"; + print OUT ""; + printq(\*OUT,,$cl->{"prompt"}); + print OUT ""; + print OUT ""; + printq(\*OUT,$cl->{"cline"}); + print OUT "\n"; + print OUT "",$cl->{"last_command"},"\n"; + if (@new_commands) { + print OUT ""; + printq(\*OUT, join (" ", @new_commands)); + print OUT ""; + } + if (@new_files) { + print OUT ""; + printq(\*OUT, join (" ", @new_files)); + print OUT ""; + } + print OUT ""; + printq(\*OUT,$output); + print OUT "\n"; + if ($cl->{"diff"}) { + print OUT ""; + printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); + print OUT "\n"; + } + print OUT "\n"; + + } + + print OUT "\n"; + close(OUT); +} + + +=cut +sub print_command_lines2 +{ + my $output_filename=$_[0]; + open(OUT, ">", $output_filename) + or die "Can't open $output_filename for writing\n"; + + + print OUT < +OUT + + my $cl; + for my $i (@Command_Lines_Index) { + + + $cl = $Command_Lines[$i]; + + +# Printing out + print OUT < + $cl->{day} + $cl->{hour} + $cl->{min} + $cl->{sec} + $cl->{tty} + $cl->{uid} + $cl->{euid} + $cl->{prompt} + $cl->{cline} + $cl->{err} + +$cl->{output} + +OUT + } + + for my $diff (@Diffs) { + + print OUT < + $diff->{path} + $diff->{uid} + $diff->{day} + $diff->{hour} + $diff->{min} + $diff->{sec} + +$diff->{text} + +OUT + } + + print OUT < +OUT +} +=cut + +main(); + +sub main +{ +$| = 1; + +init_variables(); +init_config(); + +for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) { + load_diff_files($lab_log); +} + +load_command_lines($Config{"input"}, $Config{"input_mask"}); +sort_command_lines; +process_command_lines; +print_command_lines($Config{"cache"}); + +} + +sub init_variables +{ +} diff -r 22c617db6172 -r 6d93c5f1d0e5 l3-frontend --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/l3-frontend Wed Nov 02 19:16:11 2005 +0200 @@ -0,0 +1,493 @@ +#!/usr/bin/perl -w + +use lib '.'; +use l3config; + +our @Command_Lines; + +# vvv Инициализация переменных выполняется процедурой init_variables +our @Day_Name; +our @Month_Name; +our @Of_Month_Name; +our %Search_Machines; +our %Elements_Visibility; +# ^^^ + +sub search_buy; +sub make_comment; +sub load_command_lines_from_xml; +sub print_command_lines; +sub init_variables; +sub main; + +main(); + +sub main +{ + $| = 1; + + init_variables(); + init_config(); + + load_command_lines_from_xml($Config{"cache"}); + print_command_lines($Config{"output"}); +} + + +sub search_by +{ + my $sm = shift; + my $topic = shift; + $topic =~ s/ /+/; + + return ""; +} + +sub make_comment +{ + my $commands = $_[0]; + my $files = $_[1]; + chomp $commands; + chomp $files; + return if (!$commands && !$files); + + my $comment=""; + + # Commands + for my $command (split /\s+/,$commands) { + $command =~ s/'//g; + my $description=""; + eval { $description=`mywi-client '$command'`; } ; + $description = join ("
\n", grep(/\([18]\)/, split(/\n/, $description))); + $description =~ s/.*?-//; + next if $description =~ /^\s*$/; + + my $query=$command." ".$Config{"keywords"}; + $query =~ s/\ /+/g; + my $search= search_by("opennet",$query). + search_by("local",$command). + search_by("google",$query); + + $comment .= "$command". + "$search". + "". + "$description"; + } + + # Files + for my $file (split /\s+/,$files) { + $file =~ s@.*/@@; + $file =~ s/'//g; + next if $file =~ /^\s*$/; + next if $file =~ /^-/; + + my $description=`mywi '$file'`; + $description = join ("
\n", grep(/\(5\)/, split(/\n/, $description))); + next if $description =~ /^\s*$/; + + my $query=$file." ".$Config{"files_keywords"}; + $query =~ s/\ /+/g; + my $search= search_by("opennet",$query). + search_by("local",$file). + search_by("google",$query); + + $comment .= "$file". + "$search". + "". + "$description"; + } + + + return $comment; +} + +=cut +Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта +из XML-документа в переменную @Command_Lines + +Предупреждение! +Процедура не в состоянии обрабатывать XML-документ любой структуры. +В действительности файл cache из которого загружаются данные +просто напоминает XML с виду. +=cut +sub load_command_lines_from_xml +{ + my $datafile = $_[0]; + + open (CLASS, $datafile) + or die "Can't open file of the class ",$datafile,"\n"; + local $/; + $data = ; + close(CLASS); + + for $command ($data =~ m@(.*?)@sg) { + my %cl; + while ($command =~ m@<([^>]*?)>(.*?)@sg) { + $cl{$1} = $2; + } + push @Command_Lines, \%cl; + } +} + +=cut +Процедура print_command_lines выводит HTML-представление +разобранного lab-скрипта. + +Разобранный lab-скрипт должен находиться в массиве @Command_Lines +=cut + +sub print_command_lines +{ + my $output_filename=$_[0]; + + my $course_name = $Config{"course-name"}; + my $course_code = $Config{"course-code"}; + my $course_date = $Config{"course-date"}; + my $course_center = $Config{"course-center"}; + my $course_trainer = $Config{"course-trainer"}; + my $course_student = $Config{"course-student"}; + + + # Результат выполнения процедуры равен + # join("", @Result{header,body,stat,help,about,footer}) + my %Result; + my $toc =""; # Хранит оглавление по дням + + $Result{"body"} = "\n"; + + my $cl; + my $last_tty=""; + my $last_day=""; + my $in_range=0; + + for my $cl (@Command_Lines) { + + if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { + $in_range=1; + next; + } + if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { + $in_range=0; + next; + } + next if ($Config{"from"} && $Config{"to"} && !$in_range) + || + ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) + || + ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) + || + ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); + + #my @new_commands=@{$cl->{"new_commands"}}; + #my @new_files=@{$cl->{"new_files"}}; + + my $cl_class="cline"; + my $out_class="output"; + if ($cl->{"class"}) { + $cl_class = $cl->{"class"}."_".$cl_class; + $out_class = $cl->{"class"}."_".$out_class; + } + + my @new_commands; + my @new_files; + @new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"}; + @new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"}; + + my $output=""; + if ($Config{"head_lines"} || $Config{"tail_lines"}) { + # Partialy output + my @lines = split '\n', $cl->{"output"}; + # head + my $mark=1; + for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) { + $output .= $lines[$i]."\n"; + } + # tail + my $start=$#lines-$Config{"tail_lines"}+1; + if ($start < 0) { + $start=0; + $mark=0; + } + if ($start < $Config{"head_lines"}) { + $start=$Config{"head_lines"}; + $mark=0; + } + $output .= $Config{"skip_text"}."\n" if $mark; + for (my $i=$start; $i<= $#lines; $i++) { + $output .= $lines[$i]."\n"; + } + } + else { + # Full output + $output .= $cl->{"output"}; + } + #$output .= "^C\n" if ($cl->{"err"} eq "130"); + + # + ## + ## Начинается собственно вывод + ## + # + + # + + my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time}); + # Добавляем спереди 0 для удобочитаемости + $min = "0".$min if $min =~ /^.$/; + $hour = "0".$hour if $hour =~ /^.$/; + $sec = "0".$sec if $sec =~ /^.$/; + + $class=$cl->{"out_class"}; + $class =~ s/output$//; + + + $Result{"body"} .= "\n"; + + + # DAY CHANGE + if ( $last_day ne $day) { + #$Result{"body"} .= ""; + $Result{"body"} .= ""; + $toc .= "
  • ".$Day_Name[$wday]."
  • \n"; + $last_day=$day; + } + + # CONSOLE CHANGE + if ( $last_tty ne $cl->{"tty"}) { + $Result{"body"} .= ""; + $last_tty=$cl->{"tty"}; + } + + # TIME + if ($Config{"show_time"} =~ /^y/i) { + $Result{"body"} .= ""; + } else { + $Result{"body"} .= "\n"; + $Result{"body"} .= "\n"; + } + + $Result{"body"} .= "

    День ",$day,"

    ".$Day_Name[$wday]."

    ".$cl->{"tty"}."
    ".
    +				$hour. ":". $min. ":". $sec.
    +				"
    " + } + + # COMMAND + $Result{"body"} .= "\n"; + $Result{"body"} .= "
    \n";
    +		my $cline = $cl->{"cline"};
    +		$cline =~ s/\n//;
    +		$Result{"body"} .= $cl->{"prompt"}.$cl->{"cline"};
    +		$Result{"body"} .= "
    \n"; + + my $last_command = $cl->{"last_command"}; + if (!( + $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) || + $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) || + $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}}) + )) { + + $Result{"body"} .= "
    ";
    +			$Result{"body"} .= $output;
    +			$Result{"body"} .= "
    \n"; + } + + # DIFF + if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) { + $Result{"body"} .= "
    ";
    +			$Result{"body"} .= $cl->{"diff"};
    +			$Result{"body"} .= "
    "; + } + + # COMMENT + if ( $Config{"show_comments"} =~ /^y/i) { + my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files)); + if ($comment) { + $Result{"body"} .= "". + "
    "; + $Result{"body"} .= ""; + $Result{"body"} .= $comment; + $Result{"body"} .= "
    \n"; + $Result{"body"} .= "
    "; + } + else { + $Result{"body"} .= "". + "
    "; + $Result{"body"} .= ""; + $Result{"body"} .= "commands ".join(" ",@new_commands)."
    "; + $Result{"body"} .= "files ".join(" ",@new_files)."
    "; + $Result{"body"} .= "
    \n"; + $Result{"body"} .= "
    "; + } + } + + # Вывод очередной команды окончен + $Result{"body"} .= "
    \n"; + + $Result{"stat"} = "
    "; + $Result{"stat"} .= "

    Статистика

    "; + $Result{"stat"} .= "Статистическая информация о журнале
    "; + $Result{"help"} .= "
    "; + $Result{"help"} .= "

    Справка

    "; + $Result{"help"} .= "$Html_Help
    "; + $Result{"about"} .= "
    "; + $Result{"about"} .= "

    О программе

    "; + $Result{"about"} .= "$Html_About"; + $Result{"footer"} .= "\n"; + $Result{"footer"} .= "\n"; + + # Заголовок генерируется позже всего + # Тогда, когда известно уже, что должно быть написано в + # оглавлении + $Result{"header"} = < + + + + + + +

    Журнал лабораторных работ

    + +

    + Выполнил $course_student
    + Проверил $course_trainer
    + Курс $course_name ($course_code), + $course_date
    + Учебный центр $course_center
    +

    + + + +

    Журнал

    +HEADER + $Result{"header"} .= "
    \n"; + for my $element (keys %Elements_Visibility) + { + my @e = split /\s+/, $element; + my $showhide = join "", map { "ShowHide('$_');" } @e ; + $Result{"header"} .= "". + $Elements_Visibility{$element}. + "
    \n"; + } + + $Result{"header"} .= "
    \n"; + + open(OUT, ">", $output_filename) + or die "Can't open $output_filename for writing\n"; + print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"}; + close(OUT); +} + + + + + + +sub init_variables +{ +$Html_Help = < + LiLaLo (L3) расшифровывается как Live Lab Log.
    + Программа разработана для повышения эффективности обучения
    + Unix/Linux-системам.
    + (c) Игорь Чубин, 2004-2005
    +

    +ABOUT +$Html_About.='$Id$

    '; + +$Html_JavaScript = <0) + subelems[j].style.display = "none"; + else + subelems[j].style.display = ""; + + } + + } + } + + } +JS + +%Search_Machines = ( + "google" => { "query" => "http://www.google.com/search?q=" , + "icon" => "google.ico" }, + "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", + "icon" => "freebsd.ico" }, + "linux" => { "query" => "http://man.he.net/?topic=", + "icon" => "linux.ico"}, + "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=", + "icon" => "opennet.ico"}, + "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", + "icon" => "freebsd.ico" }, + + ); + +%Elements_Visibility = ( + "note" => "замечания", + "diff" => "редактор", + "time" => "время", + "ttychange" => "терминал", + "wrong_output wrong_cline wrong_root_output wrong_root_cline" + => "команды с ошибками", + "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" + => "прерванные команды", + "tab_completion_output tab_completion_cline" + => "продолжение с помощью tab" +); + +@Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /; +@Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /; +@Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /; +} + diff -r 22c617db6172 -r 6d93c5f1d0e5 l3-report --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/l3-report Wed Nov 02 19:16:11 2005 +0200 @@ -0,0 +1,5 @@ +#!/bin/sh + +./l3-agent "$@" +./l3-frontend "$@" + diff -r 22c617db6172 -r 6d93c5f1d0e5 l3config.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/l3config.pm Wed Nov 02 19:16:11 2005 +0200 @@ -0,0 +1,92 @@ +package l3config; + +use Exporter; +use Getopt::Long; + +@ISA = ('Exporter'); +@EXPORT = qw(%Config &init_config); + +our $Config_File = "labmaker.conf"; +our %Config = ( + "skip_empty" => "yes", + "skip_interrupted" => "no", + "skip_wrong" => "no", + "editors" => ["vi", "pico", "ee", "vim"], + "pagers" => ["more", "less", "zmore", "zless", "info", + "man", "mc", "trafshow", "screen", "cfdisk", + "trafshow-bsd", "yes", "lynx", "links", "centericq" + ], + "terminal" => ["mc"], + "suppress_editors" => "yes", + "suppress_pagers" => "yes", + "suppress_terminal" => "yes", + + "terminal_width" => 100, + "terminal_height" => 100, + "verbose" => "yes", + + "head_lines" => 5, + "tail_lines" => 5, + "cache_head_lines" => 5, + "cache_tail_lines" => 5, + "skip_text" => "...", + "show_time" => "yes", + "show_diffs" => "yes", + "show_comments" => "yes", + + "input" => "/root/.labmaker", + "diffs" => "", + "input_mask" => "*.script", + "encoding" => "utf-8", + + "cache" => "/tmp/report.xml", + + "output" => "/tmp/report.html", + #"output" => "report.xml", + "output_mask" => "INDEX", + "output_format" => "html", + + "signature" => "#lm:", + "from" => "", + "to" => "", + "lab" => "", + "keywords" => "linux command", + "files_keywords" => "linux file", + + comment_width => "300", + time_width => "60", + + "course-name" => "", + "course-code" => "", + "course-date" => "", + "course-center" => "", + "course-trainer" => "", + "course-student" => "", + ); + +sub read_config_file +{ + my $config = $_[0]; + my $filename = $_[1]; + open(CONFIG, "$filename") + or return; + while () { + s/#.*//; + next if /^\s*$/; + my ($var, $val) = split /\s*=\s*/, $_, 2; + $var =~ s/\s*//; + $config->{$var} = $val; + } + close(CONFIG); +} + + +sub init_config +{ + my %argv_config; + my %file_config; + read_config_file(\%file_config, $Config_File); + GetOptions(\%argv_config, map "$_=s", keys %Config); + %Config = (%Config, %file_config, %argv_config); +} + diff -r 22c617db6172 -r 6d93c5f1d0e5 lm --- a/lm Tue Nov 01 14:32:30 2005 +0200 +++ b/lm Wed Nov 02 19:16:11 2005 +0200 @@ -21,7 +21,8 @@ "show_host" => "no", # Вспомогательные программы - "l3-report" => "./lm-report", + #"l3-report" => "./lm-report", + "l3-report" => "./l3-report", # Каталоги "path_lilalo" => "/var/lilalo/", diff -r 22c617db6172 -r 6d93c5f1d0e5 lm-report --- a/lm-report Tue Nov 01 14:32:30 2005 +0200 +++ b/lm-report Wed Nov 02 19:16:11 2005 +0200 @@ -1184,19 +1184,3 @@ } -sub init_variables -{ -$Html_Help = < - LiLaLo (L3) расшифровывается как Live Lab Log.
    - Программа разработана для повышения эффективности обучения
    - Unix/Linux-системам.
    - (c) Игорь Чубин, 2004-2005
    -

    -ABOUT -$Html_About.='$Id$

    '; -}